Skip to content

Commit 7aab4e3

Browse files
authored
Merge pull request #44 from dtolnay/hash
Shorten symbol names for Mach-O by hashing
2 parents d76eeb4 + ac70867 commit 7aab4e3

File tree

3 files changed

+53
-6
lines changed

3 files changed

+53
-6
lines changed

impl/src/hash.rs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
use std::collections::hash_map;
2+
use std::fmt::{self, Display, Write};
3+
use std::hash::{Hash, Hasher};
4+
use 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.
10+
pub(crate) struct Symbol(u64);
11+
12+
pub(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+
18+
impl 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]
39+
fn test_hash() {
40+
let ident = Ident::new("EXAMPLE", proc_macro2::Span::call_site());
41+
assert_eq!(hash(&ident).to_string(), "0GPSzIoo");
42+
}

impl/src/lib.rs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
#![allow(clippy::needless_pass_by_value, clippy::too_many_lines)]
1+
#![allow(
2+
clippy::cast_possible_truncation,
3+
clippy::needless_pass_by_value,
4+
clippy::too_many_lines
5+
)]
26

37
extern crate proc_macro;
48

@@ -7,13 +11,14 @@ mod attr;
711
mod declaration;
812
mod derive;
913
mod element;
14+
mod hash;
1015
mod linker;
1116

17+
use crate::args::Args;
18+
use crate::hash::hash;
1219
use proc_macro::TokenStream;
1320
use syn::parse_macro_input;
1421

15-
use crate::args::Args;
16-
1722
#[proc_macro_attribute]
1823
pub fn distributed_slice(args: TokenStream, input: TokenStream) -> TokenStream {
1924
let args = parse_macro_input!(args as Args);

impl/src/linker.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,15 @@ pub mod macos {
1818
use syn::Ident;
1919

2020
pub fn section(ident: &Ident) -> String {
21-
format!("__DATA,__{}", ident)
21+
format!("__DATA,__linkme{}", crate::hash(ident))
2222
}
2323

2424
pub fn section_start(ident: &Ident) -> String {
25-
format!("\x01section$start$__DATA$__{}", ident)
25+
format!("\x01section$start$__DATA$__linkme{}", crate::hash(ident))
2626
}
2727

2828
pub fn section_stop(ident: &Ident) -> String {
29-
format!("\x01section$end$__DATA$__{}", ident)
29+
format!("\x01section$end$__DATA$__linkme{}", crate::hash(ident))
3030
}
3131
}
3232

0 commit comments

Comments
 (0)