allocative/
key.rs

1/*
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 *
4 * This source code is licensed under both the MIT license found in the
5 * LICENSE-MIT file in the root directory of this source tree and the Apache
6 * License, Version 2.0 found in the LICENSE-APACHE file in the root directory
7 * of this source tree.
8 */
9
10use std::any;
11use std::cmp::Ordering;
12use std::hash::Hash;
13use std::hash::Hasher;
14use std::ops::Deref;
15
16/// Hashed string, which is a key while descending into a tree (e.g. type name or field name).
17#[derive(Clone, Eq, PartialEq, Debug)]
18pub struct Key {
19    hash: u64,
20    s: &'static str,
21}
22
23impl PartialOrd for Key {
24    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
25        Some(self.cmp(other))
26    }
27}
28
29impl Ord for Key {
30    fn cmp(&self, other: &Self) -> Ordering {
31        self.s.cmp(other.s)
32    }
33}
34
35#[allow(clippy::derived_hash_with_manual_eq)]
36impl Hash for Key {
37    fn hash<H: Hasher>(&self, state: &mut H) {
38        self.hash.hash(state);
39    }
40}
41
42impl Deref for Key {
43    type Target = str;
44
45    fn deref(&self) -> &str {
46        self.s
47    }
48}
49
50impl Key {
51    /// Must be identical to `allocative_derive::hash`.
52    const fn hash(s: &str) -> u64 {
53        let mut hash = 0xcbf29ce484222325;
54        let mut i = 0;
55        while i < s.as_bytes().len() {
56            let b = s.as_bytes()[i];
57            hash ^= b as u64;
58            hash = hash.wrapping_mul(0x100000001b3);
59            i += 1;
60        }
61        hash
62    }
63
64    /// Compute hash.
65    pub const fn new(s: &'static str) -> Key {
66        let hash = Self::hash(s);
67        Key::new_unchecked(hash, s)
68    }
69
70    pub const fn new_unchecked(hash: u64, s: &'static str) -> Key {
71        Key { hash, s }
72    }
73
74    pub fn for_type_name<T: ?Sized>() -> Key {
75        // Compute hash at compile time.
76        #[cfg(rust_nightly)]
77        return Key {
78            hash: AllocativeKeyForType::<T>::KEY.hash,
79            s: AllocativeKeyForType::<T>::KEY.s,
80        };
81        // Hope optimizer folds this to constant (it doesn't for long type names).
82        #[cfg(not(rust_nightly))]
83        return Key::new(any::type_name::<T>());
84    }
85}
86
87#[cfg(rust_nightly)]
88struct AllocativeKeyForType<T: ?Sized>(std::marker::PhantomData<fn(&T)>);
89
90#[cfg(rust_nightly)]
91impl<T: ?Sized> AllocativeKeyForType<T> {
92    /// Force compute it at compile time. Const fn does not guarantee that.
93    pub const KEY: Key = Key::new(any::type_name::<T>());
94}