shape/helpers.rs
1use std::collections::hash_map::DefaultHasher;
2use std::collections::hash_map::RandomState;
3use std::hash::BuildHasher;
4use std::sync::LazyLock;
5
6use super::Shape;
7
8/// Per-process random seed used by [`inner_hasher`] to construct hashers for
9/// the order-insensitive XOR-combiners in `hashing.rs` and `merge.rs`. The
10/// `RandomState` is initialized once on first use and reused for every
11/// subsequent inner hasher within the process, so equal shapes hash to equal
12/// values within a single run (preserving the `a == b ⇒ hash(a) == hash(b)`
13/// contract) while randomizing across processes (preventing offline
14/// precomputation of element-hash collisions).
15static INNER_HASHER_SEED: LazyLock<RandomState> = LazyLock::new(RandomState::new);
16
17/// Build a [`DefaultHasher`] seeded from the per-process [`INNER_HASHER_SEED`]
18/// random key. Used as the inner hasher inside the order-insensitive
19/// XOR-combiners that hash `Object`/`One`/`All` and `MergeSet` contents. See
20/// the module-level doc on `crate::hashing` for the protection this provides.
21pub(crate) fn inner_hasher() -> DefaultHasher {
22 INNER_HASHER_SEED.build_hasher()
23}
24
25/// [`Ref<T>`] is a placeholder for whichever reference counting wrapper type we
26/// want to define here ([`std::rc::Rc`] also works).
27///
28/// Besides allowing for cheap cloning and sharing of references to [`Shape`]
29/// subtrees, reference counting also paves the way for structural sharing of
30/// canonical shapes, which could have profound performance benefits.
31///
32/// Finally, Rust will complain about the [`Shape`] type referring to itself
33/// without indirection unless we introduce a wrapper type like [`Ref<T>`] to
34/// provide the indirection.
35pub(crate) type Ref<T> = std::sync::Arc<T>;
36pub(crate) type RefWeak<T> = std::sync::Weak<T>;
37
38/// Since we're using [`std::sync::Arc`] for reference counting, and [`Shape`]
39/// is an immutable structure, we can safely implement [`Send`] and [`Sync`] for
40/// [`Shape`].
41unsafe impl Sync for Shape {}
42unsafe impl Send for Shape {}
43
44/// Quote a string with double quotes and appropriate JSON escaping.
45pub(super) fn quote_string(s: &str) -> String {
46 serde_json_bytes::Value::String(s.into()).to_string()
47}
48
49pub(super) fn quote_non_identifier(s: &str) -> String {
50 if s.chars().all(|c| c.is_ascii_alphanumeric() || c == '_') {
51 s.to_string()
52 } else {
53 quote_string(s)
54 }
55}
56
57/// A [start..end) range of byte offsets within some source document.
58pub type OffsetRange = Option<std::ops::Range<usize>>;