summaryrefslogtreecommitdiff
path: root/src/hash.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/hash.rs')
-rw-r--r--src/hash.rs42
1 files changed, 42 insertions, 0 deletions
diff --git a/src/hash.rs b/src/hash.rs
new file mode 100644
index 0000000..b8e5aea
--- /dev/null
+++ b/src/hash.rs
@@ -0,0 +1,42 @@
+use std::collections::hash_map;
+use std::fmt::{self, Display, Write};
+use std::hash::{Hash, Hasher};
+use syn::Ident;
+
+// 8-character symbol hash consisting of a-zA-Z0-9. We use 8 character because
+// Mach-O section specifiers are restricted to at most 16 characters (see
+// https://github.com/dtolnay/linkme/issues/35) and we leave room for a
+// linkme-specific prefix.
+pub(crate) struct Symbol(u64);
+
+pub(crate) fn hash(ident: &Ident) -> Symbol {
+ let mut hasher = hash_map::DefaultHasher::new();
+ ident.hash(&mut hasher);
+ Symbol(hasher.finish())
+}
+
+impl Display for Symbol {
+ fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+ // log(62^8)/log(2) is 47.6 so we have enough bits in the 64-bit
+ // standard library hash to produce a good distribution over 8 digits
+ // from a 62-character alphabet.
+ let mut remainder = self.0;
+ for _ in 0..8 {
+ let digit = (remainder % 62) as u8;
+ remainder /= 62;
+ formatter.write_char(match digit {
+ 0..=25 => b'a' + digit,
+ 26..=51 => b'A' + digit - 26,
+ 52..=61 => b'0' + digit - 52,
+ _ => unreachable!(),
+ } as char)?;
+ }
+ Ok(())
+ }
+}
+
+#[test]
+fn test_hash() {
+ let ident = Ident::new("EXAMPLE", proc_macro2::Span::call_site());
+ assert_eq!(hash(&ident).to_string(), "0GPSzIoo");
+}