/* This Source Code Form is subject to the terms of the Mozilla Public *License,v.2.0.IfacopyoftheMPLwasnotdistributedwiththis
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#![allow(unknown_lints)] #![warn(rust_2018_idioms)] // (It's tempting to avoid the utf8 checks, but they're easy to get wrong, so) #![deny(unsafe_code)] #[cfg(feature = "serde_support")] mod serde_support;
#[cfg(feature = "rusqlite_support")] mod rusqlite_support;
use std::{
cmp::Ordering,
fmt,
hash::{Hash, Hasher},
ops, str,
};
#[cfg(feature = "random")] use base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine};
/// This is a type intended to be used to represent the guids used by sync. It /// has several benefits over using a `String`: /// /// 1. It's more explicit about what is being stored, and could prevent bugs /// where a Guid is passed to a function expecting text. /// /// 2. Guids are guaranteed to be immutable. /// /// 3. It's optimized for the guids commonly used by sync. In particular, short guids /// (including the guids which would meet `PlacesUtils.isValidGuid`) do not incur /// any heap allocation, and are stored inline. #[derive(Clone)] pubstruct Guid(Repr);
// The internal representation of a GUID. Most Sync GUIDs are 12 bytes, // and contain only base64url characters; we can store them on the stack // without a heap allocation. However, arbitrary ascii guids of up to length 64 // are possible, in which case we fall back to a heap-allocated string. // // This is separate only because making `Guid` an enum would expose the // internals. #[derive(Clone)] enum Repr { // see FastGuid for invariants
Fast(FastGuid),
/// Invariants: /// /// - `len <= MAX_FAST_GUID_LEN`. /// - `data[0..len]` encodes valid utf8. /// - `data[len..].iter().all(|&b| b == b'\0')` /// /// Note: None of these are required for memory safety, just correctness. #[derive(Clone)] struct FastGuid {
len: u8,
data: [u8; MAX_FAST_GUID_LEN],
}
// This is the maximum length (experimentally determined) we can make it before // `Repr::Fast` is larger than `Guid::Slow` on 32 bit systems. The important // thing is really that it's not too big, and is above 12 bytes. const MAX_FAST_GUID_LEN: usize = 14;
impl FastGuid { #[inline] fn from_slice(bytes: &[u8]) -> Self { // Checked by the caller, so debug_assert is fine.
debug_assert!(
can_use_fast(bytes), "Bug: Caller failed to check can_use_fast: {:?}",
bytes
); letmut data = [0u8; MAX_FAST_GUID_LEN];
data[0..bytes.len()].copy_from_slice(bytes);
FastGuid {
len: bytes.len() as u8,
data,
}
}
#[inline] fn as_str(&self) -> &str { // Note: we only use debug_assert! to ensure valid utf8-ness, so this need
str::from_utf8(self.bytes()).expect("Invalid fast guid bytes!")
}
#[inline] fn len(&self) -> usize { self.len as usize
}
// Returns: // - true to use Repr::Fast // - false to use Repr::Slow #[inline] fn can_use_fast<T: ?Sized + AsRef<[u8]>>(bytes: &T) -> bool { let bytes = bytes.as_ref(); // This is fine as a debug_assert since we'll still panic if it's ever used // in such a way where it would matter.
debug_assert!(str::from_utf8(bytes).is_ok());
bytes.len() <= MAX_FAST_GUID_LEN
}
impl Guid { /// Create a guid from a `str`. #[inline] pubfn new(s: &str) -> Self {
Guid::from_slice(s.as_ref())
}
/// Create an empty guid. Usable as a constant. #[inline] pubconstfn empty() -> Self {
Guid(Repr::Fast(FastGuid {
len: 0,
data: [0u8; MAX_FAST_GUID_LEN],
}))
}
/// Create a random guid (of 12 base64url characters). Requires the `random` /// feature. #[cfg(feature = "random")] pubfn random() -> Self { let bytes: [u8; 9] = rand::random();
// Note: only first 12 bytes are used, but remaining are required to // build the FastGuid letmut output = [0u8; MAX_FAST_GUID_LEN];
let bytes_written = URL_SAFE_NO_PAD
.encode_slice(bytes, &mut output[..12])
.expect("Output buffer too small");
/// Convert `b` into a `Guid`. #[inline] pubfn from_string(s: String) -> Self {
Guid::from_vec(s.into_bytes())
}
/// Convert `b` into a `Guid`. #[inline] pubfn from_slice(b: &[u8]) -> Self { if can_use_fast(b) {
Guid(Repr::Fast(FastGuid::from_slice(b)))
} else {
Guid::new_slow(b.into())
}
}
/// Convert `v` to a `Guid`, consuming it. #[inline] pubfn from_vec(v: Vec<u8>) -> Self { if can_use_fast(&v) {
Guid(Repr::Fast(FastGuid::from_slice(&v)))
} else {
Guid::new_slow(v)
}
}
/// Get the data backing this `Guid` as a `&[u8]`. #[inline] pubfn as_bytes(&self) -> &[u8] { match &self.0 {
Repr::Fast(rep) => rep.bytes(),
Repr::Slow(rep) => rep.as_ref(),
}
}
/// Get the data backing this `Guid` as a `&str`. #[inline] pubfn as_str(&self) -> &str { match &self.0 {
Repr::Fast(rep) => rep.as_str(),
Repr::Slow(rep) => rep.as_ref(),
}
}
/// Convert this `Guid` into a `String`, consuming it in the process. #[inline] pubfn into_string(self) -> String { matchself.0 {
Repr::Fast(rep) => rep.as_str().into(),
Repr::Slow(rep) => rep,
}
}
/// Returns true for Guids that are valid places guids, and false for all others. pubfn is_valid_for_places(&self) -> bool { self.len() == 12 && self.bytes().all(Guid::is_valid_places_byte)
}
/// Returns true if the byte `b` is a valid base64url byte. #[inline] pubfn is_valid_places_byte(b: u8) -> bool {
BASE64URL_BYTES[b as usize] == 1
}
#[cold] fn new_slow(v: Vec<u8>) -> Self {
assert!(
!can_use_fast(&v), "Could use fast for guid (len = {})",
v.len()
);
Guid(Repr::Slow(
String::from_utf8(v).expect("Invalid slow guid bytes!"),
))
}
}
impl std::default::Default for Guid { /// Create a default guid by calling `Guid::empty()` #[inline] fn default() -> Self {
Guid::empty()
}
}
macro_rules! impl_guid_eq {
($($other: ty),+) => {$( // This macro is used for items with and without lifetimes. #[allow(clippy::extra_unused_lifetimes)] impl<'a> PartialEq<$other> for Guid { #[inline] fn eq(&self, other: &$other) -> bool {
PartialEq::eq(AsRef::<[u8]>::as_ref(self), AsRef::<[u8]>::as_ref(other))
}
}
// Implement direct comparison with some common types from the stdlib.
impl_guid_eq![str, &'a str, String, [u8], &'a [u8], Vec<u8>];
#[cfg(test)] mod test { usesuper::*;
#[test] fn test_base64url_bytes() { letmut expect = [0u8; 256]; for b in b'0'..=b'9' {
expect[b as usize] = 1;
} for b in b'a'..=b'z' {
expect[b as usize] = 1;
} for b in b'A'..=b'Z' {
expect[b as usize] = 1;
}
expect[b'_'as usize] = 1;
expect[b'-'as usize] = 1;
assert_eq!(&BASE64URL_BYTES[..], &expect[..]);
}
#[test] fn test_valid_for_places() {
assert!(Guid::from("aaaabbbbcccc").is_valid_for_places());
assert!(Guid::from_slice(b"09_az-AZ_09-").is_valid_for_places());
assert!(!Guid::from("aaaabbbbccccd").is_valid_for_places()); // too long
assert!(!Guid::from("aaaabbbbccc").is_valid_for_places()); // too short
assert!(!Guid::from("aaaabbbbccc=").is_valid_for_places()); // right length, bad character
assert!(!Guid::empty().is_valid_for_places()); // empty isn't valid to insert.
}
assert_eq!(Guid::from("abcdabcdabcd"), &b"abcdabcdabcd"[..]); // b"abcdabcdabcd" has type &[u8; 12]...
assert_ne!(Guid::from(&b"abcdabcdabcd"[..]), &b"ABCDabcdabcd"[..]);
// order by data instead of length // hrmph - clippy in 1.54-nightly complains about the below: // 'error: this creates an owned instance just for comparison' // '... help: try: `*"aaaaaa"`' // and suggests a change that's wrong - so we've ignored the lint above.
assert!(Guid::from("zzz") > Guid::from("aaaaaa"));
assert!(Guid::from("ThisIsASolowGuid") < Guid::from("zzz"));
assert!(Guid::from("ThisIsASolowGuid") > Guid::from("AnotherSlowGuid"));
}
#[cfg(feature = "random")] #[test] fn test_random() { use std::collections::HashSet; // Used to verify uniqueness within our sample of 1000. Could cause // random failures, but desktop has the same test, and it's never caused // a problem AFAIK. letmut seen: HashSet<String> = HashSet::new(); for _ in0..1000 { let g = Guid::random();
assert_eq!(g.len(), 12);
assert!(g.is_valid_for_places()); let decoded = URL_SAFE_NO_PAD.decode(&g).unwrap();
assert_eq!(decoded.len(), 9); let no_collision = seen.insert(g.clone().into_string());
assert!(no_collision, "{}", g);
}
}
}
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.17 Sekunden
(vorverarbeitet am 2026-06-18)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.