1 | use core::num::NonZeroUsize; |
2 | |
3 | use crate::error::Needed; |
4 | use crate::lib::std::iter::{Cloned, Enumerate}; |
5 | use crate::lib::std::slice::Iter; |
6 | use crate::lib::std::{cmp::Ordering, fmt, ops}; |
7 | use crate::stream::AsBStr; |
8 | use crate::stream::Checkpoint; |
9 | use crate::stream::Compare; |
10 | use crate::stream::CompareResult; |
11 | use crate::stream::FindSlice; |
12 | use crate::stream::Offset; |
13 | #[cfg (feature = "unstable-recover" )] |
14 | #[cfg (feature = "std" )] |
15 | use crate::stream::Recover; |
16 | use crate::stream::SliceLen; |
17 | use crate::stream::Stream; |
18 | use crate::stream::StreamIsPartial; |
19 | use crate::stream::UpdateSlice; |
20 | |
21 | /// Improved `Debug` experience for `&[u8]` UTF-8-ish streams |
22 | #[allow (clippy::derived_hash_with_manual_eq)] |
23 | #[derive (Hash)] |
24 | #[repr (transparent)] |
25 | pub struct BStr([u8]); |
26 | |
27 | impl BStr { |
28 | /// Make a stream out of a byte slice-like. |
29 | #[inline ] |
30 | pub fn new<B: ?Sized + AsRef<[u8]>>(bytes: &B) -> &Self { |
31 | Self::from_bytes(slice:bytes.as_ref()) |
32 | } |
33 | |
34 | #[inline ] |
35 | fn from_bytes(slice: &[u8]) -> &Self { |
36 | unsafe { crate::lib::std::mem::transmute(src:slice) } |
37 | } |
38 | |
39 | #[inline ] |
40 | fn as_bytes(&self) -> &[u8] { |
41 | &self.0 |
42 | } |
43 | } |
44 | |
45 | impl SliceLen for &BStr { |
46 | #[inline (always)] |
47 | fn slice_len(&self) -> usize { |
48 | self.len() |
49 | } |
50 | } |
51 | |
52 | impl<'i> Stream for &'i BStr { |
53 | type Token = u8; |
54 | type Slice = &'i [u8]; |
55 | |
56 | type IterOffsets = Enumerate<Cloned<Iter<'i, u8>>>; |
57 | |
58 | type Checkpoint = Checkpoint<Self, Self>; |
59 | |
60 | #[inline (always)] |
61 | fn iter_offsets(&self) -> Self::IterOffsets { |
62 | self.iter().cloned().enumerate() |
63 | } |
64 | #[inline (always)] |
65 | fn eof_offset(&self) -> usize { |
66 | self.len() |
67 | } |
68 | |
69 | #[inline (always)] |
70 | fn next_token(&mut self) -> Option<Self::Token> { |
71 | if self.is_empty() { |
72 | None |
73 | } else { |
74 | let token = self[0]; |
75 | *self = &self[1..]; |
76 | Some(token) |
77 | } |
78 | } |
79 | |
80 | #[inline (always)] |
81 | fn peek_token(&self) -> Option<Self::Token> { |
82 | if self.is_empty() { |
83 | None |
84 | } else { |
85 | Some(self[0]) |
86 | } |
87 | } |
88 | |
89 | #[inline (always)] |
90 | fn offset_for<P>(&self, predicate: P) -> Option<usize> |
91 | where |
92 | P: Fn(Self::Token) -> bool, |
93 | { |
94 | self.iter().position(|b| predicate(*b)) |
95 | } |
96 | #[inline (always)] |
97 | fn offset_at(&self, tokens: usize) -> Result<usize, Needed> { |
98 | if let Some(needed) = tokens.checked_sub(self.len()).and_then(NonZeroUsize::new) { |
99 | Err(Needed::Size(needed)) |
100 | } else { |
101 | Ok(tokens) |
102 | } |
103 | } |
104 | #[inline (always)] |
105 | fn next_slice(&mut self, offset: usize) -> Self::Slice { |
106 | let (slice, next) = self.0.split_at(offset); |
107 | *self = BStr::from_bytes(next); |
108 | slice |
109 | } |
110 | #[inline (always)] |
111 | unsafe fn next_slice_unchecked(&mut self, offset: usize) -> Self::Slice { |
112 | #[cfg (debug_assertions)] |
113 | self.peek_slice(offset); |
114 | |
115 | // SAFETY: `Stream::next_slice_unchecked` requires `offset` to be in bounds |
116 | let slice = unsafe { self.0.get_unchecked(..offset) }; |
117 | // SAFETY: `Stream::next_slice_unchecked` requires `offset` to be in bounds |
118 | let next = unsafe { self.0.get_unchecked(offset..) }; |
119 | *self = BStr::from_bytes(next); |
120 | slice |
121 | } |
122 | #[inline (always)] |
123 | fn peek_slice(&self, offset: usize) -> Self::Slice { |
124 | &self[..offset] |
125 | } |
126 | #[inline (always)] |
127 | unsafe fn peek_slice_unchecked(&self, offset: usize) -> Self::Slice { |
128 | #[cfg (debug_assertions)] |
129 | self.peek_slice(offset); |
130 | |
131 | // SAFETY: `Stream::next_slice_unchecked` requires `offset` to be in bounds |
132 | let slice = unsafe { self.0.get_unchecked(..offset) }; |
133 | slice |
134 | } |
135 | |
136 | #[inline (always)] |
137 | fn checkpoint(&self) -> Self::Checkpoint { |
138 | Checkpoint::<_, Self>::new(*self) |
139 | } |
140 | #[inline (always)] |
141 | fn reset(&mut self, checkpoint: &Self::Checkpoint) { |
142 | *self = checkpoint.inner; |
143 | } |
144 | |
145 | #[inline (always)] |
146 | fn raw(&self) -> &dyn crate::lib::std::fmt::Debug { |
147 | self |
148 | } |
149 | } |
150 | |
151 | #[cfg (feature = "unstable-recover" )] |
152 | #[cfg (feature = "std" )] |
153 | impl<E> Recover<E> for &BStr { |
154 | #[inline (always)] |
155 | fn record_err( |
156 | &mut self, |
157 | _token_start: &Self::Checkpoint, |
158 | _err_start: &Self::Checkpoint, |
159 | err: E, |
160 | ) -> Result<(), E> { |
161 | Err(err) |
162 | } |
163 | |
164 | /// Report whether the [`Stream`] can save off errors for recovery |
165 | #[inline (always)] |
166 | fn is_recovery_supported() -> bool { |
167 | false |
168 | } |
169 | } |
170 | |
171 | impl StreamIsPartial for &BStr { |
172 | type PartialState = (); |
173 | |
174 | #[inline ] |
175 | fn complete(&mut self) -> Self::PartialState { |
176 | // Already complete |
177 | } |
178 | |
179 | #[inline ] |
180 | fn restore_partial(&mut self, _state: Self::PartialState) {} |
181 | |
182 | #[inline (always)] |
183 | fn is_partial_supported() -> bool { |
184 | false |
185 | } |
186 | } |
187 | |
188 | impl Offset for &BStr { |
189 | #[inline (always)] |
190 | fn offset_from(&self, start: &Self) -> usize { |
191 | self.as_bytes().offset_from(&start.as_bytes()) |
192 | } |
193 | } |
194 | |
195 | impl<'a> Offset<<&'a BStr as Stream>::Checkpoint> for &'a BStr { |
196 | #[inline (always)] |
197 | fn offset_from(&self, other: &<&'a BStr as Stream>::Checkpoint) -> usize { |
198 | self.checkpoint().offset_from(start:other) |
199 | } |
200 | } |
201 | |
202 | impl AsBStr for &BStr { |
203 | #[inline (always)] |
204 | fn as_bstr(&self) -> &[u8] { |
205 | (*self).as_bytes() |
206 | } |
207 | } |
208 | |
209 | impl<'a, T> Compare<T> for &'a BStr |
210 | where |
211 | &'a [u8]: Compare<T>, |
212 | { |
213 | #[inline (always)] |
214 | fn compare(&self, t: T) -> CompareResult { |
215 | let bytes: &[u8] = (*self).as_bytes(); |
216 | bytes.compare(t) |
217 | } |
218 | } |
219 | |
220 | impl<'i, S> FindSlice<S> for &'i BStr |
221 | where |
222 | &'i [u8]: FindSlice<S>, |
223 | { |
224 | #[inline (always)] |
225 | fn find_slice(&self, substr: S) -> Option<crate::lib::std::ops::Range<usize>> { |
226 | let bytes: &[u8] = (*self).as_bytes(); |
227 | let offset: Option> = bytes.find_slice(substr); |
228 | offset |
229 | } |
230 | } |
231 | |
232 | impl UpdateSlice for &BStr { |
233 | #[inline (always)] |
234 | fn update_slice(self, inner: Self::Slice) -> Self { |
235 | BStr::new(bytes:inner) |
236 | } |
237 | } |
238 | |
239 | #[cfg (feature = "alloc" )] |
240 | impl fmt::Display for BStr { |
241 | #[inline ] |
242 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
243 | crate::lib::std::string::String::from_utf8_lossy(self.as_bytes()).fmt(f) |
244 | } |
245 | } |
246 | |
247 | impl fmt::Debug for BStr { |
248 | #[inline ] |
249 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
250 | if !f.alternate() { |
251 | write!(f, " \"" )?; |
252 | } |
253 | for byte: &u8 in self.as_bytes() { |
254 | let c: char = *byte as char; |
255 | write!(f, " {}" , c.escape_debug())?; |
256 | } |
257 | if !f.alternate() { |
258 | write!(f, " \"" )?; |
259 | } |
260 | Ok(()) |
261 | } |
262 | } |
263 | |
264 | impl ops::Deref for BStr { |
265 | type Target = [u8]; |
266 | |
267 | #[inline ] |
268 | fn deref(&self) -> &[u8] { |
269 | self.as_bytes() |
270 | } |
271 | } |
272 | |
273 | impl ops::Index<usize> for BStr { |
274 | type Output = u8; |
275 | |
276 | #[inline ] |
277 | fn index(&self, idx: usize) -> &u8 { |
278 | &self.as_bytes()[idx] |
279 | } |
280 | } |
281 | |
282 | impl ops::Index<ops::RangeFull> for BStr { |
283 | type Output = BStr; |
284 | |
285 | #[inline ] |
286 | fn index(&self, _: ops::RangeFull) -> &BStr { |
287 | self |
288 | } |
289 | } |
290 | |
291 | impl ops::Index<ops::Range<usize>> for BStr { |
292 | type Output = BStr; |
293 | |
294 | #[inline ] |
295 | fn index(&self, r: ops::Range<usize>) -> &BStr { |
296 | BStr::new(&self.as_bytes()[r.start..r.end]) |
297 | } |
298 | } |
299 | |
300 | impl ops::Index<ops::RangeInclusive<usize>> for BStr { |
301 | type Output = BStr; |
302 | |
303 | #[inline ] |
304 | fn index(&self, r: ops::RangeInclusive<usize>) -> &BStr { |
305 | BStr::new(&self.as_bytes()[*r.start()..=*r.end()]) |
306 | } |
307 | } |
308 | |
309 | impl ops::Index<ops::RangeFrom<usize>> for BStr { |
310 | type Output = BStr; |
311 | |
312 | #[inline ] |
313 | fn index(&self, r: ops::RangeFrom<usize>) -> &BStr { |
314 | BStr::new(&self.as_bytes()[r.start..]) |
315 | } |
316 | } |
317 | |
318 | impl ops::Index<ops::RangeTo<usize>> for BStr { |
319 | type Output = BStr; |
320 | |
321 | #[inline ] |
322 | fn index(&self, r: ops::RangeTo<usize>) -> &BStr { |
323 | BStr::new(&self.as_bytes()[..r.end]) |
324 | } |
325 | } |
326 | |
327 | impl ops::Index<ops::RangeToInclusive<usize>> for BStr { |
328 | type Output = BStr; |
329 | |
330 | #[inline ] |
331 | fn index(&self, r: ops::RangeToInclusive<usize>) -> &BStr { |
332 | BStr::new(&self.as_bytes()[..=r.end]) |
333 | } |
334 | } |
335 | |
336 | impl AsRef<[u8]> for BStr { |
337 | #[inline ] |
338 | fn as_ref(&self) -> &[u8] { |
339 | self.as_bytes() |
340 | } |
341 | } |
342 | |
343 | impl AsRef<BStr> for [u8] { |
344 | #[inline ] |
345 | fn as_ref(&self) -> &BStr { |
346 | BStr::new(self) |
347 | } |
348 | } |
349 | |
350 | impl AsRef<BStr> for str { |
351 | #[inline ] |
352 | fn as_ref(&self) -> &BStr { |
353 | BStr::new(self) |
354 | } |
355 | } |
356 | |
357 | #[cfg (feature = "alloc" )] |
358 | impl crate::lib::std::borrow::ToOwned for BStr { |
359 | type Owned = crate::lib::std::vec::Vec<u8>; |
360 | |
361 | #[inline ] |
362 | fn to_owned(&self) -> Self::Owned { |
363 | crate::lib::std::vec::Vec::from(self.as_bytes()) |
364 | } |
365 | } |
366 | |
367 | #[cfg (feature = "alloc" )] |
368 | impl crate::lib::std::borrow::Borrow<BStr> for crate::lib::std::vec::Vec<u8> { |
369 | #[inline ] |
370 | fn borrow(&self) -> &BStr { |
371 | BStr::from_bytes(self.as_slice()) |
372 | } |
373 | } |
374 | |
375 | impl<'a> Default for &'a BStr { |
376 | fn default() -> &'a BStr { |
377 | BStr::new(bytes:b"" ) |
378 | } |
379 | } |
380 | |
381 | impl<'a> From<&'a [u8]> for &'a BStr { |
382 | #[inline ] |
383 | fn from(s: &'a [u8]) -> &'a BStr { |
384 | BStr::new(bytes:s) |
385 | } |
386 | } |
387 | |
388 | impl<'a> From<&'a BStr> for &'a [u8] { |
389 | #[inline ] |
390 | fn from(s: &'a BStr) -> &'a [u8] { |
391 | BStr::as_bytes(self:s) |
392 | } |
393 | } |
394 | |
395 | impl<'a> From<&'a str> for &'a BStr { |
396 | #[inline ] |
397 | fn from(s: &'a str) -> &'a BStr { |
398 | BStr::new(s.as_bytes()) |
399 | } |
400 | } |
401 | |
402 | impl Eq for BStr {} |
403 | |
404 | impl PartialEq<BStr> for BStr { |
405 | #[inline ] |
406 | fn eq(&self, other: &BStr) -> bool { |
407 | self.as_bytes() == other.as_bytes() |
408 | } |
409 | } |
410 | |
411 | impl_partial_eq!(BStr, [u8]); |
412 | impl_partial_eq!(BStr, &'a [u8]); |
413 | impl_partial_eq!(BStr, str); |
414 | impl_partial_eq!(BStr, &'a str); |
415 | |
416 | impl PartialOrd for BStr { |
417 | #[inline ] |
418 | fn partial_cmp(&self, other: &BStr) -> Option<Ordering> { |
419 | Some(self.cmp(other)) |
420 | } |
421 | } |
422 | |
423 | impl Ord for BStr { |
424 | #[inline ] |
425 | fn cmp(&self, other: &BStr) -> Ordering { |
426 | Ord::cmp(self.as_bytes(), other.as_bytes()) |
427 | } |
428 | } |
429 | |
430 | impl_partial_ord!(BStr, [u8]); |
431 | impl_partial_ord!(BStr, &'a [u8]); |
432 | impl_partial_ord!(BStr, str); |
433 | impl_partial_ord!(BStr, &'a str); |
434 | |
435 | #[cfg (all(test, feature = "std" ))] |
436 | mod display { |
437 | use crate::stream::BStr; |
438 | |
439 | #[test ] |
440 | fn clean() { |
441 | assert_eq!(&format!("{}" , BStr::new(b"abc" )), "abc" ); |
442 | assert_eq!(&format!("{}" , BStr::new(b" \xf0\x28\x8c\xbc" )), "�(��" ); |
443 | } |
444 | } |
445 | |
446 | #[cfg (all(test, feature = "std" ))] |
447 | mod debug { |
448 | use crate::stream::BStr; |
449 | |
450 | #[test ] |
451 | fn test_debug() { |
452 | assert_eq!(&format!("{:?}" , BStr::new(b"abc" )), " \"abc \"" ); |
453 | |
454 | assert_eq!( |
455 | " \"\\0 \\0 \\0 ftypisom \\0 \\0 \\u{2} \\0isomiso2avc1mp \"" , |
456 | format!( |
457 | "{:?}" , |
458 | BStr::new(b" \0\0\0 ftypisom \0\0\x02\0isomiso2avc1mp" ) |
459 | ), |
460 | ); |
461 | } |
462 | |
463 | #[test ] |
464 | fn test_pretty_debug() { |
465 | assert_eq!(&format!("{:#?}" , BStr::new(b"abc" )), "abc" ); |
466 | } |
467 | } |
468 | |