linkme_impl/
hash.rs

1use std::collections::hash_map;
2use std::fmt::{self, Display, Write};
3use std::hash::{Hash, Hasher};
4use syn::Ident;
5
6// 8-character symbol hash consisting of a-zA-Z0-9. We use 8 character because
7// Mach-O section specifiers are restricted to at most 16 characters (see
8// https://github.com/dtolnay/linkme/issues/35) and we leave room for a
9// linkme-specific prefix.
10pub(crate) struct Symbol(u64);
11
12pub(crate) fn hash(ident: &Ident) -> Symbol {
13    let mut hasher = hash_map::DefaultHasher::new();
14    ident.hash(&mut hasher);
15    Symbol(hasher.finish())
16}
17
18impl Display for Symbol {
19    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
20        // log(62^8)/log(2) is 47.6 so we have enough bits in the 64-bit
21        // standard library hash to produce a good distribution over 8 digits
22        // from a 62-character alphabet.
23        let mut remainder = self.0;
24        for _ in 0..8 {
25            let digit = (remainder % 62) as u8;
26            remainder /= 62;
27            formatter.write_char(match digit {
28                0..=25 => b'a' + digit,
29                26..=51 => b'A' + digit - 26,
30                52..=61 => b'0' + digit - 52,
31                _ => unreachable!(),
32            } as char)?;
33        }
34        Ok(())
35    }
36}
37
38#[test]
39fn test_hash() {
40    let ident = Ident::new("EXAMPLE", proc_macro2::Span::call_site());
41    assert_eq!(hash(&ident).to_string(), "0GPSzIoo");
42}