kstring/
interned_string.rs1#[repr(transparent)]
15pub struct InternedString {
16 ptr: *const u8,
17}
18
19unsafe impl Sync for InternedString {}
22unsafe impl Send for InternedString {}
23
24impl InternedString {
25 #[inline]
32 pub const unsafe fn new_raw(ptr: *const u8) -> Self {
33 Self { ptr }
34 }
35
36 #[inline]
38 pub const fn as_ptr(&self) -> *const u8 {
39 self.ptr
40 }
41
42 #[inline]
44 pub fn as_c_str(&self) -> &'static core::ffi::CStr {
45 unsafe { core::ffi::CStr::from_ptr(self.ptr as *const core::ffi::c_char) }
48 }
49
50 #[inline]
52 pub fn id(&self) -> u16 {
53 unsafe extern "C" {
54 #[link_name = "__start___fxt_interned_string_table"]
55 static START: InternedString;
56 }
57 let self_ptr = self as *const InternedString;
58 let start_ptr = unsafe { &START as *const InternedString };
59 let diff = unsafe { self_ptr.offset_from(start_ptr) };
62 (diff + 1) as u16
63 }
64}
65
66#[macro_export]
87macro_rules! declare_interned_string {
88 ($var_name:ident, $str_lit:literal) => {
89 #[allow(non_snake_case)]
90 mod $var_name {
91 #[$crate::interned_string_export_name($str_lit)]
92 #[unsafe(link_section = "__fxt_interned_string_table")]
93 #[used]
94 pub static STRING: $crate::interned_string::InternedString = unsafe {
95 $crate::interned_string::InternedString::new_raw(concat!($str_lit, "\0").as_ptr())
98 };
99 }
100
101 pub static $var_name: &$crate::interned_string::InternedString = &$var_name::STRING;
102 };
103
104 ($var_name:ident, $str_lit:literal, extern) => {
105 #[allow(non_snake_case)]
106 mod $var_name {
107 $crate::import_string!(STRING, $str_lit);
108 }
109
110 pub static $var_name: &$crate::interned_string::InternedString =
111 unsafe { &$var_name::STRING };
112 };
113}
114
115#[cfg(test)]
116mod tests {
117 use super::*;
118
119 declare_interned_string!(TEST_STR_1, "hello");
120 declare_interned_string!(TEST_STR_2, "world");
121
122 #[test]
123 fn test_as_ptr() {
124 assert!(!TEST_STR_1.as_ptr().is_null());
125 assert!(!TEST_STR_2.as_ptr().is_null());
126
127 unsafe {
129 let mut ptr = TEST_STR_1.as_ptr();
130 while *ptr != 0 {
131 ptr = ptr.add(1);
132 }
133 assert_eq!(*ptr, 0);
134 }
135 }
136
137 #[test]
138 fn test_as_c_str() {
139 let c_str1 = TEST_STR_1.as_c_str();
140 let c_str2 = TEST_STR_2.as_c_str();
141
142 assert_eq!(c_str1, c"hello");
143 assert_eq!(c_str2, c"world");
144
145 assert_eq!(c_str1.to_str().unwrap(), "hello");
147 assert_eq!(c_str2.to_str().unwrap(), "world");
148 }
149
150 #[cfg(any(target_os = "fuchsia", target_os = "linux"))]
153 #[test]
154 fn test_linker_section_allocation() {
155 unsafe extern "C" {
156 #[link_name = "__start___fxt_interned_string_table"]
157 static START: InternedString;
158 #[link_name = "__stop___fxt_interned_string_table"]
159 static STOP: InternedString;
160 }
161
162 let start_ptr = unsafe { &START as *const InternedString };
163 let stop_ptr = unsafe { &STOP as *const InternedString };
164
165 assert!(start_ptr <= stop_ptr);
167 let diff =
168 (stop_ptr as usize - start_ptr as usize) / core::mem::size_of::<InternedString>();
169 assert!(diff >= 2, "Expected at least 2 entries in the table, found {diff}");
170
171 let p1 = TEST_STR_1 as *const InternedString;
173 let p2 = TEST_STR_2 as *const InternedString;
174
175 assert!(
176 p1 >= start_ptr && p1 < stop_ptr,
177 "TEST_STR_1 pointer {p1:p} is outside bounds [{start_ptr:p}, {stop_ptr:p})"
178 );
179 assert!(
180 p2 >= start_ptr && p2 < stop_ptr,
181 "TEST_STR_2 pointer {p2:p} is outside bounds [{start_ptr:p}, {stop_ptr:p})"
182 );
183 }
184
185 #[test]
186 fn test_id() {
187 assert!(TEST_STR_1.id() > 0);
188 assert!(TEST_STR_2.id() > 0);
189
190 let p1 = TEST_STR_1 as *const InternedString;
191 let p2 = TEST_STR_2 as *const InternedString;
192 let expected_diff = unsafe { p2.offset_from(p1) };
193 let actual_diff = (TEST_STR_2.id() as isize) - (TEST_STR_1.id() as isize);
194 assert_eq!(actual_diff, expected_diff);
195 }
196
197 #[test]
198 fn test_symbol_name_matching() {
199 unsafe extern "C" {
202 #[link_name = "_ZN3fxt8internal21InternedStringStorageIJLc104ELc101ELc108ELc108ELc111EEE15interned_stringE"]
203 static EXPECTED_SYMBOL: InternedString;
204 }
205
206 let p_expected = unsafe { &EXPECTED_SYMBOL as *const InternedString };
207 let p_actual = TEST_STR_1 as *const InternedString;
208 assert_eq!(p_actual, p_expected);
209 }
210}