Skip to main content

pistonite_cu/str/
zstring.rs

1/// A string that will have its inner buffer zeroed when dropped
2#[derive(Default, Clone)]
3pub struct ZString(String);
4impl ZString {
5    pub const fn new() -> Self {
6        Self(String::new())
7    }
8}
9impl std::fmt::Display for ZString {
10    #[inline(always)]
11    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
12        self.0.fmt(f)
13    }
14}
15impl std::fmt::Debug for ZString {
16    #[inline(always)]
17    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
18        self.0.fmt(f)
19    }
20}
21impl From<String> for ZString {
22    #[inline(always)]
23    fn from(value: String) -> Self {
24        Self(value)
25    }
26}
27impl AsRef<[u8]> for ZString {
28    #[inline(always)]
29    fn as_ref(&self) -> &[u8] {
30        self.0.as_ref()
31    }
32}
33impl AsRef<String> for ZString {
34    #[inline(always)]
35    fn as_ref(&self) -> &String {
36        &self.0
37    }
38}
39impl AsRef<str> for ZString {
40    #[inline(always)]
41    fn as_ref(&self) -> &str {
42        &self.0
43    }
44}
45impl Drop for ZString {
46    #[inline(always)]
47    fn drop(&mut self) {
48        // safety: we are dropped
49        unsafe { do_zero(&mut self.0) }
50    }
51}
52impl std::ops::Deref for ZString {
53    type Target = String;
54    #[inline(always)]
55    fn deref(&self) -> &String {
56        &self.0
57    }
58}
59impl std::ops::DerefMut for ZString {
60    #[inline(always)]
61    fn deref_mut(&mut self) -> &mut Self::Target {
62        &mut self.0
63    }
64}
65impl PartialEq<str> for ZString {
66    #[inline(always)]
67    fn eq(&self, other: &str) -> bool {
68        self.0 == other
69    }
70}
71impl PartialEq<&str> for ZString {
72    #[inline(always)]
73    fn eq(&self, other: &&str) -> bool {
74        self.0 == *other
75    }
76}
77impl PartialEq<String> for ZString {
78    #[inline(always)]
79    fn eq(&self, other: &String) -> bool {
80        &self.0 == other
81    }
82}
83impl PartialEq<&String> for ZString {
84    #[inline(always)]
85    fn eq(&self, other: &&String) -> bool {
86        &self.0 == *other
87    }
88}
89
90/// Write 0's to the internal buffer of the string
91#[inline(always)]
92pub fn zero(s: &mut String) {
93    let mut s = std::mem::take(s);
94    // safety: s is dropped afterwards when going out of scope
95    unsafe { do_zero(&mut s) }
96}
97
98// Safety: the string must be dropped afterwards
99#[allow(clippy::ptr_arg)]
100unsafe fn do_zero(s: &mut String) {
101    // SAFETY: we don't use the string again
102    for c in unsafe { s.as_bytes_mut() } {
103        // SAFETY: c is a valid u8 pointer
104        unsafe { std::ptr::write_volatile(c, 0) };
105    }
106    // ensure other threads see this change
107    std::sync::atomic::fence(std::sync::atomic::Ordering::SeqCst);
108    std::sync::atomic::compiler_fence(std::sync::atomic::Ordering::SeqCst);
109}