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#[allow(clippy::derived_hash_with_manual_eq)]
23#[derive(Hash)]
24#[repr(transparent)]
25pub struct BStr([u8]);
26
27impl BStr {
28 #[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 fn peek_slice(&self, offset: usize) -> Self::Slice {
112 let (slice, _next) = self.split_at(offset);
113 slice
114 }
115
116 #[inline(always)]
117 fn checkpoint(&self) -> Self::Checkpoint {
118 Checkpoint::<_, Self>::new(*self)
119 }
120 #[inline(always)]
121 fn reset(&mut self, checkpoint: &Self::Checkpoint) {
122 *self = checkpoint.inner;
123 }
124
125 #[inline(always)]
126 fn raw(&self) -> &dyn crate::lib::std::fmt::Debug {
127 self
128 }
129}
130
131#[cfg(feature = "unstable-recover")]
132#[cfg(feature = "std")]
133impl<E> Recover<E> for &BStr {
134 #[inline(always)]
135 fn record_err(
136 &mut self,
137 _token_start: &Self::Checkpoint,
138 _err_start: &Self::Checkpoint,
139 err: E,
140 ) -> Result<(), E> {
141 Err(err)
142 }
143
144 #[inline(always)]
146 fn is_recovery_supported() -> bool {
147 false
148 }
149}
150
151impl StreamIsPartial for &BStr {
152 type PartialState = ();
153
154 #[inline]
155 fn complete(&mut self) -> Self::PartialState {
156 }
158
159 #[inline]
160 fn restore_partial(&mut self, _state: Self::PartialState) {}
161
162 #[inline(always)]
163 fn is_partial_supported() -> bool {
164 false
165 }
166}
167
168impl Offset for &BStr {
169 #[inline(always)]
170 fn offset_from(&self, start: &Self) -> usize {
171 self.as_bytes().offset_from(&start.as_bytes())
172 }
173}
174
175impl<'a> Offset<<&'a BStr as Stream>::Checkpoint> for &'a BStr {
176 #[inline(always)]
177 fn offset_from(&self, other: &<&'a BStr as Stream>::Checkpoint) -> usize {
178 self.checkpoint().offset_from(other)
179 }
180}
181
182impl AsBStr for &BStr {
183 #[inline(always)]
184 fn as_bstr(&self) -> &[u8] {
185 (*self).as_bytes()
186 }
187}
188
189impl<'a, T> Compare<T> for &'a BStr
190where
191 &'a [u8]: Compare<T>,
192{
193 #[inline(always)]
194 fn compare(&self, t: T) -> CompareResult {
195 let bytes = (*self).as_bytes();
196 bytes.compare(t)
197 }
198}
199
200impl<'i, S> FindSlice<S> for &'i BStr
201where
202 &'i [u8]: FindSlice<S>,
203{
204 #[inline(always)]
205 fn find_slice(&self, substr: S) -> Option<crate::lib::std::ops::Range<usize>> {
206 let bytes = (*self).as_bytes();
207 let offset = bytes.find_slice(substr);
208 offset
209 }
210}
211
212impl UpdateSlice for &BStr {
213 #[inline(always)]
214 fn update_slice(self, inner: Self::Slice) -> Self {
215 BStr::new(inner)
216 }
217}
218
219#[cfg(feature = "alloc")]
220impl fmt::Display for BStr {
221 #[inline]
222 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
223 crate::lib::std::string::String::from_utf8_lossy(self.as_bytes()).fmt(f)
224 }
225}
226
227impl fmt::Debug for BStr {
228 #[inline]
229 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
230 if !f.alternate() {
231 write!(f, "\"")?;
232 }
233 for byte in self.as_bytes() {
234 let c = *byte as char;
235 write!(f, "{}", c.escape_debug())?;
236 }
237 if !f.alternate() {
238 write!(f, "\"")?;
239 }
240 Ok(())
241 }
242}
243
244impl ops::Deref for BStr {
245 type Target = [u8];
246
247 #[inline]
248 fn deref(&self) -> &[u8] {
249 self.as_bytes()
250 }
251}
252
253impl ops::Index<usize> for BStr {
254 type Output = u8;
255
256 #[inline]
257 fn index(&self, idx: usize) -> &u8 {
258 &self.as_bytes()[idx]
259 }
260}
261
262impl ops::Index<ops::RangeFull> for BStr {
263 type Output = BStr;
264
265 #[inline]
266 fn index(&self, _: ops::RangeFull) -> &BStr {
267 self
268 }
269}
270
271impl ops::Index<ops::Range<usize>> for BStr {
272 type Output = BStr;
273
274 #[inline]
275 fn index(&self, r: ops::Range<usize>) -> &BStr {
276 BStr::new(&self.as_bytes()[r.start..r.end])
277 }
278}
279
280impl ops::Index<ops::RangeInclusive<usize>> for BStr {
281 type Output = BStr;
282
283 #[inline]
284 fn index(&self, r: ops::RangeInclusive<usize>) -> &BStr {
285 BStr::new(&self.as_bytes()[*r.start()..=*r.end()])
286 }
287}
288
289impl ops::Index<ops::RangeFrom<usize>> for BStr {
290 type Output = BStr;
291
292 #[inline]
293 fn index(&self, r: ops::RangeFrom<usize>) -> &BStr {
294 BStr::new(&self.as_bytes()[r.start..])
295 }
296}
297
298impl ops::Index<ops::RangeTo<usize>> for BStr {
299 type Output = BStr;
300
301 #[inline]
302 fn index(&self, r: ops::RangeTo<usize>) -> &BStr {
303 BStr::new(&self.as_bytes()[..r.end])
304 }
305}
306
307impl ops::Index<ops::RangeToInclusive<usize>> for BStr {
308 type Output = BStr;
309
310 #[inline]
311 fn index(&self, r: ops::RangeToInclusive<usize>) -> &BStr {
312 BStr::new(&self.as_bytes()[..=r.end])
313 }
314}
315
316impl AsRef<[u8]> for BStr {
317 #[inline]
318 fn as_ref(&self) -> &[u8] {
319 self.as_bytes()
320 }
321}
322
323impl AsRef<BStr> for [u8] {
324 #[inline]
325 fn as_ref(&self) -> &BStr {
326 BStr::new(self)
327 }
328}
329
330impl AsRef<BStr> for str {
331 #[inline]
332 fn as_ref(&self) -> &BStr {
333 BStr::new(self)
334 }
335}
336
337#[cfg(feature = "alloc")]
338impl crate::lib::std::borrow::ToOwned for BStr {
339 type Owned = crate::lib::std::vec::Vec<u8>;
340
341 #[inline]
342 fn to_owned(&self) -> Self::Owned {
343 crate::lib::std::vec::Vec::from(self.as_bytes())
344 }
345}
346
347#[cfg(feature = "alloc")]
348impl crate::lib::std::borrow::Borrow<BStr> for crate::lib::std::vec::Vec<u8> {
349 #[inline]
350 fn borrow(&self) -> &BStr {
351 BStr::from_bytes(self.as_slice())
352 }
353}
354
355impl<'a> Default for &'a BStr {
356 fn default() -> &'a BStr {
357 BStr::new(b"")
358 }
359}
360
361impl<'a> From<&'a [u8]> for &'a BStr {
362 #[inline]
363 fn from(s: &'a [u8]) -> &'a BStr {
364 BStr::new(s)
365 }
366}
367
368impl<'a> From<&'a BStr> for &'a [u8] {
369 #[inline]
370 fn from(s: &'a BStr) -> &'a [u8] {
371 BStr::as_bytes(s)
372 }
373}
374
375impl<'a> From<&'a str> for &'a BStr {
376 #[inline]
377 fn from(s: &'a str) -> &'a BStr {
378 BStr::new(s.as_bytes())
379 }
380}
381
382impl Eq for BStr {}
383
384impl PartialEq<BStr> for BStr {
385 #[inline]
386 fn eq(&self, other: &BStr) -> bool {
387 self.as_bytes() == other.as_bytes()
388 }
389}
390
391impl_partial_eq!(BStr, [u8]);
392impl_partial_eq!(BStr, &'a [u8]);
393impl_partial_eq!(BStr, str);
394impl_partial_eq!(BStr, &'a str);
395
396impl PartialOrd for BStr {
397 #[inline]
398 fn partial_cmp(&self, other: &BStr) -> Option<Ordering> {
399 Some(self.cmp(other))
400 }
401}
402
403impl Ord for BStr {
404 #[inline]
405 fn cmp(&self, other: &BStr) -> Ordering {
406 Ord::cmp(self.as_bytes(), other.as_bytes())
407 }
408}
409
410impl_partial_ord!(BStr, [u8]);
411impl_partial_ord!(BStr, &'a [u8]);
412impl_partial_ord!(BStr, str);
413impl_partial_ord!(BStr, &'a str);
414
415#[cfg(all(test, feature = "std"))]
416mod display {
417 use crate::stream::BStr;
418
419 #[test]
420 fn clean() {
421 assert_eq!(&format!("{}", BStr::new(b"abc")), "abc");
422 assert_eq!(&format!("{}", BStr::new(b"\xf0\x28\x8c\xbc")), "�(��");
423 }
424}
425
426#[cfg(all(test, feature = "std"))]
427mod debug {
428 use crate::stream::BStr;
429
430 #[test]
431 fn test_debug() {
432 assert_eq!(&format!("{:?}", BStr::new(b"abc")), "\"abc\"");
433
434 assert_eq!(
435 "\"\\0\\0\\0 ftypisom\\0\\0\\u{2}\\0isomiso2avc1mp\"",
436 format!(
437 "{:?}",
438 BStr::new(b"\0\0\0 ftypisom\0\0\x02\0isomiso2avc1mp")
439 ),
440 );
441 }
442
443 #[test]
444 fn test_pretty_debug() {
445 assert_eq!(&format!("{:#?}", BStr::new(b"abc")), "abc");
446 }
447}