sensitive/
string.rs

1//! Guarded [string](std::string) type
2
3use crate::auxiliary::zero;
4use crate::guard;
5use crate::vec::{InnerVec, Vec};
6
7use std::cmp::{PartialEq, min, max};
8use std::convert::From;
9use std::iter::FromIterator;
10use std::mem::MaybeUninit;
11use std::str::Chars;
12
13use unicode_normalization::UnicodeNormalization;
14use unicode_normalization::char::decompose_canonical;
15
16/// Guarded [string](std::string::String) type
17#[must_use]
18#[derive(Debug, Default)]
19pub struct String(Vec<u8>);
20
21/// Reference to immutably borrowed guarded [`String`]
22#[must_use]
23#[derive(Debug)]
24pub struct Ref<'t>(guard::Ref<'t, InnerVec<u8>>);
25
26/// Reference to mutably borrowed guarded [`String`]
27#[must_use]
28#[derive(Debug)]
29pub struct RefMut<'t>(guard::RefMut<'t, InnerVec<u8>>);
30
31impl String {
32	const CMP_MIN: usize = 32;
33
34	fn eq_slice(a: &InnerVec<u8>, b: &[u8]) -> bool {
35		if a.capacity() == 0 {
36			debug_assert!(a.is_empty());
37			b.is_empty()
38		} else {
39			debug_assert!(a.capacity() >= Self::CMP_MIN);
40
41			b.iter().take(a.capacity()).enumerate().fold(0, |d, (i, e)| {
42				d | unsafe { a.as_ptr().add(i).read() as usize ^ *e as usize }
43			}) | (max(a.len(), b.len()) - min(a.len(), b.len())) == 0
44		}
45	}
46
47	#[inline]
48	pub fn new() -> Self {
49		Self(Vec::new())
50	}
51
52	#[inline]
53	pub fn with_capacity(capacity: usize) -> Self {
54		Self(Vec::with_capacity(capacity))
55	}
56
57	#[inline]
58	pub fn capacity(&self) -> usize {
59		self.0.capacity()
60	}
61
62	#[inline]
63	pub fn len(&self) -> usize {
64		self.0.len()
65	}
66
67	#[inline]
68	pub fn is_empty(&self) -> bool {
69		self.0.is_empty()
70	}
71
72	#[inline]
73	pub fn reserve(&mut self, capacity: usize) {
74		self.0.reserve(capacity);
75	}
76
77	#[inline]
78	pub fn reserve_exact(&mut self, capacity: usize) {
79		self.0.reserve_exact(capacity);
80	}
81
82	#[inline]
83	pub fn borrow(&self) -> Ref<'_> {
84		Ref(self.0.borrow())
85	}
86
87	#[inline]
88	pub fn borrow_mut(&mut self) -> RefMut<'_> {
89		RefMut(self.0.borrow_mut())
90	}
91}
92
93impl FromIterator<char> for String {
94	fn from_iter<I>(into: I) -> Self
95		where I: IntoIterator<Item = char> {
96		let iter = into.into_iter();
97		let (lower, upper) = iter.size_hint();
98		let mut string = Self::with_capacity(upper.unwrap_or(lower));
99
100		{
101			let mut mutable = string.borrow_mut();
102
103			for ch in iter {
104				mutable.push(ch);
105			}
106		}
107
108		string
109	}
110}
111
112impl From<&str> for String {
113	fn from(source: &str) -> Self {
114		let iter = source.nfd();
115		let (lower, upper) = iter.size_hint();
116		let mut string = Self::with_capacity(upper.unwrap_or(lower));
117
118		{
119			let mut mutable = string.borrow_mut();
120
121			for decomp in iter {
122				mutable.reserve(decomp.len_utf8());
123				decomp.encode_utf8(unsafe { MaybeUninit::slice_assume_init_mut(mutable.0.spare_capacity_mut()) });
124				unsafe { mutable.0.set_len(mutable.0.len() + decomp.len_utf8()); }
125			}
126		}
127
128		string
129	}
130}
131
132impl From<std::string::String> for String {
133	fn from(mut source: std::string::String) -> Self {
134		let string = Self::from(source.as_str());
135
136		// Zero out source
137		unsafe { zero(source.as_mut_ptr(), source.len()); }
138
139		string
140	}
141}
142
143impl Ref<'_> {
144	#[must_use] #[inline]
145	pub fn as_bytes(&self) -> &[u8] {
146		self.0.as_slice()
147	}
148
149	#[must_use] #[inline]
150	pub fn as_str(&self) -> &str {
151		unsafe { std::str::from_utf8_unchecked(self.0.as_slice()) }
152	}
153
154	#[inline]
155	pub fn chars(&self) -> Chars<'_> {
156		self.as_str().chars()
157	}
158}
159
160impl PartialEq<Self> for Ref<'_> {
161	#[inline]
162	fn eq(&self, other: &Self) -> bool {
163		String::eq_slice(unsafe { self.0.0.inner() }, unsafe { other.0.0.inner() })
164	}
165}
166
167impl PartialEq<RefMut<'_>> for Ref<'_> {
168	#[inline]
169	fn eq(&self, other: &RefMut<'_>) -> bool {
170		String::eq_slice(unsafe { self.0.0.inner() }, unsafe { other.0.0.inner() })
171	}
172}
173
174impl RefMut<'_> {
175	#[must_use] #[inline]
176	pub fn as_bytes(&self) -> &[u8] {
177		self.0.as_slice()
178	}
179
180	#[must_use] #[inline]
181	pub fn as_str(&self) -> &str {
182		unsafe { std::str::from_utf8_unchecked(self.0.as_slice()) }
183	}
184
185	#[inline]
186	pub fn chars(&self) -> Chars<'_> {
187		self.as_str().chars()
188	}
189
190	#[inline]
191	pub fn reserve(&mut self, capacity: usize) {
192		self.0.reserve(capacity);
193	}
194
195	#[inline]
196	pub fn reserve_exact(&mut self, capacity: usize) {
197		self.0.reserve_exact(capacity);
198	}
199
200	pub fn push(&mut self, ch: char) {
201		decompose_canonical(ch, |decomp| {
202			self.0.0.reserve(decomp.len_utf8());
203			decomp.encode_utf8(unsafe { MaybeUninit::slice_assume_init_mut(self.0.spare_capacity_mut()) });
204			unsafe { self.0.0.set_len(self.0.len() + decomp.len_utf8()); }
205		});
206	}
207
208	pub fn push_str(&mut self, string: &str) {
209		let iter = string.nfd();
210		let (lower, upper) = iter.size_hint();
211
212		self.0.0.reserve(upper.unwrap_or(lower));
213
214		for decomp in iter {
215			self.0.0.reserve(decomp.len_utf8());
216			decomp.encode_utf8(unsafe { MaybeUninit::slice_assume_init_mut(self.0.spare_capacity_mut()) });
217			unsafe { self.0.0.set_len(self.0.len() + decomp.len_utf8()); }
218		}
219	}
220
221	pub fn pop(&mut self) -> Option<char> {
222		let ch = self.chars().next_back()?;
223		unsafe { self.0.0.set_len(self.0.len() - ch.len_utf8()); }
224		unsafe { zero(self.0.as_mut_ptr().add(self.0.len()), ch.len_utf8()); }
225		Some(ch)
226	}
227}
228
229impl PartialEq<Self> for RefMut<'_> {
230	#[inline]
231	fn eq(&self, other: &Self) -> bool {
232		String::eq_slice(unsafe { self.0.0.inner() }, unsafe { other.0.0.inner() })
233	}
234}
235
236impl PartialEq<Ref<'_>> for RefMut<'_> {
237	#[inline]
238	fn eq(&self, other: &Ref<'_>) -> bool {
239		String::eq_slice(unsafe { self.0.0.inner() }, unsafe { other.0.0.inner() })
240	}
241}
242
243#[cfg(test)]
244mod tests {
245	use super::*;
246
247	#[test]
248	fn eq() {
249		assert_eq!(String::from("").borrow(), String::from("").borrow());
250		assert_eq!(String::from("ÄÖÜäöüß").borrow(), String::from("A\u{308}O\u{308}U\u{308}a\u{308}o\u{308}u\u{308}ß").borrow());
251
252		assert_ne!(String::from("empty").borrow(), String::from("").borrow());
253		assert_ne!(String::from("Warum Thunfische das?").borrow(), String::from("Darum Thunfische das!").borrow());
254	}
255
256	#[test]
257	fn pop() {
258		let mut string = String::from("Warum Thunfische das?");
259		let mut immutable = string.borrow_mut();
260
261		assert_eq!(immutable.pop(), Some('?'));
262		assert_eq!(immutable.pop(), Some('s'));
263		assert_eq!(immutable.pop(), Some('a'));
264		assert_eq!(immutable.pop(), Some('d'));
265		assert_eq!(immutable.pop(), Some(' '));
266		assert_eq!(immutable, String::from("Warum Thunfische").borrow());
267	}
268}