1use core::num::NonZeroUsize;
2
3use crate::stream::AsBStr;
4use crate::stream::Checkpoint;
5use crate::stream::Compare;
6use crate::stream::CompareResult;
7use crate::stream::FindSlice;
8use crate::stream::Needed;
9use crate::stream::Offset;
10#[cfg(feature = "unstable-recover")]
11#[cfg(feature = "std")]
12use crate::stream::Recover;
13use crate::stream::SliceLen;
14use crate::stream::Stream;
15use crate::stream::StreamIsPartial;
16use crate::stream::UpdateSlice;
17use core::iter::{Cloned, Enumerate};
18use core::slice::Iter;
19use core::{cmp::Ordering, fmt, ops};
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 { core::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 fn trace(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
146 write!(f, "{self:#?}")
147 }
148}
149
150#[cfg(feature = "unstable-recover")]
151#[cfg(feature = "std")]
152impl<E> Recover<E> for &BStr {
153 #[inline(always)]
154 fn record_err(
155 &mut self,
156 _token_start: &Self::Checkpoint,
157 _err_start: &Self::Checkpoint,
158 err: E,
159 ) -> Result<(), E> {
160 Err(err)
161 }
162
163 #[inline(always)]
165 fn is_recovery_supported() -> bool {
166 false
167 }
168}
169
170impl StreamIsPartial for &BStr {
171 type PartialState = ();
172
173 #[inline]
174 fn complete(&mut self) -> Self::PartialState {
175 }
177
178 #[inline]
179 fn restore_partial(&mut self, _state: Self::PartialState) {}
180
181 #[inline(always)]
182 fn is_partial_supported() -> bool {
183 false
184 }
185}
186
187impl Offset for &BStr {
188 #[inline(always)]
189 fn offset_from(&self, start: &Self) -> usize {
190 self.as_bytes().offset_from(&start.as_bytes())
191 }
192}
193
194impl<'a> Offset<<&'a BStr as Stream>::Checkpoint> for &'a BStr {
195 #[inline(always)]
196 fn offset_from(&self, other: &<&'a BStr as Stream>::Checkpoint) -> usize {
197 self.checkpoint().offset_from(other)
198 }
199}
200
201impl AsBStr for &BStr {
202 #[inline(always)]
203 fn as_bstr(&self) -> &[u8] {
204 (*self).as_bytes()
205 }
206}
207
208impl<'a, T> Compare<T> for &'a BStr
209where
210 &'a [u8]: Compare<T>,
211{
212 #[inline(always)]
213 fn compare(&self, t: T) -> CompareResult {
214 let bytes = (*self).as_bytes();
215 bytes.compare(t)
216 }
217}
218
219impl<'i, S> FindSlice<S> for &'i BStr
220where
221 &'i [u8]: FindSlice<S>,
222{
223 #[inline(always)]
224 fn find_slice(&self, substr: S) -> Option<core::ops::Range<usize>> {
225 let bytes = (*self).as_bytes();
226 let offset = bytes.find_slice(substr);
227 offset
228 }
229}
230
231impl UpdateSlice for &BStr {
232 #[inline(always)]
233 fn update_slice(self, inner: Self::Slice) -> Self {
234 BStr::new(inner)
235 }
236}
237
238#[cfg(feature = "alloc")]
239impl fmt::Display for BStr {
240 #[inline]
241 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
242 alloc::string::String::from_utf8_lossy(self.as_bytes()).fmt(f)
243 }
244}
245
246impl fmt::Debug for BStr {
247 #[inline]
248 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
249 if !f.alternate() {
250 write!(f, "\"")?;
251 }
252 for byte in self.as_bytes() {
253 let c = *byte as char;
254 write!(f, "{}", c.escape_debug())?;
255 }
256 if !f.alternate() {
257 write!(f, "\"")?;
258 }
259 Ok(())
260 }
261}
262
263impl ops::Deref for BStr {
264 type Target = [u8];
265
266 #[inline]
267 fn deref(&self) -> &[u8] {
268 self.as_bytes()
269 }
270}
271
272impl ops::Index<usize> for BStr {
273 type Output = u8;
274
275 #[inline]
276 fn index(&self, idx: usize) -> &u8 {
277 &self.as_bytes()[idx]
278 }
279}
280
281impl ops::Index<ops::RangeFull> for BStr {
282 type Output = BStr;
283
284 #[inline]
285 fn index(&self, _: ops::RangeFull) -> &BStr {
286 self
287 }
288}
289
290impl ops::Index<ops::Range<usize>> for BStr {
291 type Output = BStr;
292
293 #[inline]
294 fn index(&self, r: ops::Range<usize>) -> &BStr {
295 BStr::new(&self.as_bytes()[r.start..r.end])
296 }
297}
298
299impl ops::Index<ops::RangeInclusive<usize>> for BStr {
300 type Output = BStr;
301
302 #[inline]
303 fn index(&self, r: ops::RangeInclusive<usize>) -> &BStr {
304 BStr::new(&self.as_bytes()[*r.start()..=*r.end()])
305 }
306}
307
308impl ops::Index<ops::RangeFrom<usize>> for BStr {
309 type Output = BStr;
310
311 #[inline]
312 fn index(&self, r: ops::RangeFrom<usize>) -> &BStr {
313 BStr::new(&self.as_bytes()[r.start..])
314 }
315}
316
317impl ops::Index<ops::RangeTo<usize>> for BStr {
318 type Output = BStr;
319
320 #[inline]
321 fn index(&self, r: ops::RangeTo<usize>) -> &BStr {
322 BStr::new(&self.as_bytes()[..r.end])
323 }
324}
325
326impl ops::Index<ops::RangeToInclusive<usize>> for BStr {
327 type Output = BStr;
328
329 #[inline]
330 fn index(&self, r: ops::RangeToInclusive<usize>) -> &BStr {
331 BStr::new(&self.as_bytes()[..=r.end])
332 }
333}
334
335impl AsRef<[u8]> for BStr {
336 #[inline]
337 fn as_ref(&self) -> &[u8] {
338 self.as_bytes()
339 }
340}
341
342impl AsRef<BStr> for [u8] {
343 #[inline]
344 fn as_ref(&self) -> &BStr {
345 BStr::new(self)
346 }
347}
348
349impl AsRef<BStr> for str {
350 #[inline]
351 fn as_ref(&self) -> &BStr {
352 BStr::new(self)
353 }
354}
355
356#[cfg(feature = "alloc")]
357impl alloc::borrow::ToOwned for BStr {
358 type Owned = alloc::vec::Vec<u8>;
359
360 #[inline]
361 fn to_owned(&self) -> Self::Owned {
362 alloc::vec::Vec::from(self.as_bytes())
363 }
364}
365
366#[cfg(feature = "alloc")]
367impl core::borrow::Borrow<BStr> for alloc::vec::Vec<u8> {
368 #[inline]
369 fn borrow(&self) -> &BStr {
370 BStr::from_bytes(self.as_slice())
371 }
372}
373
374impl<'a> Default for &'a BStr {
375 fn default() -> &'a BStr {
376 BStr::new(b"")
377 }
378}
379
380impl<'a> From<&'a [u8]> for &'a BStr {
381 #[inline]
382 fn from(s: &'a [u8]) -> &'a BStr {
383 BStr::new(s)
384 }
385}
386
387impl<'a> From<&'a BStr> for &'a [u8] {
388 #[inline]
389 fn from(s: &'a BStr) -> &'a [u8] {
390 BStr::as_bytes(s)
391 }
392}
393
394impl<'a> From<&'a str> for &'a BStr {
395 #[inline]
396 fn from(s: &'a str) -> &'a BStr {
397 BStr::new(s.as_bytes())
398 }
399}
400
401impl Eq for BStr {}
402
403impl PartialEq<BStr> for BStr {
404 #[inline]
405 fn eq(&self, other: &BStr) -> bool {
406 self.as_bytes() == other.as_bytes()
407 }
408}
409
410impl_partial_eq!(BStr, [u8]);
411impl_partial_eq!(BStr, &'a [u8]);
412impl_partial_eq!(BStr, str);
413impl_partial_eq!(BStr, &'a str);
414
415impl PartialOrd for BStr {
416 #[inline]
417 fn partial_cmp(&self, other: &BStr) -> Option<Ordering> {
418 Some(self.cmp(other))
419 }
420}
421
422impl Ord for BStr {
423 #[inline]
424 fn cmp(&self, other: &BStr) -> Ordering {
425 Ord::cmp(self.as_bytes(), other.as_bytes())
426 }
427}
428
429impl_partial_ord!(BStr, [u8]);
430impl_partial_ord!(BStr, &'a [u8]);
431impl_partial_ord!(BStr, str);
432impl_partial_ord!(BStr, &'a str);
433
434#[cfg(test)]
435mod test {
436 use crate::stream::BStr;
437
438 #[test]
439 fn partial_eq_bstr_byte_slice() {
440 let input = b"foo".as_slice();
441 let actual = BStr::new(input);
442 assert!(actual == input);
443 }
444
445 #[test]
446 fn partial_eq_byte_slice_bstr() {
447 let input = b"foo".as_slice();
448 let actual = BStr::new(input);
449 assert!(input == actual);
450 }
451
452 #[test]
453 fn partial_eq_bstr_str() {
454 let input = "foo";
455 let actual = BStr::new(input);
456 assert!(actual == input);
457 }
458
459 #[test]
460 fn partial_eq_str_bstr() {
461 let input = "foo";
462 let actual = BStr::new(input);
463 assert!(input == actual);
464 }
465
466 #[test]
467 fn partial_ord_bstr_byte_slice() {
468 let input = b"foo".as_slice();
469 let actual = BStr::new(input);
470 assert!(actual.partial_cmp(input) == Some(core::cmp::Ordering::Equal));
471 }
472
473 #[test]
474 fn partial_ord_byte_slice_bstr() {
475 let input = b"foo".as_slice();
476 let actual = BStr::new(input);
477 assert!(input.partial_cmp(actual) == Some(core::cmp::Ordering::Equal));
478 }
479
480 #[test]
481 fn partial_ord_bstr_str() {
482 let input = "foo";
483 let actual = BStr::new(input);
484 assert!(actual.partial_cmp(input) == Some(core::cmp::Ordering::Equal));
485 }
486
487 #[test]
488 fn partial_ord_str_bstr() {
489 let input = "foo";
490 let actual = BStr::new(input);
491 assert!(input.partial_cmp(actual) == Some(core::cmp::Ordering::Equal));
492 }
493}
494
495#[cfg(all(test, feature = "std"))]
496mod display {
497 use crate::stream::BStr;
498
499 #[test]
500 fn clean() {
501 assert_eq!(&format!("{}", BStr::new(b"abc")), "abc");
502 assert_eq!(&format!("{}", BStr::new(b"\xf0\x28\x8c\xbc")), "�(��");
503 }
504}
505
506#[cfg(all(test, feature = "std"))]
507mod debug {
508 use crate::stream::BStr;
509 use crate::stream::Stream as _;
510 use snapbox::assert_data_eq;
511 use snapbox::str;
512
513 #[test]
514 fn test_debug() {
515 let input = BStr::new(b"abc");
516 let expected = str![[r#""abc""#]];
517 assert_data_eq!(&format!("{input:?}"), expected);
518
519 let input = BStr::new(b"\0\0\0 ftypisom\0\0\x02\0isomiso2avc1mp");
520 let expected = str![[r#""/0/0/0 ftypisom/0/0/u{2}/0isomiso2avc1mp""#]];
521 assert_data_eq!(&format!("{input:?}"), expected);
522 }
523
524 #[test]
525 fn test_pretty_debug() {
526 let input = BStr::new(b"abc");
527 let expected = str!["abc"];
528 assert_data_eq!(&format!("{input:#?}"), expected);
529 }
530
531 #[test]
532 fn test_trace() {
533 let input = BStr::new(b"abc");
534 let expected = str!["abc"];
535 assert_data_eq!(
536 crate::util::from_fn(|f| input.trace(f)).to_string(),
537 expected
538 );
539 }
540}