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 unsafe fn next_slice_unchecked(&mut self, offset: usize) -> Self::Slice {
112 #[cfg(debug_assertions)]
113 self.peek_slice(offset);
114
115 let slice = unsafe { self.0.get_unchecked(..offset) };
117 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 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 #[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 }
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}