1extern crate core;
2
3use crate::long_str::{Class, LongBStr};
4use crate::short_str::ShortBStr;
5use bstr::BStr;
6use std::cmp::Ordering;
7use std::fmt::{Debug, Display, Formatter};
8use std::hash::{Hash, Hasher};
9use std::ops::Deref;
10use std::slice::SliceIndex;
11
12#[derive(Clone, Copy)]
13#[repr(transparent)]
14pub struct GermanBStr<'a>(BStrInner<'a>);
15pub use long_str::StrAllocator;
16
17impl Default for GermanBStr<'_> {
18 fn default() -> Self {
19 Self::new_static(&[])
20 }
21}
22
23#[cfg(feature = "bumpalo")]
24mod bumpalo;
25mod long_str;
26mod short_str;
27
28#[derive(Clone, Copy)]
29#[repr(C)]
30union BStrInner<'a> {
31 head: BStrHead,
32 short: ShortBStr,
33 long: LongBStr<'a>,
34}
35
36#[derive(Clone, Copy, Eq, PartialEq)]
37#[repr(C)]
38struct BStrHead {
39 len: u32,
40 head: [u8; 4],
41}
42
43impl<'a> GermanBStr<'a> {
44 pub fn is_empty(self) -> bool {
45 self.len() == 0
46 }
47
48 pub fn len(self) -> usize {
49 self.head().len as usize
50 }
51
52 fn head_bytes(&self) -> &[u8; 4] {
53 &self.head().head
54 }
55
56 fn is_short(self) -> bool {
57 self.len() <= 12
58 }
59
60 fn head(&self) -> &BStrHead {
61 unsafe { &self.0.head }
62 }
63
64 fn as_long(&self) -> Option<&LongBStr<'a>> {
65 if self.is_short() {
66 return None;
67 };
68 unsafe { Some(&self.0.long) }
69 }
70
71 pub fn slice(self, i: impl SliceIndex<[u8], Output = [u8]>) -> Self {
72 if let Some(long) = self.as_long() {
73 GermanBStr(long.slice(i))
74 } else {
75 GermanBStr(BStrInner {
76 short: ShortBStr::new(&(**self)[i]),
77 })
78 }
79 }
80}
81
82impl Deref for GermanBStr<'_> {
83 type Target = BStr;
84 fn deref(&self) -> &BStr {
85 self.as_ref()
86 }
87}
88
89impl AsRef<[u8]> for GermanBStr<'_> {
90 fn as_ref(&self) -> &[u8] {
91 if self.is_short() {
92 unsafe { &self.0.short }
93 } else {
94 unsafe { &self.0.long }
95 }
96 }
97}
98
99impl AsRef<BStr> for GermanBStr<'_> {
100 fn as_ref(&self) -> &BStr {
101 BStr::new(self)
102 }
103}
104
105impl Display for GermanBStr<'_> {
106 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
107 <BStr as Display>::fmt(&**self, f)
108 }
109}
110
111impl Debug for GermanBStr<'_> {
112 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
113 let variant_label = if self.is_short() {
114 "Short"
115 } else {
116 match unsafe { self.0.long.tag() } {
117 Class::Borrowed => "Borrowed",
118 Class::Static => "Static",
119 }
120 };
121 f.debug_tuple(variant_label)
122 .field(&BStr::new(self))
123 .finish()
124 }
125}
126
127macro_rules! define_constructor {
128 ($name:ident,$ty:ty) => {
129 pub fn $name(data: $ty) -> Self {
130 if data.len() <= 12 {
131 GermanBStr(BStrInner {
132 short: ShortBStr::new(data),
133 })
134 } else {
135 GermanBStr(BStrInner {
136 long: LongBStr::$name(data),
137 })
138 }
139 }
140 };
141}
142
143impl<'a> GermanBStr<'a> {
144 define_constructor!(new_static, &'static [u8]);
145 define_constructor!(new_borrowed, &'a [u8]);
146
147 pub fn reallocate_borrowed<'b>(self, dst: impl StrAllocator<'b>) -> GermanBStr<'b> {
148 unsafe {
149 if self.is_short() {
150 GermanBStr(BStrInner {
151 short: self.0.short,
152 })
153 } else {
154 GermanBStr(BStrInner {
155 long: self.0.long.reallocate_borrowed(dst),
156 })
157 }
158 }
159 }
160}
161
162impl PartialOrd for GermanBStr<'_> {
163 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
164 Some(self.cmp(other))
165 }
166}
167
168impl Ord for GermanBStr<'_> {
169 fn cmp(&self, other: &Self) -> Ordering {
170 self.head_bytes()
171 .cmp(other.head_bytes())
172 .then_with(|| <[u8]>::cmp(self, other))
173 }
174}
175
176impl PartialEq for GermanBStr<'_> {
177 fn eq(&self, other: &Self) -> bool {
178 self.head() == other.head() && <[u8]>::eq(self, &**other)
179 }
180}
181
182impl Eq for GermanBStr<'_> {}
183
184impl Hash for GermanBStr<'_> {
185 fn hash<H: Hasher>(&self, state: &mut H) {
186 self.deref().hash(state)
187 }
188}
189
190#[cfg(feature = "bumpalo")]
191#[test]
192fn construct_eq() {
193 use ::bumpalo::Bump;
194 let static_str = b"abcdefghijklmnopqrstuvwxyzABCD";
195 let bump = Bump::new();
196 let bump2 = Bump::new();
197 let mut strings = Vec::new();
198 let mut strings2 = Vec::new();
199 for l in 0..30 {
200 let s = &static_str[..l];
201 let gb = GermanBStr::new_borrowed(s);
202 let gs = GermanBStr::new_static(s);
203 assert_eq!(gb, gs);
204 assert_eq!(*gb, s);
205 strings.push((gb.reallocate_borrowed(&bump), gs.reallocate_borrowed(&bump)));
206 }
207 for (b, s) in &strings {
208 assert_eq!(b, s);
209 strings2.push((b.reallocate_borrowed(&bump2), s.reallocate_borrowed(&bump2)));
210 }
211 drop(strings);
212 drop(bump);
213 for (b, s) in &strings2 {
214 assert_eq!(b, s);
215 }
216}
217
218#[test]
219fn substring() {
220 let static_str = b"abcdefghijklmnopqrstuvwxyzABCD";
221 for l in 0..20 {
222 let s = GermanBStr::new_static(&static_str[..l]);
223 for start in 0..=l {
224 for end in start..=l {
225 assert_eq!(
226 s.slice(start..end),
227 GermanBStr::new_static(&static_str[start..end])
228 );
229 }
230 }
231 }
232}