Skip to main content

kstring/
interned_string.rs

1// Copyright 2026 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5/// A binary-stable, transparent representation of `fxt::InternedString`.
6///
7/// This structure has the exact same memory layout as the C++ `fxt::InternedString`
8/// (which is a single `const char*` pointer pointing to a null-terminated string).
9///
10/// Placing instances of this structure in the special linker section `__fxt_interned_string_table`
11/// allows them to sit contiguous alongside C++'s own interned strings in the global
12/// string table segment, meaning their trace-ID calculations (`this - section_begin`)
13/// are safe, exact, and valid.
14#[repr(transparent)]
15pub struct InternedString {
16    ptr: *const u8,
17}
18
19// SAFETY: InternedString points to a static, read-only null-terminated string byte array.
20// It is completely thread-safe to share and access across threads.
21unsafe impl Sync for InternedString {}
22unsafe impl Send for InternedString {}
23
24impl InternedString {
25    /// Creates a new `InternedString` from a raw pointer.
26    ///
27    /// # Safety
28    ///
29    /// The caller must ensure that the pointer points to a null-terminated string
30    /// with static storage duration ('static).
31    #[inline]
32    pub const unsafe fn new_raw(ptr: *const u8) -> Self {
33        Self { ptr }
34    }
35
36    /// Returns the raw pointer to the null-terminated string.
37    #[inline]
38    pub const fn as_ptr(&self) -> *const u8 {
39        self.ptr
40    }
41
42    /// Returns the static name as a safe, null-terminated C string reference.
43    #[inline]
44    pub fn as_c_str(&self) -> &'static core::ffi::CStr {
45        // SAFETY: The safety invariants of the raw constructor guarantee that the pointer
46        // points to a valid, null-terminated static byte string in read-only memory.
47        unsafe { core::ffi::CStr::from_ptr(self.ptr as *const core::ffi::c_char) }
48    }
49}
50
51/// Statically declares a new `InternedString` and allocates it inside the special
52/// `__fxt_interned_string_table` linker section.
53///
54/// # Example
55/// ```rust
56/// declare_interned_string!(MY_STRING, "hello.world");
57/// ```
58#[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            // Append a null byte to the string literal at compile time to satisfy
66            // the C++ const char* expectations.
67            $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        // Verify null termination!
85        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        // Verify conversion to standard Rust &str
103        assert_eq!(c_str1.to_str().unwrap(), "hello");
104        assert_eq!(c_str2.to_str().unwrap(), "world");
105    }
106
107    // This linker section bound test is only valid on ELF targets (Fuchsia/Linux)
108    // where the linker generates __start and __stop symbols for orphan sections.
109    #[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        // Ensure the boundary is valid and holds our entries
123        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        // Verify our static variables reside strictly within the linker bounds
129        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        // "hello" should mangle to the exact C++ symbol name:
145        // _ZN3fxt8internal21InternedStringStorageIJLc104ELc101ELc108ELc108ELc111EEE15interned_stringE
146        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}