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
51#[macro_export]
59macro_rules! declare_interned_string {
60 ($var_name:ident, $str_lit:literal) => {
61 #[$crate::interned_string_export_name($str_lit)]
62 #[unsafe(link_section = "__fxt_interned_string_table")]
63 #[used]
64 pub static $var_name: $crate::interned_string::InternedString = unsafe {
65 $crate::interned_string::InternedString::new_raw(concat!($str_lit, "\0").as_ptr())
68 };
69 };
70}
71
72#[cfg(test)]
73mod tests {
74 use super::*;
75
76 declare_interned_string!(TEST_STR_1, "hello");
77 declare_interned_string!(TEST_STR_2, "world");
78
79 #[test]
80 fn test_as_ptr() {
81 assert!(!TEST_STR_1.as_ptr().is_null());
82 assert!(!TEST_STR_2.as_ptr().is_null());
83
84 unsafe {
86 let mut ptr = TEST_STR_1.as_ptr();
87 while *ptr != 0 {
88 ptr = ptr.add(1);
89 }
90 assert_eq!(*ptr, 0);
91 }
92 }
93
94 #[test]
95 fn test_as_c_str() {
96 let c_str1 = TEST_STR_1.as_c_str();
97 let c_str2 = TEST_STR_2.as_c_str();
98
99 assert_eq!(c_str1, c"hello");
100 assert_eq!(c_str2, c"world");
101
102 assert_eq!(c_str1.to_str().unwrap(), "hello");
104 assert_eq!(c_str2.to_str().unwrap(), "world");
105 }
106
107 #[cfg(any(target_os = "fuchsia", target_os = "linux"))]
110 #[test]
111 fn test_linker_section_allocation() {
112 unsafe extern "C" {
113 #[link_name = "__start___fxt_interned_string_table"]
114 static START: InternedString;
115 #[link_name = "__stop___fxt_interned_string_table"]
116 static STOP: InternedString;
117 }
118
119 let start_ptr = unsafe { &START as *const InternedString };
120 let stop_ptr = unsafe { &STOP as *const InternedString };
121
122 assert!(start_ptr <= stop_ptr);
124 let diff =
125 (stop_ptr as usize - start_ptr as usize) / core::mem::size_of::<InternedString>();
126 assert!(diff >= 2, "Expected at least 2 entries in the table, found {diff}");
127
128 let p1 = &TEST_STR_1 as *const InternedString;
130 let p2 = &TEST_STR_2 as *const InternedString;
131
132 assert!(
133 p1 >= start_ptr && p1 < stop_ptr,
134 "TEST_STR_1 pointer {p1:p} is outside bounds [{start_ptr:p}, {stop_ptr:p})"
135 );
136 assert!(
137 p2 >= start_ptr && p2 < stop_ptr,
138 "TEST_STR_2 pointer {p2:p} is outside bounds [{start_ptr:p}, {stop_ptr:p})"
139 );
140 }
141
142 #[test]
143 fn test_symbol_name_matching() {
144 unsafe extern "C" {
147 #[link_name = "_ZN3fxt8internal21InternedStringStorageIJLc104ELc101ELc108ELc108ELc111EEE15interned_stringE"]
148 static EXPECTED_SYMBOL: InternedString;
149 }
150
151 let p_expected = unsafe { &EXPECTED_SYMBOL as *const InternedString };
152 let p_actual = &TEST_STR_1 as *const InternedString;
153 assert_eq!(p_actual, p_expected);
154 }
155}