winnow/stream/
bstr.rs

1use core::num::NonZeroUsize;
2
3use crate::error::Needed;
4use crate::lib::std::iter::{Cloned, Enumerate};
5use crate::lib::std::slice::Iter;
6use crate::lib::std::{cmp::Ordering, fmt, ops};
7use crate::stream::AsBStr;
8use crate::stream::Checkpoint;
9use crate::stream::Compare;
10use crate::stream::CompareResult;
11use crate::stream::FindSlice;
12use crate::stream::Offset;
13#[cfg(feature = "unstable-recover")]
14#[cfg(feature = "std")]
15use crate::stream::Recover;
16use crate::stream::SliceLen;
17use crate::stream::Stream;
18use crate::stream::StreamIsPartial;
19use 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)]
25pub struct BStr([u8]);
26
27impl 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(bytes.as_ref())
32    }
33
34    #[inline]
35    fn from_bytes(slice: &[u8]) -> &Self {
36        unsafe { crate::lib::std::mem::transmute(slice) }
37    }
38
39    #[inline]
40    fn as_bytes(&self) -> &[u8] {
41        &self.0
42    }
43}
44
45impl SliceLen for &BStr {
46    #[inline(always)]
47    fn slice_len(&self) -> usize {
48        self.len()
49    }
50}
51
52impl<'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")]
153impl<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
171impl 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
188impl 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
195impl<'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(other)
199    }
200}
201
202impl AsBStr for &BStr {
203    #[inline(always)]
204    fn as_bstr(&self) -> &[u8] {
205        (*self).as_bytes()
206    }
207}
208
209impl<'a, T> Compare<T> for &'a BStr
210where
211    &'a [u8]: Compare<T>,
212{
213    #[inline(always)]
214    fn compare(&self, t: T) -> CompareResult {
215        let bytes = (*self).as_bytes();
216        bytes.compare(t)
217    }
218}
219
220impl<'i, S> FindSlice<S> for &'i BStr
221where
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 = (*self).as_bytes();
227        let offset = bytes.find_slice(substr);
228        offset
229    }
230}
231
232impl UpdateSlice for &BStr {
233    #[inline(always)]
234    fn update_slice(self, inner: Self::Slice) -> Self {
235        BStr::new(inner)
236    }
237}
238
239#[cfg(feature = "alloc")]
240impl 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
247impl 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 in self.as_bytes() {
254            let c = *byte as char;
255            write!(f, "{}", c.escape_debug())?;
256        }
257        if !f.alternate() {
258            write!(f, "\"")?;
259        }
260        Ok(())
261    }
262}
263
264impl ops::Deref for BStr {
265    type Target = [u8];
266
267    #[inline]
268    fn deref(&self) -> &[u8] {
269        self.as_bytes()
270    }
271}
272
273impl 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
282impl 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
291impl 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
300impl 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
309impl 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
318impl 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
327impl 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
336impl AsRef<[u8]> for BStr {
337    #[inline]
338    fn as_ref(&self) -> &[u8] {
339        self.as_bytes()
340    }
341}
342
343impl AsRef<BStr> for [u8] {
344    #[inline]
345    fn as_ref(&self) -> &BStr {
346        BStr::new(self)
347    }
348}
349
350impl AsRef<BStr> for str {
351    #[inline]
352    fn as_ref(&self) -> &BStr {
353        BStr::new(self)
354    }
355}
356
357#[cfg(feature = "alloc")]
358impl 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")]
368impl 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
375impl<'a> Default for &'a BStr {
376    fn default() -> &'a BStr {
377        BStr::new(b"")
378    }
379}
380
381impl<'a> From<&'a [u8]> for &'a BStr {
382    #[inline]
383    fn from(s: &'a [u8]) -> &'a BStr {
384        BStr::new(s)
385    }
386}
387
388impl<'a> From<&'a BStr> for &'a [u8] {
389    #[inline]
390    fn from(s: &'a BStr) -> &'a [u8] {
391        BStr::as_bytes(s)
392    }
393}
394
395impl<'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
402impl Eq for BStr {}
403
404impl PartialEq<BStr> for BStr {
405    #[inline]
406    fn eq(&self, other: &BStr) -> bool {
407        self.as_bytes() == other.as_bytes()
408    }
409}
410
411impl_partial_eq!(BStr, [u8]);
412impl_partial_eq!(BStr, &'a [u8]);
413impl_partial_eq!(BStr, str);
414impl_partial_eq!(BStr, &'a str);
415
416impl PartialOrd for BStr {
417    #[inline]
418    fn partial_cmp(&self, other: &BStr) -> Option<Ordering> {
419        Some(self.cmp(other))
420    }
421}
422
423impl Ord for BStr {
424    #[inline]
425    fn cmp(&self, other: &BStr) -> Ordering {
426        Ord::cmp(self.as_bytes(), other.as_bytes())
427    }
428}
429
430impl_partial_ord!(BStr, [u8]);
431impl_partial_ord!(BStr, &'a [u8]);
432impl_partial_ord!(BStr, str);
433impl_partial_ord!(BStr, &'a str);
434
435#[cfg(all(test, feature = "std"))]
436mod 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"))]
447mod 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}