1use crate::ByteView;
6use std::{ops::Deref, sync::Arc};
7
8#[repr(C)]
18#[derive(Default, Clone, PartialEq, Eq, PartialOrd, Ord)]
19pub struct StrView(ByteView);
20
21#[allow(clippy::non_send_fields_in_send_ty)]
22unsafe impl Send for StrView {}
23#[allow(clippy::non_send_fields_in_send_ty)]
24unsafe impl Sync for StrView {}
25
26impl std::fmt::Display for StrView {
27 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
28 write!(f, "{}", &**self)
29 }
30}
31
32impl std::fmt::Debug for StrView {
33 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
34 write!(f, "{:?}", &**self)
35 }
36}
37
38impl Deref for StrView {
39 type Target = str;
40
41 fn deref(&self) -> &Self::Target {
42 unsafe { std::str::from_utf8_unchecked(&self.0) }
44 }
45}
46
47impl std::hash::Hash for StrView {
48 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
49 self.deref().hash(state);
50 }
51}
52
53impl StrView {
54 #[must_use]
62 pub fn new(s: &str) -> Self {
63 Self(ByteView::new(s.as_bytes()))
64 }
65
66 #[doc(hidden)]
67 #[must_use]
68 #[allow(clippy::missing_const_for_fn)]
69 pub unsafe fn from_raw(view: ByteView) -> Self {
70 Self(view)
71 }
72
73 #[must_use]
75 pub fn to_detached(&self) -> Self {
76 Self::new(self)
77 }
78
79 #[must_use]
81 pub fn slice(&self, range: impl std::ops::RangeBounds<usize>) -> Self {
82 Self(self.0.slice(range))
83 }
84
85 #[must_use]
87 pub fn is_empty(&self) -> bool {
88 self.0.is_empty()
89 }
90
91 #[must_use]
93 pub fn len(&self) -> usize {
94 self.0.len()
95 }
96
97 #[must_use]
99 pub fn starts_with(&self, needle: &str) -> bool {
100 self.0.starts_with(needle.as_bytes())
101 }
102}
103
104impl std::borrow::Borrow<str> for StrView {
105 fn borrow(&self) -> &str {
106 self
107 }
108}
109
110impl AsRef<str> for StrView {
111 fn as_ref(&self) -> &str {
112 self
113 }
114}
115
116impl From<&str> for StrView {
117 fn from(value: &str) -> Self {
118 Self::new(value)
119 }
120}
121
122impl From<String> for StrView {
123 fn from(value: String) -> Self {
124 Self::new(&value)
125 }
126}
127
128impl From<Arc<str>> for StrView {
129 fn from(value: Arc<str>) -> Self {
130 Self::new(&value)
131 }
132}
133
134impl TryFrom<ByteView> for StrView {
135 type Error = std::str::Utf8Error;
136
137 fn try_from(value: ByteView) -> Result<Self, Self::Error> {
138 std::str::from_utf8(&value)?;
139 Ok(Self(value))
140 }
141}
142
143impl From<StrView> for ByteView {
144 fn from(val: StrView) -> Self {
145 val.0
146 }
147}
148
149#[cfg(feature = "serde")]
150mod serde {
151 use super::StrView;
152 use serde::de::{self, Visitor};
153 use serde::{Deserialize, Deserializer, Serialize, Serializer};
154 use std::fmt;
155 use std::ops::Deref;
156
157 impl Serialize for StrView {
158 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
159 where
160 S: Serializer,
161 {
162 serializer.serialize_str(self.deref())
163 }
164 }
165
166 impl<'de> Deserialize<'de> for StrView {
167 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
168 where
169 D: Deserializer<'de>,
170 {
171 struct StrViewVisitor;
172
173 impl<'de> Visitor<'de> for StrViewVisitor {
174 type Value = StrView;
175
176 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
177 formatter.write_str("a string")
178 }
179
180 fn visit_str<E>(self, v: &str) -> Result<StrView, E>
181 where
182 E: de::Error,
183 {
184 Ok(StrView::new(v))
185 }
186 }
187
188 deserializer.deserialize_bytes(StrViewVisitor)
189 }
190 }
191}
192
193#[cfg(test)]
194mod tests {
195 use super::StrView;
196 use std::collections::HashMap;
197
198 #[test]
199 fn strview_hash() {
200 let a = StrView::from("abcdef");
201
202 let mut map = HashMap::new();
203 map.insert(a, 0);
204 assert!(map.contains_key("abcdef"));
205 }
206
207 #[test]
208 fn cmp_misc_1() {
209 let a = StrView::from("abcdef");
210 let b = StrView::from("abcdefhelloworldhelloworld");
211 assert!(a < b);
212 }
213
214 #[test]
215 fn nostr() {
216 let slice = StrView::from("");
217 assert_eq!(0, slice.len());
218 assert_eq!(&*slice, "");
219 }
220
221 #[test]
222 fn default_str() {
223 let slice = StrView::default();
224 assert_eq!(0, slice.len());
225 assert_eq!(&*slice, "");
226 }
227
228 #[test]
229 fn short_str() {
230 let slice = StrView::from("abcdef");
231 assert_eq!(6, slice.len());
232 assert_eq!(&*slice, "abcdef");
233 }
234
235 #[test]
236 #[cfg(target_pointer_width = "64")]
237 fn medium_str() {
238 let slice = StrView::from("abcdefabcdef");
239 assert_eq!(12, slice.len());
240 assert_eq!(&*slice, "abcdefabcdef");
241 }
242
243 #[test]
244 #[cfg(target_pointer_width = "64")]
245 fn medium_long_str() {
246 let slice = StrView::from("abcdefabcdefabcdabcd");
247 assert_eq!(20, slice.len());
248 assert_eq!(&*slice, "abcdefabcdefabcdabcd");
249 }
250
251 #[test]
252 #[cfg(target_pointer_width = "64")]
253 fn medium_str_clone() {
254 let slice = StrView::from("abcdefabcdefabcdefa");
255
256 #[allow(clippy::redundant_clone)]
257 let copy = slice.clone();
258
259 assert_eq!(slice, copy);
260 }
261
262 #[test]
263 fn long_str() {
264 let slice = StrView::from("abcdefabcdefabcdefababcd");
265 assert_eq!(24, slice.len());
266 assert_eq!(&*slice, "abcdefabcdefabcdefababcd");
267 }
268
269 #[test]
270 fn long_str_clone() {
271 let slice = StrView::from("abcdefabcdefabcdefababcd");
272
273 #[allow(clippy::redundant_clone)]
274 let copy = slice.clone();
275
276 assert_eq!(slice, copy);
277 }
278
279 #[test]
280 fn long_str_slice_full() {
281 let slice = StrView::from("helloworld_thisisalongstring");
282
283 let copy = slice.slice(..);
284 assert_eq!(copy, slice);
285 }
286
287 #[test]
288 #[cfg(target_pointer_width = "64")]
289 fn long_str_slice() {
290 let slice = StrView::from("helloworld_thisisalongstring");
291
292 let copy = slice.slice(11..);
293 assert_eq!("thisisalongstring", &*copy);
294 }
295
296 #[test]
297 #[cfg(target_pointer_width = "64")]
298 fn long_str_slice_twice() {
299 let slice = StrView::from("helloworld_thisisalongstring");
300
301 let copy = slice.slice(11..);
302 assert_eq!("thisisalongstring", &*copy);
303
304 let copycopy = copy.slice(..);
305 assert_eq!(copy, copycopy);
306 }
307
308 #[test]
309 #[cfg(target_pointer_width = "64")]
310 fn long_str_slice_downgrade() {
311 let slice = StrView::from("helloworld_thisisalongstring");
312
313 let copy = slice.slice(11..);
314 assert_eq!("thisisalongstring", &*copy);
315
316 let copycopy = copy.slice(0..4);
317 assert_eq!("this", &*copycopy);
318
319 {
320 let copycopy = copy.slice(0..=4);
321 assert_eq!("thisi", &*copycopy);
322 assert_eq!('t', copycopy.chars().next().unwrap());
323 }
324 }
325
326 #[test]
327 fn short_str_clone() {
328 let slice = StrView::from("abcdef");
329 let copy = slice.clone();
330 assert_eq!(slice, copy);
331
332 drop(slice);
333 assert_eq!(&*copy, "abcdef");
334 }
335
336 #[test]
337 fn short_str_slice_full() {
338 let slice = StrView::from("abcdef");
339 let copy = slice.slice(..);
340 assert_eq!(slice, copy);
341
342 drop(slice);
343 assert_eq!(&*copy, "abcdef");
344 }
345
346 #[test]
347 fn short_str_slice_part() {
348 let slice = StrView::from("abcdef");
349 let copy = slice.slice(3..);
350
351 drop(slice);
352 assert_eq!(&*copy, "def");
353 }
354
355 #[test]
356 fn short_str_slice_empty() {
357 let slice = StrView::from("abcdef");
358 let copy = slice.slice(0..0);
359
360 drop(slice);
361 assert_eq!(&*copy, "");
362 }
363
364 #[test]
365 fn tiny_str_starts_with() {
366 let a = StrView::from("abc");
367 assert!(a.starts_with("ab"));
368 assert!(!a.starts_with("b"));
369 }
370
371 #[test]
372 fn long_str_starts_with() {
373 let a = StrView::from("abcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdef");
374 assert!(a.starts_with("abcdef"));
375 assert!(!a.starts_with("def"));
376 }
377
378 #[test]
379 fn tiny_str_cmp() {
380 let a = StrView::from("abc");
381 let b = StrView::from("def");
382 assert!(a < b);
383 }
384
385 #[test]
386 fn tiny_str_eq() {
387 let a = StrView::from("abc");
388 let b = StrView::from("def");
389 assert!(a != b);
390 }
391
392 #[test]
393 fn long_str_eq() {
394 let a = StrView::from("abcdefabcdefabcdefabcdef");
395 let b = StrView::from("xycdefabcdefabcdefabcdef");
396 assert!(a != b);
397 }
398
399 #[test]
400 fn long_str_cmp() {
401 let a = StrView::from("abcdefabcdefabcdefabcdef");
402 let b = StrView::from("xycdefabcdefabcdefabcdef");
403 assert!(a < b);
404 }
405
406 #[test]
407 fn long_str_eq_2() {
408 let a = StrView::from("abcdefabcdefabcdefabcdef");
409 let b = StrView::from("abcdefabcdefabcdefabcdef");
410 assert!(a == b);
411 }
412
413 #[test]
414 fn long_str_cmp_2() {
415 let a = StrView::from("abcdefabcdefabcdefabcdef");
416 let b = StrView::from("abcdefabcdefabcdefabcdeg");
417 assert!(a < b);
418 }
419
420 #[test]
421 fn long_str_cmp_3() {
422 let a = StrView::from("abcdefabcdefabcdefabcde");
423 let b = StrView::from("abcdefabcdefabcdefabcdef");
424 assert!(a < b);
425 }
426}