1use std::borrow::Cow;
2
3#[derive(Clone, Copy)]
4enum ByteStrInner<'arena, 'src> {
5 Src(&'src str),
6 ArenaStr(&'arena str),
7 ArenaBytes(&'arena [u8]),
8}
9
10#[derive(Clone, Copy)]
17pub struct ByteStr<'arena, 'src>(ByteStrInner<'arena, 'src>);
18
19impl<'arena, 'src> ByteStr<'arena, 'src> {
20 #[inline]
22 pub fn from_source(s: &'src str) -> Self {
23 Self(ByteStrInner::Src(s))
24 }
25
26 #[inline]
28 pub fn from_arena_str(s: &'arena str) -> Self {
29 Self(ByteStrInner::ArenaStr(s))
30 }
31
32 pub fn new_in(arena: &'arena bumpalo::Bump, bytes: &[u8]) -> Self {
34 match std::str::from_utf8(bytes) {
35 Ok(s) => Self(ByteStrInner::ArenaStr(arena.alloc_str(s))),
36 Err(_) => Self(ByteStrInner::ArenaBytes(arena.alloc_slice_copy(bytes))),
37 }
38 }
39
40 #[inline]
42 pub fn as_bytes(&self) -> &[u8] {
43 match self.0 {
44 ByteStrInner::Src(s) | ByteStrInner::ArenaStr(s) => s.as_bytes(),
45 ByteStrInner::ArenaBytes(b) => b,
46 }
47 }
48
49 #[inline]
51 pub fn to_str(&self) -> Option<&str> {
52 match self.0 {
53 ByteStrInner::Src(s) | ByteStrInner::ArenaStr(s) => Some(s),
54 ByteStrInner::ArenaBytes(_) => None,
55 }
56 }
57
58 pub fn to_string_lossy(&self) -> Cow<'_, str> {
60 match self.0 {
61 ByteStrInner::Src(s) | ByteStrInner::ArenaStr(s) => Cow::Borrowed(s),
62 ByteStrInner::ArenaBytes(b) => String::from_utf8_lossy(b),
63 }
64 }
65
66 #[inline]
68 pub(crate) fn into_src_str(self) -> Option<&'src str> {
69 match self.0 {
70 ByteStrInner::Src(s) => Some(s),
71 _ => None,
72 }
73 }
74}
75
76impl<'arena, 'src> std::ops::Deref for ByteStr<'arena, 'src> {
77 type Target = [u8];
78 #[inline]
79 fn deref(&self) -> &[u8] {
80 self.as_bytes()
81 }
82}
83
84impl<'arena, 'src, 'a, 'b> PartialEq<ByteStr<'a, 'b>> for ByteStr<'arena, 'src> {
85 fn eq(&self, other: &ByteStr<'a, 'b>) -> bool {
86 self.as_bytes() == other.as_bytes()
87 }
88}
89
90impl<'arena, 'src> Eq for ByteStr<'arena, 'src> {}
91
92impl<'arena, 'src> PartialEq<str> for ByteStr<'arena, 'src> {
93 fn eq(&self, other: &str) -> bool {
94 self.as_bytes() == other.as_bytes()
95 }
96}
97
98impl<'arena, 'src> PartialEq<&str> for ByteStr<'arena, 'src> {
99 fn eq(&self, other: &&str) -> bool {
100 self.as_bytes() == other.as_bytes()
101 }
102}
103
104impl<'arena, 'src> PartialEq<[u8]> for ByteStr<'arena, 'src> {
105 fn eq(&self, other: &[u8]) -> bool {
106 self.as_bytes() == other
107 }
108}
109
110impl<'arena, 'src> std::hash::Hash for ByteStr<'arena, 'src> {
111 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
112 self.as_bytes().hash(state)
113 }
114}
115
116impl<'arena, 'src> std::fmt::Debug for ByteStr<'arena, 'src> {
117 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
118 match self.to_str() {
119 Some(s) => std::fmt::Debug::fmt(s, f),
120 None => write!(f, "b{:?}", self.as_bytes().escape_ascii().to_string()),
121 }
122 }
123}
124
125#[cfg(feature = "serde")]
126impl<'arena, 'src> serde::Serialize for ByteStr<'arena, 'src> {
127 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
128 serialize_bytes(self.as_bytes(), serializer)
129 }
130}
131
132#[cfg(feature = "serde")]
134pub(crate) fn serialize_bytes<S: serde::Serializer>(
135 bytes: &[u8],
136 serializer: S,
137) -> Result<S::Ok, S::Error> {
138 use serde::ser::SerializeMap;
139 match std::str::from_utf8(bytes) {
140 Ok(s) => serializer.serialize_str(s),
141 Err(_) => {
142 let mut map = serializer.serialize_map(Some(1))?;
143 map.serialize_entry("bytes", bytes)?;
144 map.end()
145 }
146 }
147}