1use bytes::Bytes;
2
3use std::{ops, str};
4
5#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
6pub(crate) struct ByteStr {
7 // Invariant: bytes contains valid UTF-8
8 bytes: Bytes,
9}
10
11impl ByteStr {
12 #[inline]
13 pub fn new() -> ByteStr {
14 ByteStr {
15 // Invariant: the empty slice is trivially valid UTF-8.
16 bytes: Bytes::new(),
17 }
18 }
19
20 #[inline]
21 pub const fn from_static(val: &'static str) -> ByteStr {
22 ByteStr {
23 // Invariant: val is a str so contains valid UTF-8.
24 bytes: Bytes::from_static(val.as_bytes()),
25 }
26 }
27
28 #[inline]
29 /// ## Panics
30 /// In a debug build this will panic if `bytes` is not valid UTF-8.
31 ///
32 /// ## Safety
33 /// `bytes` must contain valid UTF-8. In a release build it is undefined
34 /// behavior to call this with `bytes` that is not valid UTF-8.
35 pub unsafe fn from_utf8_unchecked(bytes: Bytes) -> ByteStr {
36 if cfg!(debug_assertions) {
37 match str::from_utf8(&bytes) {
38 Ok(_) => (),
39 Err(err) => panic!(
40 "ByteStr::from_utf8_unchecked() with invalid bytes; error = {}, bytes = {:?}",
41 err, bytes
42 ),
43 }
44 }
45 // Invariant: assumed by the safety requirements of this function.
46 ByteStr { bytes }
47 }
48
49 pub(crate) fn from_utf8(bytes: Bytes) -> Result<ByteStr, std::str::Utf8Error> {
50 str::from_utf8(&bytes)?;
51 // Invariant: just checked is utf8
52 Ok(ByteStr { bytes })
53 }
54}
55
56impl ops::Deref for ByteStr {
57 type Target = str;
58
59 #[inline]
60 fn deref(&self) -> &str {
61 let b: &[u8] = self.bytes.as_ref();
62 // Safety: the invariant of `bytes` is that it contains valid UTF-8.
63 unsafe { str::from_utf8_unchecked(b) }
64 }
65}
66
67impl From<String> for ByteStr {
68 #[inline]
69 fn from(src: String) -> ByteStr {
70 ByteStr {
71 // Invariant: src is a String so contains valid UTF-8.
72 bytes: Bytes::from(src),
73 }
74 }
75}
76
77impl<'a> From<&'a str> for ByteStr {
78 #[inline]
79 fn from(src: &'a str) -> ByteStr {
80 ByteStr {
81 // Invariant: src is a str so contains valid UTF-8.
82 bytes: Bytes::copy_from_slice(data:src.as_bytes()),
83 }
84 }
85}
86
87impl From<ByteStr> for Bytes {
88 fn from(src: ByteStr) -> Self {
89 src.bytes
90 }
91}
92