Update uuid to 1.11.0
Test: m
Change-Id: If5108c462e6976fc3c6a9f808ce47886517e1e92
diff --git a/crates/uuid/.android-checksum.json b/crates/uuid/.android-checksum.json
new file mode 100644
index 0000000..d69ce3a
--- /dev/null
+++ b/crates/uuid/.android-checksum.json
@@ -0,0 +1 @@
+{"package":null,"files":{"src/sha1.rs":"173b844e6a3975270ea36d235f5b7e406e9612ba95833d811b3602f5107589f0","Cargo.toml":"a0913ff79b08d0f7230558bdc51ac36fc0d0a88b9760641eafc87e2f1c8a6bb0","src/external/serde_support.rs":"15e735ff56d72b5a8bb846f429cf463361ba1e50d83118df27222ea01a8b0cb3","src/external/arbitrary_support.rs":"f508e7015c8c14c8304a133eb275b60fb946a194c7e431642fe2fa0959a9990c","src/v1.rs":"e52a27d89e344036ffe4408a7b5b5fb326d6fb18ae06704a090c6399b7eb3611","src/v4.rs":"20ae5609a8f891dca1bd8fd99421121dfe452d9ce7f51d635a6d14b4a09889b5","src/external/borsh_support.rs":"9b7f8438d29dad79dc79efd5db412e8a37e29d567ae21c76f4f3f37febabcd07","LICENSE-APACHE":"3c7cd2396b5b772507febd2615d3d5a55b80103845037df77c87ba6e64872f2c","MODULE_LICENSE_APACHE2":"0d6f8afa3940b7f06bebee651376d43bc8b0d5b437337be2696d30377451e93a","src/rng.rs":"158c5d6336c10007f7fa8ede586e7b2fccc9e28186109c8ca060f6e0086c5bb2",".cargo-checksum.json":"e9976ee82200277ca3481dbcb59e7af15a29dd3c1c7f0348576177c7a2b63ee6","src/fmt.rs":"80c5fa5eec2e2afcc4d8bfb045b63b3c796ac72fb0b7646d297cf35aa9eabd38","src/macros.rs":"59262f55948d70b7ca0dcee15c990b6a3ad4fca47705d36ba1e90f88e53e8839","src/parser.rs":"a0d125589f80f3e30bca52897b58c2fce10473eecca1f2b5253ccc024f19162b","TEST_MAPPING":"e93962d7a70966b064fcb2f8429feb4f97dca1cfaeb526f4ec56a44305c6de69","src/external.rs":"d5b9021ea1db870ed29525e3290fae1d6ef89ab615d54548830c6632a1353d4c","src/v3.rs":"7af107a0c0339cae9b50b8b084c5339caf850fe0cd60bc1b129577ee2f140e67","LICENSE":"3c7cd2396b5b772507febd2615d3d5a55b80103845037df77c87ba6e64872f2c","src/external/slog_support.rs":"9f7009bf65d8ca6961ad9b170c33a5eb787820155286a070324ff0d5fd3c09a2","src/builder.rs":"4d97dd185ed044978f142653e35e1c2d7ed15f3640106cc3f24daa448ccb212d","src/v5.rs":"2e435575aa2f5f8971709ffac815407e5f245fe24f64a4666881549a516e30d8","METADATA":"9b6cd4e225188b1ddc0b257ab148843562d8d2803476a54fb6fdf17895567314","LICENSE-MIT":"08bc387e24ed88cdda3af9eb97d401d69c31dc98049e63a9c3dfd763dc45a29d","cargo_embargo.json":"3bf1a98721509650cb9f52cad8d9432fc466b69a9556fc24726caee458caaf0b","Android.bp":"0a66a7df4a2d1d498d3adc3252de213fcf87cab7872a236f3fe8aee126f28663","src/v7.rs":"3a4fdd96ad8e4374b621b81ba332023d4c4e11cfa1b4419134c47e0a12ad86b7","rules.mk":"c6a05070a68ecf74533aa73ac4cef3a8ada1f5ef3de138d4c98a79c48f863482","src/v8.rs":"25b49a2e5e947cffad78ac4b6da187b75469193814eb340eb8a1a9c644021106","src/lib.rs":"41282650bd22c3c0d5ded9a75448eee3fc035412ec6ea5a59ff7e86288556110","src/timestamp.rs":"e456ee7e155815054977415219c694750f33de3a9af3f04ba912a38fe0f1a9b5","README.md":"e0eb9fc0d56401afc10884570fe65bc48121ea92f07357fecb1ff0d0dc15e809","src/v6.rs":"b0f8cf1bec6df06aa7c3d06a465c1a795f02a0bb41328b9509a4662c7b4cf89d","src/md5.rs":"5612c79b17b868220029baf52a87bff31530e607ac2522afdf8677eecb28e780","src/error.rs":"a074f13c8ebaef5c563e961fae295134bf75386bfc44efbea5fefbbef7f44f24"}}
\ No newline at end of file
diff --git a/crates/uuid/.cargo-checksum.json b/crates/uuid/.cargo-checksum.json
index 63a4c55..3c67d65 100644
--- a/crates/uuid/.cargo-checksum.json
+++ b/crates/uuid/.cargo-checksum.json
@@ -1 +1 @@
-{"files":{"Cargo.toml":"97148562e0953fb78f1a174448530dc5fefedf983ca46c21c4bffb06fd997351","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"436bc5a105d8e57dcd8778730f3754f7bf39c14d2f530e4cde4bd2d17a83ec3d","README.md":"5ce601f007d7c2c3b13068a7e9938e08aca71944cb0b4179c627196b06a70a7a","src/builder.rs":"2d6e3bf66fc87e36c44b1a8c108dca70f01f6296ac0d50d0a788b69b5a3dff88","src/error.rs":"621799694111aab9d6e570290f4d1b341fe6be58d2b3425fa7891ada7575cab7","src/external.rs":"a0640bdb98de1c24fcc9851a438a5abe6f7e3acb195885c817a64fac25521b9d","src/external/arbitrary_support.rs":"7e7fbcc4b8af5878b71858a1f5fa31e85d84fc2fd159614f8d450ba1fe06ac28","src/external/borsh_support.rs":"b49d82a59653445ba26db46a1515294b1ab480c0671dbe5499dfd1fb02588b3b","src/external/serde_support.rs":"1263176b9916bf61fe3ab32140c22e2e757ea29ffff6f5459b1b720acbe2ed9d","src/external/slog_support.rs":"53c6251e424bdc9b6ba35defb4c723d4d34d44053badbf98e1c7c3e7c83fbb5e","src/fmt.rs":"3bf88d68d838bef81380a1e669a86eee46f24a8113effbd7b4e92da714ec97c7","src/lib.rs":"96d77474e7d9312cc287c5fad2fa44caf775ef0ce7da825e90c736cbcee615d4","src/macros.rs":"8fa9380a39e9952f6adc9eb858264fc09219a97215f4e54768d9e0ec7f520db7","src/md5.rs":"316d65b760ffa58beb6aa678be24359eb21a744e9e143bc99c11fe1907659245","src/parser.rs":"3f30a776ed1792107fed88c15fb393167283cc487efc647fb6504824a4fc3afb","src/rng.rs":"b9f69610560aa5b5d9b2eaa16fb2239515bd163da126cf5c392e5fb9b0296c3e","src/sha1.rs":"e1a9657e11f1ed1ede33c0655f9c2641059b7c24f17be4ac425c930cc216e019","src/timestamp.rs":"2b50321ee768cd9c3ad5bdf1e4924e8e5deaa8d1a031d24dcfaf2fec44530429","src/v1.rs":"123c4b23d0458e77e6c3ed6235e0c786436e53f53d1c8074cf80d77c2ae40611","src/v3.rs":"b7d605c4d2f56a9e57bfe779ef2488fa612c7cb28568f68252093f48ac2edef4","src/v4.rs":"4f06567460871348df0ff2825e9faad9d950a9337e409cb2b568c86118addb97","src/v5.rs":"64e3feb673c8411954f930188e756803b90b57d13ec36652cd2c669e81654333","src/v6.rs":"2dd938c5bf6d1960f4fb7d179a6163c1cfd76ab24f98aaca85d0f74940184645","src/v7.rs":"e46924c0c9c9b1ac6746180d78cd3531920fbd8379f1629abdcec7914bae36c8","src/v8.rs":"8705405fef52e4706c7cf99f7ed231dde733b7cd08ced79be9e29c949be2280f"},"package":"f00cc9702ca12d3c81455259621e676d0f7251cec66a21e98fe2e9a37db93b2a"}
\ No newline at end of file
+{"files":{"Cargo.toml":"2a7f9c6958b513e041d63dfa988f4f0b48f6634d654f6fd3241ee1fd9e680aa3","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"436bc5a105d8e57dcd8778730f3754f7bf39c14d2f530e4cde4bd2d17a83ec3d","README.md":"4e2fe3e90f75b929f05775e599470f4987e41c0423a7bcd6a7ee526c29354e9c","src/builder.rs":"aa0aaa943858ceea12b06025556c1332f3b57996f5c000545a5ce84c25aaa0ae","src/error.rs":"6a402cde971f7d186be3a184953bc6c89e8b8039fa95cd351e1e158fbe7378cb","src/external.rs":"a0640bdb98de1c24fcc9851a438a5abe6f7e3acb195885c817a64fac25521b9d","src/external/arbitrary_support.rs":"7e7fbcc4b8af5878b71858a1f5fa31e85d84fc2fd159614f8d450ba1fe06ac28","src/external/borsh_support.rs":"b49d82a59653445ba26db46a1515294b1ab480c0671dbe5499dfd1fb02588b3b","src/external/serde_support.rs":"b818f54e784143677146666a6737597bf95523d0de73fedc90b22d9c949b0997","src/external/slog_support.rs":"4fe1cc136b1eb5e27d8b308801bcd72872c129fd20ced1f2415af6760a83751b","src/fmt.rs":"3bf88d68d838bef81380a1e669a86eee46f24a8113effbd7b4e92da714ec97c7","src/lib.rs":"50eb1bd0151dea8bdc61a75f52c9144ddf7df6064dde50d0554af33921d37ebe","src/macros.rs":"dff4a00bcbc37912d38d58edc3edfb8552ba8bb936403b8b33fe7dc3c2041908","src/md5.rs":"316d65b760ffa58beb6aa678be24359eb21a744e9e143bc99c11fe1907659245","src/parser.rs":"838e4a5af613a1d9b9dd6ca4b3c13a42e65fdea35fc02c93c34a416387dbdb7c","src/rng.rs":"d9cdd08ca225f28c103276c985db0540bb8db877a4bcb5348cb4a2648b29883e","src/sha1.rs":"e1a9657e11f1ed1ede33c0655f9c2641059b7c24f17be4ac425c930cc216e019","src/timestamp.rs":"3e282b309e0e7f93518e8ac034e9c3a8e5edda6a6bc9bf3fb6105a8c5a50ff22","src/v1.rs":"9c8254742e58a1d75b8374260108516fc914e2641f83e3a8ada75f05a62a62d1","src/v3.rs":"287860f5376e35d5292959d65948bdb0bbdb4605e3d2e463742c5400075bbe76","src/v4.rs":"c2f2844791cdb2c9e0c90bf7d9d155b96572f1f77aa9586104ddb77d44a5aeea","src/v5.rs":"70799f332c043b3d3ddf4aee791aa448296a5e05122be434945076f9cb29517c","src/v6.rs":"7bd0a52aa316e145ad55b99b0ad46ad3234b0936ab61a4935300f053f2030a56","src/v7.rs":"5e38bf0068606c797f45bb0ad72133a18fc2a879ce595b4b205e75a66266d5e3","src/v8.rs":"15a4c3b81afcca4ec406192f2099fac0ad43d734e12672b02a693ddcc38b6684"},"package":"f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a"}
\ No newline at end of file
diff --git a/crates/uuid/Android.bp b/crates/uuid/Android.bp
index b08a4a2..59d4211 100644
--- a/crates/uuid/Android.bp
+++ b/crates/uuid/Android.bp
@@ -18,7 +18,7 @@
host_supported: true,
crate_name: "uuid",
cargo_env_compat: true,
- cargo_pkg_version: "1.7.0",
+ cargo_pkg_version: "1.11.0",
crate_root: "src/lib.rs",
edition: "2018",
features: [
@@ -47,7 +47,7 @@
host_supported: true,
crate_name: "uuid",
cargo_env_compat: true,
- cargo_pkg_version: "1.7.0",
+ cargo_pkg_version: "1.11.0",
crate_root: "src/lib.rs",
test_suites: ["general-tests"],
auto_gen_config: true,
@@ -81,7 +81,7 @@
name: "libuuid_nostd",
crate_name: "uuid",
cargo_env_compat: true,
- cargo_pkg_version: "1.7.0",
+ cargo_pkg_version: "1.11.0",
crate_root: "src/lib.rs",
edition: "2018",
apex_available: [
diff --git a/crates/uuid/Cargo.toml b/crates/uuid/Cargo.toml
index 320fd4c..4355c0b 100644
--- a/crates/uuid/Cargo.toml
+++ b/crates/uuid/Cargo.toml
@@ -13,19 +13,23 @@
edition = "2018"
rust-version = "1.60.0"
name = "uuid"
-version = "1.7.0"
+version = "1.11.0"
authors = [
"Ashley Mannix<ashleymannix@live.com.au>",
- "Christopher Armstrong",
"Dylan DPC<dylan.dpc@gmail.com>",
"Hunar Roop Kahlon<hunar.roop@gmail.com>",
]
+build = false
include = [
"src",
"README.md",
"LICENSE-APACHE",
"LICENSE-MIT",
]
+autobins = false
+autoexamples = false
+autotests = false
+autobenches = false
description = "A library to generate and parse UUIDs."
homepage = "https://github.com/uuid-rs/uuid"
documentation = "https://docs.rs/uuid"
@@ -80,12 +84,16 @@
"v8",
]
+[lib]
+name = "uuid"
+path = "src/lib.rs"
+
[dependencies.arbitrary]
version = "1.1.3"
optional = true
[dependencies.atomic]
-version = "0.5"
+version = "0.6"
optional = true
default-features = false
@@ -132,15 +140,12 @@
optional = true
[dependencies.uuid-macro-internal]
-version = "1.7.0"
-optional = true
-
-[dependencies.wasm-bindgen]
-version = "0.2"
+version = "1.11.0"
optional = true
[dependencies.zerocopy]
-version = "0.6"
+version = "0.8"
+features = ["derive"]
optional = true
[dev-dependencies.bincode]
@@ -186,16 +191,17 @@
v4 = ["rng"]
v5 = ["sha1"]
v6 = ["atomic"]
-v7 = [
- "atomic",
- "rng",
-]
+v7 = ["rng"]
v8 = []
-[target."cfg(all(target_arch = \"wasm32\", target_vendor = \"unknown\", target_os = \"unknown\"))".dev-dependencies.wasm-bindgen-test]
+[target.'cfg(all(target_arch = "wasm32", target_vendor = "unknown", target_os = "unknown"))'.dependencies.wasm-bindgen]
+version = "0.2"
+optional = true
+
+[target.'cfg(all(target_arch = "wasm32", target_vendor = "unknown", target_os = "unknown"))'.dev-dependencies.wasm-bindgen-test]
version = "0.3"
-[target."cfg(target = \"wasm32-unknown-unknown\")".dev-dependencies.wasm-bindgen]
+[target.'cfg(target = "wasm32-unknown-unknown")'.dev-dependencies.wasm-bindgen]
version = "0.2"
[badges.is-it-maintained-issue-resolution]
@@ -206,3 +212,8 @@
[badges.maintenance]
status = "actively-developed"
+
+[lints.rust.unexpected_cfgs]
+level = "allow"
+priority = 0
+check-cfg = ["cfg(uuid_unstable)"]
diff --git a/crates/uuid/METADATA b/crates/uuid/METADATA
index 51da58e..6bea850 100644
--- a/crates/uuid/METADATA
+++ b/crates/uuid/METADATA
@@ -1,20 +1,17 @@
-# This project was upgraded with external_updater.
-# Usage: tools/external_updater/updater.sh update external/rust/crates/uuid
-# For more info, check https://cs.android.com/android/platform/superproject/+/main:tools/external_updater/README.md
-
name: "uuid"
description: "A library to generate and parse UUIDs."
third_party {
+ version: "1.11.0"
license_type: NOTICE
last_upgrade_date {
year: 2024
- month: 2
- day: 7
+ month: 12
+ day: 20
}
homepage: "https://crates.io/crates/uuid"
identifier {
type: "Archive"
- value: "https://static.crates.io/crates/uuid/uuid-1.7.0.crate"
- version: "1.7.0"
+ value: "https://static.crates.io/crates/uuid/uuid-1.11.0.crate"
+ version: "1.11.0"
}
}
diff --git a/crates/uuid/README.md b/crates/uuid/README.md
index a1aba43..feff9cf 100644
--- a/crates/uuid/README.md
+++ b/crates/uuid/README.md
@@ -28,7 +28,7 @@
```toml
[dependencies.uuid]
-version = "1.7.0"
+version = "1.11.0"
features = [
"v4", # Lets you generate random UUIDs
"fast-rng", # Use a faster (but still sufficiently random) RNG
@@ -65,13 +65,13 @@
If you'd like to parse UUIDs _really_ fast, check out the [`uuid-simd`](https://github.com/nugine/uuid-simd)
library.
-For more details on using `uuid`, [see the library documentation](https://docs.rs/uuid/1.7.0/uuid).
+For more details on using `uuid`, [see the library documentation](https://docs.rs/uuid/1.11.0/uuid).
## References
-* [`uuid` library docs](https://docs.rs/uuid/1.7.0/uuid).
+* [`uuid` library docs](https://docs.rs/uuid/1.11.0/uuid).
* [Wikipedia: Universally Unique Identifier](http://en.wikipedia.org/wiki/Universally_unique_identifier).
-* [RFC4122: A Universally Unique IDentifier (UUID) URN Namespace](http://tools.ietf.org/html/rfc4122).
+* [RFC 9562: Universally Unique IDentifiers (UUID)](https://www.ietf.org/rfc/rfc9562.html).
---
# License
diff --git a/crates/uuid/src/builder.rs b/crates/uuid/src/builder.rs
index 2dd68a2..e59cb43 100644
--- a/crates/uuid/src/builder.rs
+++ b/crates/uuid/src/builder.rs
@@ -54,7 +54,7 @@
///
/// # References
///
- /// * [Nil UUID in RFC4122](https://tools.ietf.org/html/rfc4122.html#section-4.1.7)
+ /// * [Nil UUID in RFC 9562](https://www.ietf.org/rfc/rfc9562.html#section-5.9)
///
/// # Examples
///
@@ -80,7 +80,7 @@
///
/// # References
///
- /// * [Max UUID in Draft RFC: New UUID Formats, Version 4](https://datatracker.ietf.org/doc/html/draft-peabody-dispatch-new-uuid-format-04#section-5.4)
+ /// * [Max UUID in RFC 9562](https://www.ietf.org/rfc/rfc9562.html#section-5.10)
///
/// # Examples
///
@@ -549,9 +549,11 @@
Builder(Uuid::from_bytes_le(b))
}
- /// Creates a `Builder` for a version 1 UUID using the supplied timestamp and node ID.
- pub const fn from_rfc4122_timestamp(ticks: u64, counter: u16, node_id: &[u8; 6]) -> Self {
- Builder(timestamp::encode_rfc4122_timestamp(ticks, counter, node_id))
+ /// Creates a `Builder` for a version 1 UUID using the supplied timestamp, counter, and node ID.
+ pub const fn from_gregorian_timestamp(ticks: u64, counter: u16, node_id: &[u8; 6]) -> Self {
+ Builder(timestamp::encode_gregorian_timestamp(
+ ticks, counter, node_id,
+ ))
}
/// Creates a `Builder` for a version 3 UUID using the supplied MD5 hashed bytes.
@@ -596,22 +598,24 @@
.with_version(Version::Sha1)
}
- /// Creates a `Builder` for a version 6 UUID using the supplied timestamp and node ID.
+ /// Creates a `Builder` for a version 6 UUID using the supplied timestamp, counter, and node ID.
///
/// This method will encode the ticks, counter, and node ID in a sortable UUID.
- pub const fn from_sorted_rfc4122_timestamp(
+ pub const fn from_sorted_gregorian_timestamp(
ticks: u64,
counter: u16,
node_id: &[u8; 6],
) -> Self {
- Builder(timestamp::encode_sorted_rfc4122_timestamp(
+ Builder(timestamp::encode_sorted_gregorian_timestamp(
ticks, counter, node_id,
))
}
- /// Creates a `Builder` for a version 7 UUID using the supplied Unix timestamp and random bytes.
+ /// Creates a `Builder` for a version 7 UUID using the supplied Unix timestamp and counter bytes.
///
- /// This method assumes the bytes are already sufficiently random.
+ /// This method will set the variant field within the counter bytes without attempting to shift
+ /// the data around it. Callers using the counter as a monotonic value should be careful not to
+ /// store significant data in the 2 least significant bits of the 3rd byte.
///
/// # Examples
///
@@ -636,10 +640,10 @@
/// # Ok(())
/// # }
/// ```
- pub const fn from_unix_timestamp_millis(millis: u64, random_bytes: &[u8; 10]) -> Self {
+ pub const fn from_unix_timestamp_millis(millis: u64, counter_random_bytes: &[u8; 10]) -> Self {
Builder(timestamp::encode_unix_timestamp_millis(
millis,
- random_bytes,
+ counter_random_bytes,
))
}
@@ -901,3 +905,26 @@
self.0
}
}
+
+#[doc(hidden)]
+impl Builder {
+ #[deprecated(
+ since = "1.11.0",
+ note = "use `Builder::from_gregorian_timestamp(ticks, counter, node_id)`"
+ )]
+ pub const fn from_rfc4122_timestamp(ticks: u64, counter: u16, node_id: &[u8; 6]) -> Self {
+ Builder::from_gregorian_timestamp(ticks, counter, node_id)
+ }
+
+ #[deprecated(
+ since = "1.11.0",
+ note = "use `Builder::from_sorted_gregorian_timestamp(ticks, counter, node_id)`"
+ )]
+ pub const fn from_sorted_rfc4122_timestamp(
+ ticks: u64,
+ counter: u16,
+ node_id: &[u8; 6],
+ ) -> Self {
+ Builder::from_sorted_gregorian_timestamp(ticks, counter, node_id)
+ }
+}
diff --git a/crates/uuid/src/error.rs b/crates/uuid/src/error.rs
index e9aecbf..30e0175 100644
--- a/crates/uuid/src/error.rs
+++ b/crates/uuid/src/error.rs
@@ -82,7 +82,7 @@
group_bounds[hyphen_count] = index;
}
hyphen_count += 1;
- } else if !matches!(byte, b'0'..=b'9' | b'a'..=b'f' | b'A'..=b'F') {
+ } else if !byte.is_ascii_hexdigit() {
// Non-hex char
return Error(ErrorKind::Char {
character: byte as char,
diff --git a/crates/uuid/src/external/serde_support.rs b/crates/uuid/src/external/serde_support.rs
index 5228daf..f389271 100644
--- a/crates/uuid/src/external/serde_support.rs
+++ b/crates/uuid/src/external/serde_support.rs
@@ -84,22 +84,22 @@
{
#[rustfmt::skip]
let bytes = [
- match seq.next_element()? { Some(e) => e, None => return Err(A::Error::invalid_length(16, &self)) },
- match seq.next_element()? { Some(e) => e, None => return Err(A::Error::invalid_length(16, &self)) },
- match seq.next_element()? { Some(e) => e, None => return Err(A::Error::invalid_length(16, &self)) },
- match seq.next_element()? { Some(e) => e, None => return Err(A::Error::invalid_length(16, &self)) },
- match seq.next_element()? { Some(e) => e, None => return Err(A::Error::invalid_length(16, &self)) },
- match seq.next_element()? { Some(e) => e, None => return Err(A::Error::invalid_length(16, &self)) },
- match seq.next_element()? { Some(e) => e, None => return Err(A::Error::invalid_length(16, &self)) },
- match seq.next_element()? { Some(e) => e, None => return Err(A::Error::invalid_length(16, &self)) },
- match seq.next_element()? { Some(e) => e, None => return Err(A::Error::invalid_length(16, &self)) },
- match seq.next_element()? { Some(e) => e, None => return Err(A::Error::invalid_length(16, &self)) },
- match seq.next_element()? { Some(e) => e, None => return Err(A::Error::invalid_length(16, &self)) },
- match seq.next_element()? { Some(e) => e, None => return Err(A::Error::invalid_length(16, &self)) },
- match seq.next_element()? { Some(e) => e, None => return Err(A::Error::invalid_length(16, &self)) },
- match seq.next_element()? { Some(e) => e, None => return Err(A::Error::invalid_length(16, &self)) },
- match seq.next_element()? { Some(e) => e, None => return Err(A::Error::invalid_length(16, &self)) },
- match seq.next_element()? { Some(e) => e, None => return Err(A::Error::invalid_length(16, &self)) },
+ match seq.next_element()? { Some(e) => e, None => return Err(A::Error::invalid_length(0, &self)) },
+ match seq.next_element()? { Some(e) => e, None => return Err(A::Error::invalid_length(1, &self)) },
+ match seq.next_element()? { Some(e) => e, None => return Err(A::Error::invalid_length(2, &self)) },
+ match seq.next_element()? { Some(e) => e, None => return Err(A::Error::invalid_length(3, &self)) },
+ match seq.next_element()? { Some(e) => e, None => return Err(A::Error::invalid_length(4, &self)) },
+ match seq.next_element()? { Some(e) => e, None => return Err(A::Error::invalid_length(5, &self)) },
+ match seq.next_element()? { Some(e) => e, None => return Err(A::Error::invalid_length(6, &self)) },
+ match seq.next_element()? { Some(e) => e, None => return Err(A::Error::invalid_length(7, &self)) },
+ match seq.next_element()? { Some(e) => e, None => return Err(A::Error::invalid_length(8, &self)) },
+ match seq.next_element()? { Some(e) => e, None => return Err(A::Error::invalid_length(9, &self)) },
+ match seq.next_element()? { Some(e) => e, None => return Err(A::Error::invalid_length(10, &self)) },
+ match seq.next_element()? { Some(e) => e, None => return Err(A::Error::invalid_length(11, &self)) },
+ match seq.next_element()? { Some(e) => e, None => return Err(A::Error::invalid_length(12, &self)) },
+ match seq.next_element()? { Some(e) => e, None => return Err(A::Error::invalid_length(13, &self)) },
+ match seq.next_element()? { Some(e) => e, None => return Err(A::Error::invalid_length(14, &self)) },
+ match seq.next_element()? { Some(e) => e, None => return Err(A::Error::invalid_length(15, &self)) },
];
Ok(Uuid::from_bytes(bytes))
@@ -127,6 +127,33 @@
}
}
+enum ExpectedFormat {
+ Simple,
+ Braced,
+ Urn,
+}
+
+impl std::fmt::Display for ExpectedFormat {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ let s = match self {
+ ExpectedFormat::Simple => "a simple Uuid string like 67e5504410b1426f9247bb680e5fe0c8",
+ ExpectedFormat::Braced => {
+ "a braced Uuid string like {67e55044-10b1-426f-9247-bb680e5fe0c8}"
+ }
+ ExpectedFormat::Urn => {
+ "a URN Uuid string like urn:uuid:67e55044-10b1-426f-9247-bb680e5fe0c8"
+ }
+ };
+ f.write_str(s)
+ }
+}
+
+impl de::Expected for ExpectedFormat {
+ fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+ <ExpectedFormat as std::fmt::Display>::fmt(self, formatter)
+ }
+}
+
pub mod compact {
//! Serialize a [`Uuid`] as a `[u8; 16]`.
//!
@@ -157,7 +184,7 @@
#[cfg(test)]
mod tests {
use serde_derive::*;
- use serde_test::{self, Configure};
+ use serde_test::Configure;
#[test]
fn test_serialize_compact() {
@@ -207,6 +234,401 @@
}
}
+/// Serialize from a [`Uuid`] as a `uuid::fmt::Simple`
+///
+/// [`Uuid`]: ../../struct.Uuid.html
+///
+/// ## Example
+///
+/// ```rust
+/// #[derive(serde_derive::Serialize, serde_derive::Deserialize)]
+/// struct StructA {
+/// // This will change both serailization and deserialization
+/// #[serde(with = "uuid::serde::simple")]
+/// id: uuid::Uuid,
+/// }
+///
+/// #[derive(serde_derive::Serialize, serde_derive::Deserialize)]
+/// struct StructB {
+/// // This will be serialized as uuid::fmt::Simple and deserialize from all valid formats
+/// #[serde(serialize_with = "uuid::serde::simple::serialize")]
+/// id: uuid::Uuid,
+/// }
+/// ```
+pub mod simple {
+ use serde::{de, Deserialize};
+
+ use crate::{parser::parse_simple, Uuid};
+
+ use super::ExpectedFormat;
+
+ /// Serialize from a [`Uuid`] as a `uuid::fmt::Simple`
+ ///
+ /// [`Uuid`]: ../../struct.Uuid.html
+ ///
+ /// # Example
+ ///
+ /// ```rust
+ /// #[derive(serde_derive::Serialize)]
+ /// struct Struct {
+ /// // This will be serialize as uuid::fmt::Simple
+ /// #[serde(serialize_with = "uuid::serde::simple::serialize")]
+ /// id: uuid::Uuid,
+ /// }
+ ///
+ /// ```
+ pub fn serialize<S>(u: &crate::Uuid, serializer: S) -> Result<S::Ok, S::Error>
+ where
+ S: serde::Serializer,
+ {
+ serde::Serialize::serialize(u.as_simple(), serializer)
+ }
+
+ /// Deserialize a simple Uuid string as a [`Uuid`]
+ ///
+ /// [`Uuid`]: ../../struct.Uuid.html
+ pub fn deserialize<'de, D>(deserializer: D) -> Result<Uuid, D::Error>
+ where
+ D: serde::Deserializer<'de>,
+ {
+ let s = <&str as Deserialize>::deserialize(deserializer)?;
+ let bytes = parse_simple(s.as_bytes()).map_err(|_| {
+ de::Error::invalid_value(de::Unexpected::Str(s), &ExpectedFormat::Simple)
+ })?;
+ Ok(Uuid::from_bytes(bytes))
+ }
+
+ #[cfg(test)]
+ mod tests {
+ use serde::de::{self, Error};
+ use serde_test::{Readable, Token};
+
+ use crate::{external::serde_support::ExpectedFormat, Uuid};
+
+ const HYPHENATED_UUID_STR: &'static str = "f9168c5e-ceb2-4faa-b6bf-329bf39fa1e4";
+ const SIMPLE_UUID_STR: &'static str = "f9168c5eceb24faab6bf329bf39fa1e4";
+
+ #[test]
+ fn test_serialize_as_simple() {
+ #[derive(serde_derive::Serialize)]
+ struct Struct(#[serde(with = "super")] crate::Uuid);
+
+ let u = Struct(Uuid::parse_str(HYPHENATED_UUID_STR).unwrap());
+ serde_test::assert_ser_tokens(
+ &u,
+ &[
+ Token::NewtypeStruct { name: "Struct" },
+ Token::Str(SIMPLE_UUID_STR),
+ ],
+ );
+ }
+
+ #[test]
+ fn test_de_from_simple() {
+ #[derive(PartialEq, Debug, serde_derive::Deserialize)]
+ struct Struct(#[serde(with = "super")] crate::Uuid);
+ let s = Struct(HYPHENATED_UUID_STR.parse().unwrap());
+ serde_test::assert_de_tokens::<Struct>(
+ &s,
+ &[
+ Token::TupleStruct {
+ name: "Struct",
+ len: 1,
+ },
+ Token::BorrowedStr(SIMPLE_UUID_STR),
+ Token::TupleStructEnd,
+ ],
+ );
+ }
+
+ #[test]
+ fn test_de_reject_hypenated() {
+ #[derive(PartialEq, Debug, serde_derive::Deserialize)]
+ struct Struct(#[serde(with = "super")] crate::Uuid);
+ serde_test::assert_de_tokens_error::<Readable<Struct>>(
+ &[
+ Token::TupleStruct {
+ name: "Struct",
+ len: 1,
+ },
+ Token::BorrowedStr(HYPHENATED_UUID_STR),
+ Token::TupleStructEnd,
+ ],
+ &format!(
+ "{}",
+ de::value::Error::invalid_value(
+ de::Unexpected::Str(HYPHENATED_UUID_STR),
+ &ExpectedFormat::Simple,
+ )
+ ),
+ );
+ }
+ }
+}
+
+/// Serialize from a [`Uuid`] as a `uuid::fmt::Braced`
+///
+/// [`Uuid`]: ../../struct.Uuid.html
+///
+/// ## Example
+///
+/// ```rust
+/// #[derive(serde_derive::Serialize, serde_derive::Deserialize)]
+/// struct StructA {
+/// // This will change both serailization and deserialization
+/// #[serde(with = "uuid::serde::braced")]
+/// id: uuid::Uuid,
+/// }
+///
+/// #[derive(serde_derive::Serialize, serde_derive::Deserialize)]
+/// struct StructB {
+/// // This will be serialized as uuid::fmt::Urn and deserialize from all valid formats
+/// #[serde(serialize_with = "uuid::serde::braced::serialize")]
+/// id: uuid::Uuid,
+/// }
+/// ```
+pub mod braced {
+ use serde::{de, Deserialize};
+
+ use crate::parser::parse_braced;
+
+ use super::ExpectedFormat;
+
+ /// Serialize from a [`Uuid`] as a `uuid::fmt::Braced`
+ ///
+ /// [`Uuid`]: ../../struct.Uuid.html
+ ///
+ /// # Example
+ ///
+ /// ```rust
+ /// #[derive(serde_derive::Serialize)]
+ /// struct Struct {
+ /// // This will be serialize as uuid::fmt::Braced
+ /// #[serde(serialize_with = "uuid::serde::braced::serialize")]
+ /// id: uuid::Uuid,
+ /// }
+ ///
+ /// ```
+ pub fn serialize<S>(u: &crate::Uuid, serializer: S) -> Result<S::Ok, S::Error>
+ where
+ S: serde::Serializer,
+ {
+ serde::Serialize::serialize(u.as_braced(), serializer)
+ }
+
+ /// Deserialize a braced Uuid string as a [`Uuid`]
+ ///
+ /// [`Uuid`]: ../../struct.Uuid.html
+ pub fn deserialize<'de, D>(deserializer: D) -> Result<crate::Uuid, D::Error>
+ where
+ D: serde::Deserializer<'de>,
+ {
+ let s = <&str as Deserialize>::deserialize(deserializer)?;
+ let bytes = parse_braced(s.as_bytes()).map_err(|_| {
+ de::Error::invalid_value(de::Unexpected::Str(s), &ExpectedFormat::Braced)
+ })?;
+ Ok(crate::Uuid::from_bytes(bytes))
+ }
+
+ #[cfg(test)]
+ mod tests {
+ use serde::de::{self, Error};
+ use serde_test::{Readable, Token};
+
+ use crate::{external::serde_support::ExpectedFormat, Uuid};
+
+ const HYPHENATED_UUID_STR: &'static str = "f9168c5e-ceb2-4faa-b6bf-329bf39fa1e4";
+ const BRACED_UUID_STR: &'static str = "{f9168c5e-ceb2-4faa-b6bf-329bf39fa1e4}";
+
+ #[test]
+ fn test_serialize_as_braced() {
+ #[derive(serde_derive::Serialize)]
+ struct Struct(#[serde(with = "super")] crate::Uuid);
+
+ let u = Struct(Uuid::parse_str(HYPHENATED_UUID_STR).unwrap());
+ serde_test::assert_ser_tokens(
+ &u,
+ &[
+ Token::NewtypeStruct { name: "Struct" },
+ Token::Str(BRACED_UUID_STR),
+ ],
+ );
+ }
+
+ #[test]
+ fn test_de_from_braced() {
+ #[derive(PartialEq, Debug, serde_derive::Deserialize)]
+ struct Struct(#[serde(with = "super")] crate::Uuid);
+ let s = Struct(HYPHENATED_UUID_STR.parse().unwrap());
+ serde_test::assert_de_tokens::<Struct>(
+ &s,
+ &[
+ Token::TupleStruct {
+ name: "Struct",
+ len: 1,
+ },
+ Token::BorrowedStr(BRACED_UUID_STR),
+ Token::TupleStructEnd,
+ ],
+ );
+ }
+
+ #[test]
+ fn test_de_reject_hypenated() {
+ #[derive(PartialEq, Debug, serde_derive::Deserialize)]
+ struct Struct(#[serde(with = "super")] crate::Uuid);
+ serde_test::assert_de_tokens_error::<Readable<Struct>>(
+ &[
+ Token::TupleStruct {
+ name: "Struct",
+ len: 1,
+ },
+ Token::BorrowedStr(HYPHENATED_UUID_STR),
+ Token::TupleStructEnd,
+ ],
+ &format!(
+ "{}",
+ de::value::Error::invalid_value(
+ de::Unexpected::Str(HYPHENATED_UUID_STR),
+ &ExpectedFormat::Braced,
+ )
+ ),
+ );
+ }
+ }
+}
+
+/// Serialize from a [`Uuid`] as a `uuid::fmt::Urn`
+///
+/// [`Uuid`]: ../../struct.Uuid.html
+///
+/// ## Example
+///
+/// ```rust
+/// #[derive(serde_derive::Serialize, serde_derive::Deserialize)]
+/// struct StructA {
+/// // This will change both serailization and deserialization
+/// #[serde(with = "uuid::serde::urn")]
+/// id: uuid::Uuid,
+/// }
+///
+/// #[derive(serde_derive::Serialize, serde_derive::Deserialize)]
+/// struct StructB {
+/// // This will be serialized as uuid::fmt::Urn and deserialize from all valid formats
+/// #[serde(serialize_with = "uuid::serde::urn::serialize")]
+/// id: uuid::Uuid,
+/// }
+/// ```
+pub mod urn {
+ use serde::{de, Deserialize};
+
+ use crate::parser::parse_urn;
+
+ use super::ExpectedFormat;
+
+ /// Serialize from a [`Uuid`] as a `uuid::fmt::Urn`
+ ///
+ /// [`Uuid`]: ../../struct.Uuid.html
+ ///
+ /// # Example
+ ///
+ /// ```rust
+ /// #[derive(serde_derive::Serialize)]
+ /// struct Struct {
+ /// // This will be serialize as uuid::fmt::Urn
+ /// #[serde(serialize_with = "uuid::serde::urn::serialize")]
+ /// id: uuid::Uuid,
+ /// }
+ ///
+ /// ```
+ pub fn serialize<S>(u: &crate::Uuid, serializer: S) -> Result<S::Ok, S::Error>
+ where
+ S: serde::Serializer,
+ {
+ serde::Serialize::serialize(u.as_urn(), serializer)
+ }
+
+ /// Deserialize a urn Uuid string as a [`Uuid`]
+ ///
+ /// [`Uuid`]: ../../struct.Uuid.html
+ pub fn deserialize<'de, D>(deserializer: D) -> Result<crate::Uuid, D::Error>
+ where
+ D: serde::Deserializer<'de>,
+ {
+ let s = <&str as Deserialize>::deserialize(deserializer)?;
+ let bytes = parse_urn(s.as_bytes())
+ .map_err(|_| de::Error::invalid_value(de::Unexpected::Str(s), &ExpectedFormat::Urn))?;
+ Ok(crate::Uuid::from_bytes(bytes))
+ }
+
+ #[cfg(test)]
+ mod tests {
+ use serde::de::{self, Error};
+ use serde_test::{Readable, Token};
+
+ use crate::{external::serde_support::ExpectedFormat, Uuid};
+
+ const HYPHENATED_UUID_STR: &'static str = "f9168c5e-ceb2-4faa-b6bf-329bf39fa1e4";
+ const URN_UUID_STR: &'static str = "urn:uuid:f9168c5e-ceb2-4faa-b6bf-329bf39fa1e4";
+
+ #[test]
+ fn test_serialize_as_urn() {
+ #[derive(serde_derive::Serialize)]
+ struct Struct(#[serde(with = "super")] crate::Uuid);
+
+ let u = Struct(Uuid::parse_str(HYPHENATED_UUID_STR).unwrap());
+ serde_test::assert_ser_tokens(
+ &u,
+ &[
+ Token::NewtypeStruct { name: "Struct" },
+ Token::Str(URN_UUID_STR),
+ ],
+ );
+ }
+
+ #[test]
+ fn test_de_from_urn() {
+ #[derive(PartialEq, Debug, serde_derive::Deserialize)]
+ struct Struct(#[serde(with = "super")] crate::Uuid);
+ let s = Struct(HYPHENATED_UUID_STR.parse().unwrap());
+ serde_test::assert_de_tokens::<Struct>(
+ &s,
+ &[
+ Token::TupleStruct {
+ name: "Struct",
+ len: 1,
+ },
+ Token::BorrowedStr(URN_UUID_STR),
+ Token::TupleStructEnd,
+ ],
+ );
+ }
+
+ #[test]
+ fn test_de_reject_hypenated() {
+ #[derive(PartialEq, Debug, serde_derive::Deserialize)]
+ struct Struct(#[serde(with = "super")] crate::Uuid);
+ serde_test::assert_de_tokens_error::<Readable<Struct>>(
+ &[
+ Token::TupleStruct {
+ name: "Struct",
+ len: 1,
+ },
+ Token::BorrowedStr(HYPHENATED_UUID_STR),
+ Token::TupleStructEnd,
+ ],
+ &format!(
+ "{}",
+ de::value::Error::invalid_value(
+ de::Unexpected::Str(HYPHENATED_UUID_STR),
+ &ExpectedFormat::Urn,
+ )
+ ),
+ );
+ }
+ }
+}
+
#[cfg(test)]
mod serde_tests {
use super::*;
diff --git a/crates/uuid/src/external/slog_support.rs b/crates/uuid/src/external/slog_support.rs
index 2d6e817..cb06255 100644
--- a/crates/uuid/src/external/slog_support.rs
+++ b/crates/uuid/src/external/slog_support.rs
@@ -26,7 +26,7 @@
mod tests {
use crate::tests::new;
- use slog::{self, crit, Drain};
+ use slog::{crit, Drain};
#[test]
fn test_slog_kv() {
diff --git a/crates/uuid/src/lib.rs b/crates/uuid/src/lib.rs
index 4c3c9b9..1316d62 100644
--- a/crates/uuid/src/lib.rs
+++ b/crates/uuid/src/lib.rs
@@ -30,8 +30,7 @@
//! practical purposes, it can be assumed that an unintentional collision would
//! be extremely unlikely.
//!
-//! UUIDs have a number of standardized encodings that are specified in [RFC4122](http://tools.ietf.org/html/rfc4122),
-//! with recent additions [in draft](https://datatracker.ietf.org/doc/html/draft-peabody-dispatch-new-uuid-format-04).
+//! UUIDs have a number of standardized encodings that are specified in [RFC 9562](https://www.ietf.org/rfc/rfc9562.html).
//!
//! # Getting started
//!
@@ -39,7 +38,7 @@
//!
//! ```toml
//! [dependencies.uuid]
-//! version = "1.7.0"
+//! version = "1.11.0"
//! features = [
//! "v4", # Lets you generate random UUIDs
//! "fast-rng", # Use a faster (but still sufficiently random) RNG
@@ -84,8 +83,6 @@
//! * `v7` - Version 7 UUIDs using a Unix timestamp.
//! * `v8` - Version 8 UUIDs using user-defined data.
//!
-//! Versions that are in draft are also supported. See the _unstable features_ section for details.
-//!
//! This library also includes a [`Builder`] type that can be used to help construct UUIDs of any
//! version without any additional dependencies or features. It's a lower-level API than [`Uuid`]
//! that can be used when you need control over implicit requirements on things like a source
@@ -141,7 +138,7 @@
//!
//! ```toml
//! [dependencies.uuid]
-//! version = "1.7.0"
+//! version = "1.11.0"
//! features = [
//! "v4",
//! "v7",
@@ -156,7 +153,7 @@
//!
//! ```toml
//! [dependencies.uuid]
-//! version = "1.7.0"
+//! version = "1.11.0"
//! default-features = false
//! ```
//!
@@ -202,8 +199,7 @@
//! # References
//!
//! * [Wikipedia: Universally Unique Identifier](http://en.wikipedia.org/wiki/Universally_unique_identifier)
-//! * [RFC4122: A Universally Unique Identifier (UUID) URN Namespace](http://tools.ietf.org/html/rfc4122)
-//! * [Draft RFC: New UUID Formats, Version 4](https://datatracker.ietf.org/doc/html/draft-peabody-dispatch-new-uuid-format-04)
+//! * [RFC 9562: Universally Unique IDentifiers (UUID)](https://www.ietf.org/rfc/rfc9562.html).
//!
//! [`wasm-bindgen`]: https://crates.io/crates/wasm-bindgen
//! [`cargo-web`]: https://crates.io/crates/cargo-web
@@ -211,10 +207,11 @@
#![no_std]
#![deny(missing_debug_implementations, missing_docs)]
+#![allow(clippy::mixed_attributes_style)]
#![doc(
html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
html_favicon_url = "https://www.rust-lang.org/favicon.ico",
- html_root_url = "https://docs.rs/uuid/1.7.0"
+ html_root_url = "https://docs.rs/uuid/1.11.0"
)]
#[cfg(any(feature = "std", test))]
@@ -226,7 +223,7 @@
extern crate core as std;
#[cfg(all(uuid_unstable, feature = "zerocopy"))]
-use zerocopy::{AsBytes, FromBytes, Unaligned};
+use zerocopy::{IntoBytes, FromBytes, Immutable, KnownLayout, Unaligned};
mod builder;
mod error;
@@ -240,6 +237,9 @@
#[cfg(any(feature = "v1", feature = "v6"))]
pub use timestamp::context::Context;
+#[cfg(feature = "v7")]
+pub use timestamp::context::ContextV7;
+
#[cfg(feature = "v1")]
#[doc(hidden)]
// Soft-deprecated (Rust doesn't support deprecating re-exports)
@@ -294,7 +294,7 @@
///
/// # References
///
-/// * [Version in RFC4122](https://datatracker.ietf.org/doc/html/rfc4122#section-4.1.3)
+/// * [Version Field in RFC 9562](https://www.ietf.org/rfc/rfc9562.html#section-4.2)
#[derive(Clone, Copy, Debug, PartialEq)]
#[non_exhaustive]
#[repr(u8)]
@@ -325,14 +325,15 @@
///
/// # References
///
-/// * [Variant in RFC4122](http://tools.ietf.org/html/rfc4122#section-4.1.1)
+/// * [Variant Field in RFC 9562](https://www.ietf.org/rfc/rfc9562.html#section-4.1)
#[derive(Clone, Copy, Debug, PartialEq)]
#[non_exhaustive]
#[repr(u8)]
pub enum Variant {
/// Reserved by the NCS for backward compatibility.
NCS = 0u8,
- /// As described in the RFC4122 Specification (default).
+ /// As described in the RFC 9562 Specification (default).
+ /// (for backward compatibility it is not yet renamed)
RFC4122,
/// Reserved by Microsoft for backward compatibility.
Microsoft,
@@ -437,7 +438,7 @@
#[derive(Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[cfg_attr(
all(uuid_unstable, feature = "zerocopy"),
- derive(AsBytes, FromBytes, Unaligned)
+ derive(IntoBytes, FromBytes, KnownLayout, Immutable, Unaligned)
)]
#[cfg_attr(
feature = "borsh",
@@ -497,7 +498,7 @@
///
/// # References
///
- /// * [Variant in RFC4122](http://tools.ietf.org/html/rfc4122#section-4.1.1)
+ /// * [Variant Field in RFC 9562](https://www.ietf.org/rfc/rfc9562.html#section-4.1)
pub const fn get_variant(&self) -> Variant {
match self.as_bytes()[8] {
x if x & 0x80 == 0x00 => Variant::NCS,
@@ -532,7 +533,7 @@
///
/// # References
///
- /// * [Version in RFC4122](https://datatracker.ietf.org/doc/html/rfc4122#section-4.1.3)
+ /// * [Version Field in RFC 9562](https://www.ietf.org/rfc/rfc9562.html#section-4.2)
pub const fn get_version_num(&self) -> usize {
(self.as_bytes()[6] >> 4) as usize
}
@@ -562,7 +563,7 @@
///
/// # References
///
- /// * [Version in RFC4122](https://datatracker.ietf.org/doc/html/rfc4122#section-4.1.3)
+ /// * [Version Field in RFC 9562](https://www.ietf.org/rfc/rfc9562.html#section-4.2)
pub const fn get_version(&self) -> Option<Version> {
match self.get_version_num() {
0 if self.is_nil() => Some(Version::Nil),
@@ -877,35 +878,25 @@
}
/// If the UUID is the correct version (v1, v6, or v7) this will return
- /// the timestamp and counter portion parsed from a V1 UUID.
- ///
- /// Returns `None` if the supplied UUID is not V1.
- ///
- /// The V1 timestamp format defined in RFC4122 specifies a 60-bit
- /// integer representing the number of 100-nanosecond intervals
- /// since 00:00:00.00, 15 Oct 1582.
- ///
- /// [`Timestamp`] offers several options for converting the raw RFC4122
- /// value into more commonly-used formats, such as a unix timestamp.
+ /// the timestamp in a version-agnostic [`Timestamp`]. For other versions
+ /// this will return `None`.
///
/// # Roundtripping
///
/// This method is unlikely to roundtrip a timestamp in a UUID due to the way
/// UUIDs encode timestamps. The timestamp returned from this method will be truncated to
/// 100ns precision for version 1 and 6 UUIDs, and to millisecond precision for version 7 UUIDs.
- ///
- /// [`Timestamp`]: v1/struct.Timestamp.html
pub const fn get_timestamp(&self) -> Option<Timestamp> {
match self.get_version() {
Some(Version::Mac) => {
- let (ticks, counter) = timestamp::decode_rfc4122_timestamp(self);
+ let (ticks, counter) = timestamp::decode_gregorian_timestamp(self);
- Some(Timestamp::from_rfc4122(ticks, counter))
+ Some(Timestamp::from_gregorian(ticks, counter))
}
Some(Version::SortMac) => {
- let (ticks, counter) = timestamp::decode_sorted_rfc4122_timestamp(self);
+ let (ticks, counter) = timestamp::decode_sorted_gregorian_timestamp(self);
- Some(Timestamp::from_rfc4122(ticks, counter))
+ Some(Timestamp::from_gregorian(ticks, counter))
}
Some(Version::SortRand) => {
let millis = timestamp::decode_unix_timestamp_millis(self);
@@ -913,12 +904,27 @@
let seconds = millis / 1000;
let nanos = ((millis % 1000) * 1_000_000) as u32;
- Some(Timestamp {
- seconds,
- nanos,
- #[cfg(any(feature = "v1", feature = "v6"))]
- counter: 0,
- })
+ Some(Timestamp::from_unix_time(seconds, nanos, 0, 0))
+ }
+ _ => None,
+ }
+ }
+
+ /// If the UUID is the correct version (v1, or v6) this will return the
+ /// node value as a 6-byte array. For other versions this will return `None`.
+ pub const fn get_node_id(&self) -> Option<[u8; 6]> {
+ match self.get_version() {
+ Some(Version::Mac) | Some(Version::SortMac) => {
+ let mut node_id = [0; 6];
+
+ node_id[0] = self.0[10];
+ node_id[1] = self.0[11];
+ node_id[2] = self.0[12];
+ node_id[3] = self.0[13];
+ node_id[4] = self.0[14];
+ node_id[5] = self.0[15];
+
+ Some(node_id)
}
_ => None,
}
@@ -932,6 +938,13 @@
}
}
+impl AsRef<Uuid> for Uuid {
+ #[inline]
+ fn as_ref(&self) -> &Uuid {
+ self
+ }
+}
+
impl AsRef<[u8]> for Uuid {
#[inline]
fn as_ref(&self) -> &[u8] {
@@ -963,7 +976,7 @@
//! to change the way a [`Uuid`](../struct.Uuid.html) is serialized
//! and deserialized.
- pub use crate::external::serde_support::compact;
+ pub use crate::external::serde_support::{braced, compact, simple, urn};
}
#[cfg(test)]
@@ -1253,6 +1266,43 @@
),
wasm_bindgen_test
)]
+ fn test_get_timestamp_unsupported_version() {
+ let uuid = new();
+
+ assert_ne!(Version::Mac, uuid.get_version().unwrap());
+ assert_ne!(Version::SortMac, uuid.get_version().unwrap());
+ assert_ne!(Version::SortRand, uuid.get_version().unwrap());
+
+ assert!(uuid.get_timestamp().is_none());
+ }
+
+ #[test]
+ #[cfg_attr(
+ all(
+ target_arch = "wasm32",
+ target_vendor = "unknown",
+ target_os = "unknown"
+ ),
+ wasm_bindgen_test
+ )]
+ fn test_get_node_id_unsupported_version() {
+ let uuid = new();
+
+ assert_ne!(Version::Mac, uuid.get_version().unwrap());
+ assert_ne!(Version::SortMac, uuid.get_version().unwrap());
+
+ assert!(uuid.get_node_id().is_none());
+ }
+
+ #[test]
+ #[cfg_attr(
+ all(
+ target_arch = "wasm32",
+ target_vendor = "unknown",
+ target_os = "unknown"
+ ),
+ wasm_bindgen_test
+ )]
fn test_get_variant() {
let uuid1 = new();
let uuid2 = Uuid::parse_str("550e8400-e29b-41d4-a716-446655440000").unwrap();
@@ -1745,7 +1795,7 @@
fn test_as_bytes() {
let u = new();
let ub = u.as_bytes();
- let ur = u.as_ref();
+ let ur: &[u8] = u.as_ref();
assert_eq!(ub.len(), 16);
assert_eq!(ur.len(), 16);
@@ -1767,7 +1817,7 @@
use crate::std::{convert::TryInto, vec::Vec};
let u = new();
- let ub = u.as_ref();
+ let ub: &[u8] = u.as_ref();
let v: Vec<u8> = u.into();
diff --git a/crates/uuid/src/macros.rs b/crates/uuid/src/macros.rs
index eb95725..865c44d 100644
--- a/crates/uuid/src/macros.rs
+++ b/crates/uuid/src/macros.rs
@@ -4,6 +4,13 @@
#[cfg(feature = "macro-diagnostics")]
#[macro_export]
macro_rules! uuid {
+ ($uuid:expr) => {{
+ const OUTPUT: $crate::Uuid = match $crate::Uuid::try_parse($uuid) {
+ $crate::__macro_support::Ok(u) => u,
+ $crate::__macro_support::Err(_) => panic!("invalid UUID"),
+ };
+ OUTPUT
+ }};
($uuid:literal) => {{
$crate::Uuid::from_bytes($crate::uuid_macro_internal::parse_lit!($uuid))
}};
@@ -13,7 +20,7 @@
#[cfg(not(feature = "macro-diagnostics"))]
#[macro_export]
macro_rules! uuid {
- ($uuid:literal) => {{
+ ($uuid:expr) => {{
const OUTPUT: $crate::Uuid = match $crate::Uuid::try_parse($uuid) {
$crate::__macro_support::Ok(u) => u,
$crate::__macro_support::Err(_) => panic!("invalid UUID"),
@@ -50,6 +57,12 @@
/// # use uuid::uuid;
/// let uuid = uuid!("urn:uuid:F9168C5E-CEB2-4faa-B6BF-329BF39FA1E4");
/// ```
+/// Using a const variable:
+/// ```
+/// # use uuid::uuid;
+/// const UUID_STR: &str = "12345678-1234-5678-1234-567812345678";
+/// let UUID = uuid!(UUID_STR);
+/// ```
///
/// ## Compilation Failures
///
@@ -71,22 +84,5 @@
/// | ^
/// ```
///
-/// Tokens that aren't string literals are also rejected:
-///
-/// ```compile_fail
-/// # use uuid::uuid;
-/// let uuid_str: &str = "550e8400e29b41d4a716446655440000";
-/// let uuid = uuid!(uuid_str);
-/// ```
-///
-/// Provides the following compilation error:
-///
-/// ```txt
-/// error: expected string literal
-/// |
-/// | let uuid = uuid!(uuid_str);
-/// | ^^^^^^^^
-/// ```
-///
/// [uuid::Uuid]: https://docs.rs/uuid/*/uuid/struct.Uuid.html
}
diff --git a/crates/uuid/src/parser.rs b/crates/uuid/src/parser.rs
index 0eabcfe..b400a6d 100644
--- a/crates/uuid/src/parser.rs
+++ b/crates/uuid/src/parser.rs
@@ -73,7 +73,7 @@
/// This function is similar to [`parse_str`], in fact `parse_str` shares
/// the same underlying parser. The difference is that if `try_parse`
/// fails, it won't generate very useful error messages. The `parse_str`
- /// function will eventually be deprecated in favor or `try_parse`.
+ /// function will eventually be deprecated in favor of `try_parse`.
///
/// To parse a UUID from a byte stream instead of a UTF8 string, see
/// [`try_parse_ascii`].
@@ -133,7 +133,7 @@
}
const fn try_parse(input: &[u8]) -> Result<[u8; 16], InvalidUuid> {
- let result = match (input.len(), input) {
+ 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:
@@ -146,21 +146,38 @@
parse_hyphenated(s)
}
// Any other shaped input is immediately invalid
- _ => Err(()),
- };
-
- match result {
- Ok(b) => Ok(b),
- Err(()) => Err(InvalidUuid(input)),
+ _ => Err(InvalidUuid(input)),
}
}
#[inline]
-const fn parse_simple(s: &[u8]) -> Result<[u8; 16], ()> {
+#[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(());
+ return Err(InvalidUuid(s));
}
let mut buf: [u8; 16] = [0; 16];
@@ -175,7 +192,7 @@
// We use `0xff` as a sentinel value to indicate
// an invalid hex character sequence (like the letter `G`)
if h1 | h2 == 0xff {
- return Err(());
+ return Err(InvalidUuid(s));
}
// The upper nibble needs to be shifted into position
@@ -188,11 +205,11 @@
}
#[inline]
-const fn parse_hyphenated(s: &[u8]) -> Result<[u8; 16], ()> {
+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(());
+ return Err(InvalidUuid(s));
}
// We look at two hex-encoded values (4 chars) at a time because
@@ -207,7 +224,7 @@
// First, ensure the hyphens appear in the right places
match [s[8], s[13], s[18], s[23]] {
[b'-', b'-', b'-', b'-'] => {}
- _ => return Err(()),
+ _ => return Err(InvalidUuid(s)),
}
let positions: [u8; 8] = [0, 4, 9, 14, 19, 24, 28, 32];
@@ -225,7 +242,7 @@
let h4 = HEX_TABLE[s[(i + 3) as usize] as usize];
if h1 | h2 | h3 | h4 == 0xff {
- return Err(());
+ return Err(InvalidUuid(s));
}
buf[j * 2] = SHL4_TABLE[h1 as usize] | h2;
@@ -523,6 +540,22 @@
}
#[test]
+ fn test_roundtrip_parse_urn() {
+ let uuid_orig = new();
+ let orig_str = uuid_orig.urn().to_string();
+ let uuid_out = Uuid::from_bytes(parse_urn(orig_str.as_bytes()).unwrap());
+ assert_eq!(uuid_orig, uuid_out);
+ }
+
+ #[test]
+ fn test_roundtrip_parse_braced() {
+ let uuid_orig = new();
+ let orig_str = uuid_orig.braced().to_string();
+ let uuid_out = Uuid::from_bytes(parse_braced(orig_str.as_bytes()).unwrap());
+ assert_eq!(uuid_orig, uuid_out);
+ }
+
+ #[test]
fn test_try_parse_ascii_non_utf8() {
assert!(Uuid::try_parse_ascii(b"67e55044-10b1-426f-9247-bb680e5\0e0c8").is_err());
}
diff --git a/crates/uuid/src/rng.rs b/crates/uuid/src/rng.rs
index dcfbb8d..2ae23cd 100644
--- a/crates/uuid/src/rng.rs
+++ b/crates/uuid/src/rng.rs
@@ -1,5 +1,5 @@
#[cfg(any(feature = "v4", feature = "v7"))]
-pub(crate) fn bytes() -> [u8; 16] {
+pub(crate) fn u128() -> u128 {
#[cfg(not(feature = "fast-rng"))]
{
let mut bytes = [0u8; 16];
@@ -9,7 +9,7 @@
panic!("could not retrieve random bytes for uuid: {}", err)
});
- bytes
+ u128::from_ne_bytes(bytes)
}
#[cfg(feature = "fast-rng")]
@@ -29,7 +29,27 @@
panic!("could not retrieve random bytes for uuid: {}", err)
});
- ((bytes[0] as u16) << 8) | (bytes[1] as u16)
+ u16::from_ne_bytes(bytes)
+ }
+
+ #[cfg(feature = "fast-rng")]
+ {
+ rand::random()
+ }
+}
+
+#[cfg(feature = "v7")]
+pub(crate) fn u64() -> u64 {
+ #[cfg(not(feature = "fast-rng"))]
+ {
+ let mut bytes = [0u8; 8];
+
+ getrandom::getrandom(&mut bytes).unwrap_or_else(|err| {
+ // NB: getrandom::Error has no source; this is adequate display
+ panic!("could not retrieve random bytes for uuid: {}", err)
+ });
+
+ u64::from_ne_bytes(bytes)
}
#[cfg(feature = "fast-rng")]
diff --git a/crates/uuid/src/timestamp.rs b/crates/uuid/src/timestamp.rs
index 27112d1..d147199 100644
--- a/crates/uuid/src/timestamp.rs
+++ b/crates/uuid/src/timestamp.rs
@@ -17,12 +17,15 @@
//!
//! # References
//!
-//! * [Timestamp in RFC4122](https://www.rfc-editor.org/rfc/rfc4122#section-4.1.4)
-//! * [Timestamp in Draft RFC: New UUID Formats, Version 4](https://datatracker.ietf.org/doc/html/draft-peabody-dispatch-new-uuid-format-04#section-6.1)
+//! * [UUID Version 1 in RFC 9562](https://www.ietf.org/rfc/rfc9562.html#section-5.1)
+//! * [UUID Version 7 in RFC 9562](https://www.ietf.org/rfc/rfc9562.html#section-5.7)
+//! * [Timestamp Considerations in RFC 9562](https://www.ietf.org/rfc/rfc9562.html#section-6.1)
+
+use core::cmp;
use crate::Uuid;
-/// The number of 100 nanosecond ticks between the RFC4122 epoch
+/// The number of 100 nanosecond ticks between the RFC 9562 epoch
/// (`1582-10-15 00:00:00`) and the Unix epoch (`1970-01-01 00:00:00`).
pub const UUID_TICKS_BETWEEN_EPOCHS: u64 = 0x01B2_1DD2_1381_4000;
@@ -34,141 +37,155 @@
///
/// # References
///
-/// * [Timestamp in RFC4122](https://www.rfc-editor.org/rfc/rfc4122#section-4.1.4)
-/// * [Timestamp in Draft RFC: New UUID Formats, Version 4](https://datatracker.ietf.org/doc/html/draft-peabody-dispatch-new-uuid-format-04#section-6.1)
-/// * [Clock Sequence in RFC4122](https://datatracker.ietf.org/doc/html/rfc4122#section-4.1.5)
+/// * [Timestamp Considerations in RFC 9562](https://www.ietf.org/rfc/rfc9562.html#section-6.1)
+/// * [UUID Generator States in RFC 9562](https://www.ietf.org/rfc/rfc9562.html#section-6.3)
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Timestamp {
- pub(crate) seconds: u64,
- pub(crate) nanos: u32,
- #[cfg(any(feature = "v1", feature = "v6"))]
- pub(crate) counter: u16,
+ seconds: u64,
+ subsec_nanos: u32,
+ counter: u128,
+ usable_counter_bits: u8,
}
impl Timestamp {
- /// Get a timestamp representing the current system time.
+ /// Get a timestamp representing the current system time and up to a 128-bit counter.
///
/// This method defers to the standard library's `SystemTime` type.
- ///
- /// # Panics
- ///
- /// This method will panic if calculating the elapsed time since the Unix epoch fails.
#[cfg(feature = "std")]
- pub fn now(context: impl ClockSequence<Output = u16>) -> Self {
- #[cfg(not(any(feature = "v1", feature = "v6")))]
- {
- let _ = context;
- }
+ pub fn now(context: impl ClockSequence<Output = impl Into<u128>>) -> Self {
+ let (seconds, subsec_nanos) = now();
- let (seconds, nanos) = now();
+ let (counter, seconds, subsec_nanos) =
+ context.generate_timestamp_sequence(seconds, subsec_nanos);
+ let counter = counter.into();
+ let usable_counter_bits = context.usable_bits() as u8;
Timestamp {
seconds,
- nanos,
- #[cfg(any(feature = "v1", feature = "v6"))]
- counter: context.generate_sequence(seconds, nanos),
- }
- }
-
- /// Construct a `Timestamp` from an RFC4122 timestamp and counter, as used
- /// in versions 1 and 6 UUIDs.
- ///
- /// # Overflow
- ///
- /// If conversion from RFC4122 ticks to the internal timestamp format would overflow
- /// it will wrap.
- pub const fn from_rfc4122(ticks: u64, counter: u16) -> Self {
- #[cfg(not(any(feature = "v1", feature = "v6")))]
- {
- let _ = counter;
- }
-
- let (seconds, nanos) = Self::rfc4122_to_unix(ticks);
-
- Timestamp {
- seconds,
- nanos,
- #[cfg(any(feature = "v1", feature = "v6"))]
+ subsec_nanos,
counter,
+ usable_counter_bits,
}
}
- /// Construct a `Timestamp` from a Unix timestamp, as used in version 7 UUIDs.
+ /// Construct a `Timestamp` from the number of 100 nanosecond ticks since 00:00:00.00,
+ /// 15 October 1582 (the date of Gregorian reform to the Christian calendar) and a 14-bit
+ /// counter, as used in versions 1 and 6 UUIDs.
///
/// # Overflow
///
- /// If conversion from RFC4122 ticks to the internal timestamp format would overflow
+ /// If conversion from RFC 9562 ticks to the internal timestamp format would overflow
/// it will wrap.
- pub fn from_unix(context: impl ClockSequence<Output = u16>, seconds: u64, nanos: u32) -> Self {
- #[cfg(not(any(feature = "v1", feature = "v6")))]
- {
- let _ = context;
+ pub const fn from_gregorian(ticks: u64, counter: u16) -> Self {
+ let (seconds, subsec_nanos) = Self::gregorian_to_unix(ticks);
- Timestamp { seconds, nanos }
- }
- #[cfg(any(feature = "v1", feature = "v6"))]
- {
- let counter = context.generate_sequence(seconds, nanos);
-
- Timestamp {
- seconds,
- nanos,
- counter,
- }
+ Timestamp {
+ seconds,
+ subsec_nanos,
+ counter: counter as u128,
+ usable_counter_bits: 14,
}
}
- /// Get the value of the timestamp as an RFC4122 timestamp and counter,
- /// as used in versions 1 and 6 UUIDs.
+ /// Construct a `Timestamp` from a Unix timestamp and up to a 128-bit counter, as used in version 7 UUIDs.
+ pub const fn from_unix_time(
+ seconds: u64,
+ subsec_nanos: u32,
+ counter: u128,
+ usable_counter_bits: u8,
+ ) -> Self {
+ Timestamp {
+ seconds,
+ subsec_nanos,
+ counter,
+ usable_counter_bits,
+ }
+ }
+
+ /// Construct a `Timestamp` from a Unix timestamp and up to a 128-bit counter, as used in version 7 UUIDs.
+ pub fn from_unix(
+ context: impl ClockSequence<Output = impl Into<u128>>,
+ seconds: u64,
+ subsec_nanos: u32,
+ ) -> Self {
+ let (counter, seconds, subsec_nanos) =
+ context.generate_timestamp_sequence(seconds, subsec_nanos);
+ let counter = counter.into();
+ let usable_counter_bits = context.usable_bits() as u8;
+
+ Timestamp {
+ seconds,
+ subsec_nanos,
+ counter,
+ usable_counter_bits,
+ }
+ }
+
+ /// Get the value of the timestamp as the number of 100 nanosecond ticks since 00:00:00.00,
+ /// 15 October 1582 and a 14-bit counter, as used in versions 1 and 6 UUIDs.
///
/// # Overflow
///
- /// If conversion from RFC4122 ticks to the internal timestamp format would overflow
- /// it will wrap.
- #[cfg(any(feature = "v1", feature = "v6"))]
- pub const fn to_rfc4122(&self) -> (u64, u16) {
+ /// If conversion from the internal timestamp format to ticks would overflow
+ /// then it will wrap.
+ ///
+ /// If the internal counter is wider than 14 bits then it will be truncated to 14 bits.
+ pub const fn to_gregorian(&self) -> (u64, u16) {
(
- Self::unix_to_rfc4122_ticks(self.seconds, self.nanos),
- self.counter,
+ Self::unix_to_gregorian_ticks(self.seconds, self.subsec_nanos),
+ (self.counter as u16) & 0x3FFF,
)
}
- /// Get the value of the timestamp as a Unix timestamp, as used in version 7 UUIDs.
- ///
- /// # Overflow
- ///
- /// If conversion from RFC4122 ticks to the internal timestamp format would overflow
- /// it will wrap.
- pub const fn to_unix(&self) -> (u64, u32) {
- (self.seconds, self.nanos)
+ // NOTE: This method is not public; the usable counter bits are lost in a version 7 UUID
+ // so can't be reliably recovered.
+ #[cfg(feature = "v7")]
+ pub(crate) const fn counter(&self) -> (u128, u8) {
+ (self.counter, self.usable_counter_bits)
}
- #[cfg(any(feature = "v1", feature = "v6"))]
- const fn unix_to_rfc4122_ticks(seconds: u64, nanos: u32) -> u64 {
+ /// Get the value of the timestamp as a Unix timestamp, as used in version 7 UUIDs.
+ pub const fn to_unix(&self) -> (u64, u32) {
+ (self.seconds, self.subsec_nanos)
+ }
+
+ const fn unix_to_gregorian_ticks(seconds: u64, nanos: u32) -> u64 {
UUID_TICKS_BETWEEN_EPOCHS
.wrapping_add(seconds.wrapping_mul(10_000_000))
.wrapping_add(nanos as u64 / 100)
}
- const fn rfc4122_to_unix(ticks: u64) -> (u64, u32) {
+ const fn gregorian_to_unix(ticks: u64) -> (u64, u32) {
(
ticks.wrapping_sub(UUID_TICKS_BETWEEN_EPOCHS) / 10_000_000,
(ticks.wrapping_sub(UUID_TICKS_BETWEEN_EPOCHS) % 10_000_000) as u32 * 100,
)
}
+}
- #[deprecated(note = "use `to_unix` instead; this method will be removed in a future release")]
- /// Get the number of fractional nanoseconds in the Unix timestamp.
- ///
- /// This method is deprecated and probably doesn't do what you're expecting it to.
- /// It doesn't return the timestamp as nanoseconds since the Unix epoch, it returns
- /// the fractional seconds of the timestamp.
+#[doc(hidden)]
+impl Timestamp {
+ #[deprecated(since = "1.11.0", note = "use `Timestamp::from_gregorian(ticks, counter)`")]
+ pub const fn from_rfc4122(ticks: u64, counter: u16) -> Self {
+ Timestamp::from_gregorian(ticks, counter)
+ }
+
+ #[deprecated(since = "1.11.0", note = "use `Timestamp::to_gregorian()`")]
+ pub const fn to_rfc4122(&self) -> (u64, u16) {
+ self.to_gregorian()
+ }
+
+ #[deprecated(since = "1.2.0", note = "`Timestamp::to_unix_nanos()` is deprecated and will be removed: use `Timestamp::to_unix()`")]
pub const fn to_unix_nanos(&self) -> u32 {
- panic!("`Timestamp::to_unix_nanos` is deprecated and will be removed: use `Timestamp::to_unix` instead")
+ panic!("`Timestamp::to_unix_nanos()` is deprecated and will be removed: use `Timestamp::to_unix()`")
}
}
-pub(crate) const fn encode_rfc4122_timestamp(ticks: u64, counter: u16, node_id: &[u8; 6]) -> Uuid {
+pub(crate) const fn encode_gregorian_timestamp(
+ ticks: u64,
+ counter: u16,
+ node_id: &[u8; 6],
+) -> Uuid {
let time_low = (ticks & 0xFFFF_FFFF) as u32;
let time_mid = ((ticks >> 32) & 0xFFFF) as u16;
let time_high_and_version = (((ticks >> 48) & 0x0FFF) as u16) | (1 << 12);
@@ -187,7 +204,7 @@
Uuid::from_fields(time_low, time_mid, time_high_and_version, &d4)
}
-pub(crate) const fn decode_rfc4122_timestamp(uuid: &Uuid) -> (u64, u16) {
+pub(crate) const fn decode_gregorian_timestamp(uuid: &Uuid) -> (u64, u16) {
let bytes = uuid.as_bytes();
let ticks: u64 = ((bytes[6] & 0x0F) as u64) << 56
@@ -204,7 +221,7 @@
(ticks, counter)
}
-pub(crate) const fn encode_sorted_rfc4122_timestamp(
+pub(crate) const fn encode_sorted_gregorian_timestamp(
ticks: u64,
counter: u16,
node_id: &[u8; 6],
@@ -227,7 +244,7 @@
Uuid::from_fields(time_high, time_mid, time_low_and_version, &d4)
}
-pub(crate) const fn decode_sorted_rfc4122_timestamp(uuid: &Uuid) -> (u64, u16) {
+pub(crate) const fn decode_sorted_gregorian_timestamp(uuid: &Uuid) -> (u64, u16) {
let bytes = uuid.as_bytes();
let ticks: u64 = ((bytes[0]) as u64) << 52
@@ -244,25 +261,29 @@
(ticks, counter)
}
-pub(crate) const fn encode_unix_timestamp_millis(millis: u64, random_bytes: &[u8; 10]) -> Uuid {
+pub(crate) const fn encode_unix_timestamp_millis(
+ millis: u64,
+ counter_random_bytes: &[u8; 10],
+) -> Uuid {
let millis_high = ((millis >> 16) & 0xFFFF_FFFF) as u32;
let millis_low = (millis & 0xFFFF) as u16;
- let random_and_version =
- (random_bytes[1] as u16 | ((random_bytes[0] as u16) << 8) & 0x0FFF) | (0x7 << 12);
+ let counter_random_version = (counter_random_bytes[1] as u16
+ | ((counter_random_bytes[0] as u16) << 8) & 0x0FFF)
+ | (0x7 << 12);
let mut d4 = [0; 8];
- d4[0] = (random_bytes[2] & 0x3F) | 0x80;
- d4[1] = random_bytes[3];
- d4[2] = random_bytes[4];
- d4[3] = random_bytes[5];
- d4[4] = random_bytes[6];
- d4[5] = random_bytes[7];
- d4[6] = random_bytes[8];
- d4[7] = random_bytes[9];
+ d4[0] = (counter_random_bytes[2] & 0x3F) | 0x80;
+ d4[1] = counter_random_bytes[3];
+ d4[2] = counter_random_bytes[4];
+ d4[3] = counter_random_bytes[5];
+ d4[4] = counter_random_bytes[6];
+ d4[5] = counter_random_bytes[7];
+ d4[6] = counter_random_bytes[8];
+ d4[7] = counter_random_bytes[9];
- Uuid::from_fields(millis_high, millis_low, random_and_version, &d4)
+ Uuid::from_fields(millis_high, millis_low, counter_random_version, &d4)
}
pub(crate) const fn decode_unix_timestamp_millis(uuid: &Uuid) -> u64 {
@@ -307,6 +328,7 @@
#[cfg(all(
feature = "std",
+ not(miri),
any(
not(feature = "js"),
not(all(
@@ -324,12 +346,29 @@
(dur.as_secs(), dur.subsec_nanos())
}
-/// A counter that can be used by version 1 and version 6 UUIDs to support
+#[cfg(all(feature = "std", miri))]
+fn now() -> (u64, u32) {
+ use std::{sync::Mutex, time::Duration};
+
+ static TS: Mutex<u64> = Mutex::new(0);
+
+ let ts = Duration::from_nanos({
+ let mut ts = TS.lock().unwrap();
+ *ts += 1;
+ *ts
+ });
+
+ (ts.as_secs(), ts.subsec_nanos())
+}
+
+/// A counter that can be used by versions 1 and 6 UUIDs to support
/// the uniqueness of timestamps.
///
/// # References
///
-/// * [Clock Sequence in RFC4122](https://datatracker.ietf.org/doc/html/rfc4122#section-4.1.5)
+/// * [UUID Version 1 in RFC 9562](https://www.ietf.org/rfc/rfc9562.html#section-5.1)
+/// * [UUID Version 6 in RFC 9562](https://www.ietf.org/rfc/rfc9562.html#section-5.6)
+/// * [UUID Generator States in RFC 9562](https://www.ietf.org/rfc/rfc9562.html#section-6.3)
pub trait ClockSequence {
/// The type of sequence returned by this counter.
type Output;
@@ -337,14 +376,63 @@
/// Get the next value in the sequence to feed into a timestamp.
///
/// This method will be called each time a [`Timestamp`] is constructed.
+ ///
+ /// Any bits beyond [`ClockSequence::usable_bits`] in the output must be unset.
fn generate_sequence(&self, seconds: u64, subsec_nanos: u32) -> Self::Output;
+
+ /// Get the next value in the sequence, potentially also adjusting the timestamp.
+ ///
+ /// This method should be preferred over `generate_sequence`.
+ ///
+ /// Any bits beyond [`ClockSequence::usable_bits`] in the output must be unset.
+ fn generate_timestamp_sequence(
+ &self,
+ seconds: u64,
+ subsec_nanos: u32,
+ ) -> (Self::Output, u64, u32) {
+ (
+ self.generate_sequence(seconds, subsec_nanos),
+ seconds,
+ subsec_nanos,
+ )
+ }
+
+ /// The number of usable bits from the least significant bit in the result of [`ClockSequence::generate_sequence`]
+ /// or [`ClockSequence::generate_timestamp_sequence`].
+ ///
+ /// The number of usable bits must not exceed 128.
+ ///
+ /// The number of usable bits is not expected to change between calls. An implementation of `ClockSequence` should
+ /// always return the same value from this method.
+ fn usable_bits(&self) -> usize
+ where
+ Self::Output: Sized,
+ {
+ cmp::min(128, core::mem::size_of::<Self::Output>())
+ }
}
impl<'a, T: ClockSequence + ?Sized> ClockSequence for &'a T {
type Output = T::Output;
+
fn generate_sequence(&self, seconds: u64, subsec_nanos: u32) -> Self::Output {
(**self).generate_sequence(seconds, subsec_nanos)
}
+
+ fn generate_timestamp_sequence(
+ &self,
+ seconds: u64,
+ subsec_nanos: u32,
+ ) -> (Self::Output, u64, u32) {
+ (**self).generate_timestamp_sequence(seconds, subsec_nanos)
+ }
+
+ fn usable_bits(&self) -> usize
+ where
+ Self::Output: Sized,
+ {
+ (**self).usable_bits()
+ }
}
/// Default implementations for the [`ClockSequence`] trait.
@@ -352,12 +440,476 @@
use super::ClockSequence;
#[cfg(any(feature = "v1", feature = "v6"))]
- use atomic::{Atomic, Ordering};
+ mod v1_support {
+ use super::*;
+
+ use atomic::{Atomic, Ordering};
+
+ #[cfg(all(feature = "std", feature = "rng"))]
+ static CONTEXT: Context = Context {
+ count: Atomic::new(0),
+ };
+
+ #[cfg(all(feature = "std", feature = "rng"))]
+ static CONTEXT_INITIALIZED: Atomic<bool> = Atomic::new(false);
+
+ #[cfg(all(feature = "std", feature = "rng"))]
+ pub(crate) fn shared_context() -> &'static Context {
+ // If the context is in its initial state then assign it to a random value
+ // It doesn't matter if multiple threads observe `false` here and initialize the context
+ if CONTEXT_INITIALIZED
+ .compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed)
+ .is_ok()
+ {
+ CONTEXT.count.store(crate::rng::u16(), Ordering::Release);
+ }
+
+ &CONTEXT
+ }
+
+ /// A thread-safe, wrapping counter that produces 14-bit values.
+ ///
+ /// This type works by:
+ ///
+ /// 1. Atomically incrementing the counter value for each timestamp.
+ /// 2. Wrapping the counter back to zero if it overflows its 14-bit storage.
+ ///
+ /// 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 Context {
+ count: Atomic<u16>,
+ }
+
+ impl Context {
+ /// 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 [`Context::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 Context {
+ 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
+ }
+ }
+
+ #[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 = Context::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);
+ }
+ }
+ }
+
+ #[cfg(any(feature = "v1", feature = "v6"))]
+ pub use v1_support::*;
+
+ #[cfg(feature = "std")]
+ mod std_support {
+ use super::*;
+
+ use core::panic::{AssertUnwindSafe, RefUnwindSafe};
+ use std::{sync::Mutex, thread::LocalKey};
+
+ /// A wrapper for a context that uses thread-local storage.
+ pub struct ThreadLocalContext<C: 'static>(&'static LocalKey<C>);
+
+ impl<C> std::fmt::Debug for ThreadLocalContext<C> {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ f.debug_struct("ThreadLocalContext").finish_non_exhaustive()
+ }
+ }
+
+ impl<C: 'static> ThreadLocalContext<C> {
+ /// Wrap a thread-local container with a context.
+ pub const fn new(local_key: &'static LocalKey<C>) -> Self {
+ ThreadLocalContext(local_key)
+ }
+ }
+
+ impl<C: ClockSequence + 'static> ClockSequence for ThreadLocalContext<C> {
+ type Output = C::Output;
+
+ fn generate_sequence(&self, seconds: u64, subsec_nanos: u32) -> Self::Output {
+ self.0
+ .with(|ctxt| ctxt.generate_sequence(seconds, subsec_nanos))
+ }
+
+ fn generate_timestamp_sequence(
+ &self,
+ seconds: u64,
+ subsec_nanos: u32,
+ ) -> (Self::Output, u64, u32) {
+ self.0
+ .with(|ctxt| ctxt.generate_timestamp_sequence(seconds, subsec_nanos))
+ }
+
+ fn usable_bits(&self) -> usize {
+ self.0.with(|ctxt| ctxt.usable_bits())
+ }
+ }
+
+ impl<C: ClockSequence> ClockSequence for AssertUnwindSafe<C> {
+ type Output = C::Output;
+
+ 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,
+ {
+ self.0.usable_bits()
+ }
+ }
+
+ impl<C: ClockSequence + RefUnwindSafe> ClockSequence for Mutex<C> {
+ type Output = C::Output;
+
+ fn generate_sequence(&self, seconds: u64, subsec_nanos: u32) -> Self::Output {
+ self.lock()
+ .unwrap_or_else(|err| err.into_inner())
+ .generate_sequence(seconds, subsec_nanos)
+ }
+
+ fn generate_timestamp_sequence(
+ &self,
+ seconds: u64,
+ subsec_nanos: u32,
+ ) -> (Self::Output, u64, u32) {
+ self.lock()
+ .unwrap_or_else(|err| err.into_inner())
+ .generate_timestamp_sequence(seconds, subsec_nanos)
+ }
+
+ fn usable_bits(&self) -> usize
+ where
+ Self::Output: Sized,
+ {
+ self.lock()
+ .unwrap_or_else(|err| err.into_inner())
+ .usable_bits()
+ }
+ }
+ }
+
+ #[cfg(feature = "std")]
+ pub use std_support::*;
+
+ #[cfg(feature = "v7")]
+ mod v7_support {
+ use super::*;
+
+ use core::{cell::Cell, 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.
+ ///
+ /// This type works by:
+ ///
+ /// 1. Reseeding the counter each millisecond with a random 41-bit value. The 42nd bit
+ /// is left unset so the counter can safely increment over the millisecond.
+ /// 2. Wrapping the counter back to zero if it overflows its 42-bit storage and adding a
+ /// millisecond to the timestamp.
+ ///
+ /// 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 {
+ last_reseed: Cell<LastReseed>,
+ counter: Cell<u64>,
+ }
+
+ #[derive(Debug, Default, Clone, Copy)]
+ struct LastReseed {
+ millis: u64,
+ ts_seconds: u64,
+ ts_subsec_nanos: u32,
+ }
+
+ impl LastReseed {
+ fn from_millis(millis: u64) -> Self {
+ LastReseed {
+ millis,
+ ts_seconds: millis / 1_000,
+ ts_subsec_nanos: (millis % 1_000) as u32 * 1_000_000,
+ }
+ }
+ }
+
+ 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 {
+ last_reseed: Cell::new(LastReseed {
+ millis: 0,
+ ts_seconds: 0,
+ ts_subsec_nanos: 0,
+ }),
+ counter: Cell::new(0),
+ }
+ }
+ }
+
+ 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 millis = (seconds * 1_000).saturating_add(subsec_nanos as u64 / 1_000_000);
+
+ let last_reseed = self.last_reseed.get();
+
+ // If the observed system time has shifted forwards then regenerate the counter
+ if millis > last_reseed.millis {
+ let last_reseed = LastReseed::from_millis(millis);
+ self.last_reseed.set(last_reseed);
+
+ let counter = crate::rng::u64() & RESEED_MASK;
+ self.counter.set(counter);
+
+ (counter, last_reseed.ts_seconds, last_reseed.ts_subsec_nanos)
+ }
+ // If the observed system time has not shifted forwards then increment the counter
+ else {
+ // 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
+ let millis = ();
+ let _ = millis;
+
+ // Guaranteed to never overflow u64
+ let counter = self.counter.get() + 1;
+
+ // If the counter has not overflowed its 42-bit storage then return it
+ if counter <= MAX_COUNTER {
+ self.counter.set(counter);
+
+ (counter, last_reseed.ts_seconds, last_reseed.ts_subsec_nanos)
+ }
+ // 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
+ else {
+ // Increment the timestamp by 1 milli
+ let last_reseed = LastReseed::from_millis(last_reseed.millis + 1);
+ self.last_reseed.set(last_reseed);
+
+ // Reseed the counter
+ let counter = crate::rng::u64() & RESEED_MASK;
+ self.counter.set(counter);
+
+ (counter, last_reseed.ts_seconds, last_reseed.ts_subsec_nanos)
+ }
+ }
+ }
+
+ fn usable_bits(&self) -> usize {
+ USABLE_BITS
+ }
+ }
+
+ #[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
+ }
+ }
+
+ #[cfg(test)]
+ mod tests {
+ use core::time::Duration;
+
+ use super::*;
+
+ use crate::Timestamp;
+
+ #[test]
+ fn context() {
+ let seconds = 1_496_854_535;
+ let subsec_nanos = 812_946_000;
+
+ let context = ContextV7::new();
+
+ let ts1 = Timestamp::from_unix(&context, seconds, subsec_nanos);
+ assert_eq!(42, ts1.usable_counter_bits);
+
+ // Backwards second
+ let seconds = 1_496_854_534;
+
+ let ts2 = Timestamp::from_unix(&context, seconds, subsec_nanos);
+
+ // The backwards time should be ignored
+ // The counter should still increment
+ assert_eq!(ts1.seconds, ts2.seconds);
+ assert_eq!(ts1.subsec_nanos, ts2.subsec_nanos);
+ assert_eq!(ts1.counter + 1, ts2.counter);
+
+ // Forwards second
+ let seconds = 1_496_854_536;
+
+ let ts3 = Timestamp::from_unix(&context, seconds, subsec_nanos);
+
+ // The counter should have reseeded
+ assert_ne!(ts2.counter + 1, ts3.counter);
+ assert_ne!(0, ts3.counter);
+ }
+
+ #[test]
+ fn context_wrap() {
+ let seconds = 1_496_854_535u64;
+ let subsec_nanos = 812_946_000u32;
+
+ let millis = (seconds * 1000).saturating_add(subsec_nanos as u64 / 1_000_000);
+
+ // This context will wrap
+ let context = ContextV7 {
+ last_reseed: Cell::new(LastReseed::from_millis(millis)),
+ counter: Cell::new(u64::MAX >> 22),
+ };
+
+ let ts = Timestamp::from_unix(&context, seconds, subsec_nanos);
+
+ // The timestamp should be incremented by 1ms
+ let expected_ts = Duration::new(seconds, subsec_nanos / 1_000_000 * 1_000_000)
+ + Duration::from_millis(1);
+ assert_eq!(expected_ts.as_secs(), ts.seconds);
+ assert_eq!(expected_ts.subsec_nanos(), ts.subsec_nanos);
+
+ // The counter should have reseeded
+ assert!(ts.counter < (u64::MAX >> 22) as u128);
+ assert_ne!(0, ts.counter);
+ }
+ }
+ }
+
+ #[cfg(feature = "v7")]
+ pub use v7_support::*;
/// An empty counter that will always return the value `0`.
///
- /// This type should be used when constructing timestamps for version 7 UUIDs,
- /// since they don't need a counter for uniqueness.
+ /// This type can be used when constructing version 7 UUIDs. When used to
+ /// construct a version 7 UUID, the entire counter segment of the UUID will be
+ /// filled with a random value. This type does not maintain ordering of UUIDs
+ /// within a millisecond but is efficient.
+ ///
+ /// 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, the counter
+ /// segment will remain zero.
#[derive(Debug, Clone, Copy, Default)]
pub struct NoContext;
@@ -367,72 +919,9 @@
fn generate_sequence(&self, _seconds: u64, _nanos: u32) -> Self::Output {
0
}
- }
- #[cfg(all(any(feature = "v1", feature = "v6"), feature = "std", feature = "rng"))]
- static CONTEXT: Context = Context {
- count: Atomic::new(0),
- };
-
- #[cfg(all(any(feature = "v1", feature = "v6"), feature = "std", feature = "rng"))]
- static CONTEXT_INITIALIZED: Atomic<bool> = Atomic::new(false);
-
- #[cfg(all(any(feature = "v1", feature = "v6"), feature = "std", feature = "rng"))]
- pub(crate) fn shared_context() -> &'static Context {
- // If the context is in its initial state then assign it to a random value
- // It doesn't matter if multiple threads observe `false` here and initialize the context
- if CONTEXT_INITIALIZED
- .compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed)
- .is_ok()
- {
- CONTEXT.count.store(crate::rng::u16(), Ordering::Release);
- }
-
- &CONTEXT
- }
-
- /// A thread-safe, wrapping counter that produces 14-bit numbers.
- ///
- /// This type should be used when constructing version 1 and version 6 UUIDs.
- #[derive(Debug)]
- #[cfg(any(feature = "v1", feature = "v6"))]
- pub struct Context {
- count: Atomic<u16>,
- }
-
- #[cfg(any(feature = "v1", feature = "v6"))]
- impl Context {
- /// 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 [`Context::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()),
- }
- }
- }
-
- #[cfg(any(feature = "v1", feature = "v6"))]
- impl ClockSequence for Context {
- type Output = u16;
-
- fn generate_sequence(&self, _seconds: u64, _nanos: u32) -> Self::Output {
- // RFC4122 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 {
+ 0
}
}
}
@@ -457,12 +946,27 @@
),
wasm_bindgen_test
)]
- fn rfc4122_unix_does_not_panic() {
+ fn gregorian_unix_does_not_panic() {
// Ensure timestamp conversions never panic
- Timestamp::unix_to_rfc4122_ticks(u64::MAX, 0);
- Timestamp::unix_to_rfc4122_ticks(0, u32::MAX);
- Timestamp::unix_to_rfc4122_ticks(u64::MAX, u32::MAX);
+ Timestamp::unix_to_gregorian_ticks(u64::MAX, 0);
+ Timestamp::unix_to_gregorian_ticks(0, u32::MAX);
+ Timestamp::unix_to_gregorian_ticks(u64::MAX, u32::MAX);
- Timestamp::rfc4122_to_unix(u64::MAX);
+ Timestamp::gregorian_to_unix(u64::MAX);
+ }
+
+ #[test]
+ #[cfg_attr(
+ all(
+ target_arch = "wasm32",
+ target_vendor = "unknown",
+ target_os = "unknown"
+ ),
+ wasm_bindgen_test
+ )]
+ fn to_gregorian_truncates_to_usable_bits() {
+ let ts = Timestamp::from_gregorian(123, u16::MAX);
+
+ assert_eq!((123, u16::MAX >> 2), ts.to_gregorian());
}
}
diff --git a/crates/uuid/src/v1.rs b/crates/uuid/src/v1.rs
index 41febab..cdd6333 100644
--- a/crates/uuid/src/v1.rs
+++ b/crates/uuid/src/v1.rs
@@ -48,7 +48,7 @@
/// # Examples
///
/// A UUID can be created from a unix [`Timestamp`] with a
- /// [`ClockSequence`]. RFC4122 requires the clock sequence
+ /// [`ClockSequence`]. RFC 9562 requires the clock sequence
/// is seeded with a random value:
///
/// ```
@@ -66,12 +66,12 @@
/// );
/// ```
///
- /// The timestamp can also be created manually as per RFC4122:
+ /// The timestamp can also be created manually as per RFC 9562:
///
/// ```
/// # use uuid::{Uuid, Timestamp, Context, ClockSequence};
/// let context = Context::new(42);
- /// let ts = Timestamp::from_rfc4122(14976234442241191232, context.generate_sequence(0, 0));
+ /// let ts = Timestamp::from_gregorian(14976234442241191232, context.generate_sequence(0, 0));
///
/// let uuid = Uuid::new_v1(ts, &[1, 2, 3, 4, 5, 6]);
///
@@ -83,15 +83,15 @@
///
/// # References
///
- /// * [Version 1 UUIDs in RFC4122](https://www.rfc-editor.org/rfc/rfc4122#section-4.2)
+ /// * [UUID Version 1 in RFC 9562](https://www.ietf.org/rfc/rfc9562.html#section-5.1)
///
/// [`Timestamp`]: v1/struct.Timestamp.html
/// [`ClockSequence`]: v1/trait.ClockSequence.html
/// [`Context`]: v1/struct.Context.html
pub fn new_v1(ts: Timestamp, node_id: &[u8; 6]) -> Self {
- let (ticks, counter) = ts.to_rfc4122();
+ let (ticks, counter) = ts.to_gregorian();
- Builder::from_rfc4122_timestamp(ticks, counter, node_id).into_uuid()
+ Builder::from_gregorian_timestamp(ticks, counter, node_id).into_uuid()
}
}
@@ -131,10 +131,12 @@
"20616934-4ba2-11e7-8000-010203040506"
);
- let ts = uuid.get_timestamp().unwrap().to_rfc4122();
+ let ts = uuid.get_timestamp().unwrap().to_gregorian();
assert_eq!(ts.0 - 0x01B2_1DD2_1381_4000, 14_968_545_358_129_460);
+ assert_eq!(Some(node), uuid.get_node_id(),);
+
// Ensure parsing the same UUID produces the same timestamp
let parsed = Uuid::parse_str("20616934-4ba2-11e7-8000-010203040506").unwrap();
@@ -142,6 +144,8 @@
uuid.get_timestamp().unwrap(),
parsed.get_timestamp().unwrap()
);
+
+ assert_eq!(uuid.get_node_id().unwrap(), parsed.get_node_id().unwrap(),);
}
#[test]
@@ -162,39 +166,4 @@
assert_eq!(uuid.get_version(), Some(Version::Mac));
assert_eq!(uuid.get_variant(), Variant::RFC4122);
}
-
- #[test]
- #[cfg_attr(
- all(
- target_arch = "wasm32",
- target_vendor = "unknown",
- target_os = "unknown"
- ),
- wasm_bindgen_test
- )]
- fn test_new_context() {
- let time: u64 = 1_496_854_535;
- let time_fraction: u32 = 812_946_000;
- let node = [1, 2, 3, 4, 5, 6];
-
- // This context will wrap
- let context = Context::new(u16::MAX >> 2);
-
- let uuid1 = Uuid::new_v1(Timestamp::from_unix(&context, time, time_fraction), &node);
-
- let time: u64 = 1_496_854_536;
-
- let uuid2 = Uuid::new_v1(Timestamp::from_unix(&context, time, time_fraction), &node);
-
- assert_eq!(uuid1.get_timestamp().unwrap().to_rfc4122().1, 16383);
- assert_eq!(uuid2.get_timestamp().unwrap().to_rfc4122().1, 0);
-
- let time = 1_496_854_535;
-
- let uuid3 = Uuid::new_v1(Timestamp::from_unix(&context, time, time_fraction), &node);
- let uuid4 = Uuid::new_v1(Timestamp::from_unix(&context, time, time_fraction), &node);
-
- assert_eq!(uuid3.get_timestamp().unwrap().to_rfc4122().1, 1);
- assert_eq!(uuid4.get_timestamp().unwrap().to_rfc4122().1, 2);
- }
}
diff --git a/crates/uuid/src/v3.rs b/crates/uuid/src/v3.rs
index ed356d4..84a1e26 100644
--- a/crates/uuid/src/v3.rs
+++ b/crates/uuid/src/v3.rs
@@ -27,7 +27,8 @@
///
/// # References
///
- /// * [Version 3 and 5 UUIDs in RFC4122](https://www.rfc-editor.org/rfc/rfc4122#section-4.3)
+ /// * [UUID Version 3 in RFC 9562](https://www.ietf.org/rfc/rfc9562.html#section-5.3)
+ /// * [Name-Based UUID Generation in RFC 9562](https://www.ietf.org/rfc/rfc9562.html#section-6.5)
///
/// [`NAMESPACE_DNS`]: #associatedconstant.NAMESPACE_DNS
/// [`NAMESPACE_OID`]: #associatedconstant.NAMESPACE_OID
diff --git a/crates/uuid/src/v4.rs b/crates/uuid/src/v4.rs
index 3c42473..14d755e 100644
--- a/crates/uuid/src/v4.rs
+++ b/crates/uuid/src/v4.rs
@@ -26,12 +26,16 @@
///
/// # References
///
- /// * [Version 4 UUIDs in RFC4122](https://www.rfc-editor.org/rfc/rfc4122#section-4.4)
+ /// * [UUID Version 4 in RFC 9562](https://www.ietf.org/rfc/rfc9562.html#section-5.4)
///
/// [`getrandom`]: https://crates.io/crates/getrandom
/// [from_random_bytes]: struct.Builder.html#method.from_random_bytes
pub fn new_v4() -> Uuid {
- crate::Builder::from_random_bytes(crate::rng::bytes()).into_uuid()
+ // This is an optimized method for generating random UUIDs that just masks
+ // out the bits for the version and variant and sets them both together
+ Uuid::from_u128(
+ crate::rng::u128() & 0xFFFFFFFFFFFF4FFFBFFFFFFFFFFFFFFF | 0x40008000000000000000,
+ )
}
}
diff --git a/crates/uuid/src/v5.rs b/crates/uuid/src/v5.rs
index 265aa1a..f29ce73 100644
--- a/crates/uuid/src/v5.rs
+++ b/crates/uuid/src/v5.rs
@@ -26,7 +26,8 @@
///
/// # References
///
- /// * [Version 3 and 5 UUIDs in RFC4122](https://www.rfc-editor.org/rfc/rfc4122#section-4.3)
+ /// * [UUID Version 5 in RFC 9562](https://www.ietf.org/rfc/rfc9562.html#section-5.5)
+ /// * [Name-Based UUID Generation in RFC 9562](https://www.ietf.org/rfc/rfc9562.html#section-6.5)
///
/// [`NAMESPACE_DNS`]: struct.Uuid.html#associatedconst.NAMESPACE_DNS
/// [`NAMESPACE_OID`]: struct.Uuid.html#associatedconst.NAMESPACE_OID
diff --git a/crates/uuid/src/v6.rs b/crates/uuid/src/v6.rs
index 09c156f..05f27db 100644
--- a/crates/uuid/src/v6.rs
+++ b/crates/uuid/src/v6.rs
@@ -49,7 +49,7 @@
/// # Examples
///
/// A UUID can be created from a unix [`Timestamp`] with a
- /// [`ClockSequence`]. RFC4122 requires the clock sequence
+ /// [`ClockSequence`]. RFC 9562 requires the clock sequence
/// is seeded with a random value:
///
/// ```rust
@@ -66,13 +66,13 @@
/// );
/// ```
///
- /// The timestamp can also be created manually as per RFC4122:
+ /// The timestamp can also be created manually as per RFC 9562:
///
/// ```
/// # use uuid::{Uuid, Timestamp, Context, ClockSequence};
/// # fn random_seed() -> u16 { 42 }
/// let context = Context::new(random_seed());
- /// let ts = Timestamp::from_rfc4122(14976241191231231313, context.generate_sequence(0, 0) );
+ /// let ts = Timestamp::from_gregorian(14976241191231231313, context.generate_sequence(0, 0));
///
/// let uuid = Uuid::new_v6(ts, &[1, 2, 3, 4, 5, 6]);
///
@@ -84,15 +84,15 @@
///
/// # References
///
- /// * [Version 6 UUIDs in Draft RFC: New UUID Formats, Version 4](https://datatracker.ietf.org/doc/html/draft-peabody-dispatch-new-uuid-format-04#section-5.1)
+ /// * [UUID Version 6 in RFC 9562](https://www.ietf.org/rfc/rfc9562.html#section-5.6)
///
/// [`Timestamp`]: timestamp/struct.Timestamp.html
/// [`ClockSequence`]: timestamp/trait.ClockSequence.html
/// [`Context`]: timestamp/context/struct.Context.html
pub fn new_v6(ts: Timestamp, node_id: &[u8; 6]) -> Self {
- let (ticks, counter) = ts.to_rfc4122();
+ let (ticks, counter) = ts.to_gregorian();
- Builder::from_sorted_rfc4122_timestamp(ticks, counter, node_id).into_uuid()
+ Builder::from_sorted_gregorian_timestamp(ticks, counter, node_id).into_uuid()
}
}
@@ -133,10 +133,12 @@
"1e74ba22-0616-6934-8000-010203040506"
);
- let ts = uuid.get_timestamp().unwrap().to_rfc4122();
+ let ts = uuid.get_timestamp().unwrap().to_gregorian();
assert_eq!(ts.0 - 0x01B2_1DD2_1381_4000, 14_968_545_358_129_460);
+ assert_eq!(Some(node), uuid.get_node_id(),);
+
// Ensure parsing the same UUID produces the same timestamp
let parsed = Uuid::parse_str("1e74ba22-0616-6934-8000-010203040506").unwrap();
@@ -144,6 +146,8 @@
uuid.get_timestamp().unwrap(),
parsed.get_timestamp().unwrap()
);
+
+ assert_eq!(uuid.get_node_id().unwrap(), parsed.get_node_id().unwrap(),);
}
#[test]
@@ -164,39 +168,4 @@
assert_eq!(uuid.get_version(), Some(Version::SortMac));
assert_eq!(uuid.get_variant(), Variant::RFC4122);
}
-
- #[test]
- #[cfg_attr(
- all(
- target_arch = "wasm32",
- target_vendor = "unknown",
- target_os = "unknown"
- ),
- wasm_bindgen_test
- )]
- fn test_new_context() {
- let time: u64 = 1_496_854_535;
- let time_fraction: u32 = 812_946_000;
- let node = [1, 2, 3, 4, 5, 6];
-
- // This context will wrap
- let context = Context::new(u16::MAX >> 2);
-
- let uuid1 = Uuid::new_v6(Timestamp::from_unix(&context, time, time_fraction), &node);
-
- let time: u64 = 1_496_854_536;
-
- let uuid2 = Uuid::new_v6(Timestamp::from_unix(&context, time, time_fraction), &node);
-
- assert_eq!(uuid1.get_timestamp().unwrap().to_rfc4122().1, 16383);
- assert_eq!(uuid2.get_timestamp().unwrap().to_rfc4122().1, 0);
-
- let time = 1_496_854_535;
-
- let uuid3 = Uuid::new_v6(Timestamp::from_unix(&context, time, time_fraction), &node);
- let uuid4 = Uuid::new_v6(Timestamp::from_unix(&context, time, time_fraction), &node);
-
- assert_eq!(uuid3.get_timestamp().unwrap().counter, 1);
- assert_eq!(uuid4.get_timestamp().unwrap().counter, 2);
- }
}
diff --git a/crates/uuid/src/v7.rs b/crates/uuid/src/v7.rs
index ea8f474..44227b5 100644
--- a/crates/uuid/src/v7.rs
+++ b/crates/uuid/src/v7.rs
@@ -6,13 +6,16 @@
use crate::{rng, std::convert::TryInto, timestamp::Timestamp, Builder, Uuid};
impl Uuid {
- /// Create a new version 7 UUID using the current time value and random bytes.
+ /// Create a new version 7 UUID using the current time value.
///
/// This method is a convenient alternative to [`Uuid::new_v7`] that uses the current system time
- /// as the source timestamp.
+ /// as the source timestamp. All UUIDs generated through this method by the same process are
+ /// guaranteed to be ordered by their creation.
#[cfg(feature = "std")]
pub fn now_v7() -> Self {
- Self::new_v7(Timestamp::now(crate::NoContext))
+ Self::new_v7(Timestamp::now(
+ crate::timestamp::context::shared_context_v7(),
+ ))
}
/// Create a new version 7 UUID using a time value and random bytes.
@@ -41,22 +44,63 @@
/// );
/// ```
///
+ /// A v7 UUID can also be created with a counter to ensure batches of
+ /// UUIDs created together remain sortable:
+ ///
+ /// ```rust
+ /// # use uuid::{Uuid, Timestamp, ContextV7};
+ /// let context = ContextV7::new();
+ /// let uuid1 = Uuid::new_v7(Timestamp::from_unix(&context, 1497624119, 1234));
+ /// let uuid2 = Uuid::new_v7(Timestamp::from_unix(&context, 1497624119, 1234));
+ ///
+ /// assert!(uuid1 < uuid2);
+ /// ```
+ ///
/// # References
///
- /// * [Version 7 UUIDs in Draft RFC: New UUID Formats, Version 4](https://datatracker.ietf.org/doc/html/draft-peabody-dispatch-new-uuid-format-04#section-5.2)
+ /// * [UUID Version 7 in RFC 9562](https://www.ietf.org/rfc/rfc9562.html#section-5.7)
pub fn new_v7(ts: Timestamp) -> Self {
let (secs, nanos) = ts.to_unix();
let millis = (secs * 1000).saturating_add(nanos as u64 / 1_000_000);
- Builder::from_unix_timestamp_millis(millis, &rng::bytes()[..10].try_into().unwrap())
- .into_uuid()
+ let mut counter_and_random = rng::u128();
+
+ let (mut counter, counter_bits) = ts.counter();
+
+ debug_assert!(counter_bits <= 128);
+
+ let mut counter_bits = counter_bits as u32;
+
+ // If the counter intersects the variant field then shift around it.
+ // This ensures that any bits set in the counter that would intersect
+ // the variant are still preserved
+ if counter_bits > 12 {
+ let mask = u128::MAX << (counter_bits - 12);
+
+ counter = (counter & !mask) | ((counter & mask) << 2);
+
+ counter_bits += 2;
+ }
+
+ counter_and_random &= u128::MAX.overflowing_shr(counter_bits).0;
+ counter_and_random |= counter
+ .overflowing_shl(128u32.saturating_sub(counter_bits))
+ .0;
+
+ Builder::from_unix_timestamp_millis(
+ millis,
+ &counter_and_random.to_be_bytes()[..10].try_into().unwrap(),
+ )
+ .into_uuid()
}
}
#[cfg(test)]
mod tests {
use super::*;
- use crate::{std::string::ToString, NoContext, Variant, Version};
+
+ use crate::{std::string::ToString, ClockSequence, NoContext, Variant, Version};
+
#[cfg(all(
target_arch = "wasm32",
target_vendor = "unknown",
@@ -153,4 +197,45 @@
assert_eq!(ts.to_unix(), decoded_ts.to_unix());
}
+
+ #[test]
+ #[cfg_attr(
+ all(
+ target_arch = "wasm32",
+ target_vendor = "unknown",
+ target_os = "unknown"
+ ),
+ wasm_bindgen_test
+ )]
+ fn test_new_max_context() {
+ struct MaxContext;
+
+ #[cfg(test)]
+ impl ClockSequence for MaxContext {
+ type Output = u128;
+
+ fn generate_sequence(&self, _seconds: u64, _nanos: u32) -> Self::Output {
+ u128::MAX
+ }
+
+ fn usable_bits(&self) -> usize {
+ 128
+ }
+ }
+
+ let time: u64 = 1_496_854_535;
+ let time_fraction: u32 = 812_000_000;
+
+ // Ensure we don't overflow here
+ let ts = Timestamp::from_unix(MaxContext, time, time_fraction);
+
+ let uuid = Uuid::new_v7(ts);
+
+ assert_eq!(uuid.get_version(), Some(Version::SortRand));
+ assert_eq!(uuid.get_variant(), Variant::RFC4122);
+
+ let decoded_ts = uuid.get_timestamp().unwrap();
+
+ assert_eq!(ts.to_unix(), decoded_ts.to_unix());
+ }
}
diff --git a/crates/uuid/src/v8.rs b/crates/uuid/src/v8.rs
index b853ac7..dc3d19e 100644
--- a/crates/uuid/src/v8.rs
+++ b/crates/uuid/src/v8.rs
@@ -24,7 +24,7 @@
///
/// # References
///
- /// * [Version 8 UUIDs in Draft RFC: New UUID Formats, Version 4](https://datatracker.ietf.org/doc/html/draft-peabody-dispatch-new-uuid-format-04#section-5.3)
+ /// * [UUID Version 8 in RFC 9562](https://www.ietf.org/rfc/rfc9562.html#section-5.8)
pub fn new_v8(buf: [u8; 16]) -> Uuid {
Builder::from_custom_bytes(buf).into_uuid()
}
diff --git a/pseudo_crate/Cargo.lock b/pseudo_crate/Cargo.lock
index 8b5def0..abea7c7 100644
--- a/pseudo_crate/Cargo.lock
+++ b/pseudo_crate/Cargo.lock
@@ -5792,9 +5792,9 @@
[[package]]
name = "uuid"
-version = "1.7.0"
+version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f00cc9702ca12d3c81455259621e676d0f7251cec66a21e98fe2e9a37db93b2a"
+checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a"
[[package]]
name = "valuable"
diff --git a/pseudo_crate/Cargo.toml b/pseudo_crate/Cargo.toml
index 6484aad..32909d0 100644
--- a/pseudo_crate/Cargo.toml
+++ b/pseudo_crate/Cargo.toml
@@ -372,7 +372,7 @@
userfaultfd = "=0.7.0"
userfaultfd-sys = "=0.5.0"
utf-8 = "=0.7.6"
-uuid = "=1.7.0"
+uuid = "=1.11.0"
vhost = "=0.8.1"
vhost-user-backend = "=0.10.1"
virtio-bindings = "=0.2.2"