//! Stream capability for combinators to parse //! //! Stream types include: //! - `&[u8]` and [`Bytes`] for binary data //! - `&str` (aliased as [`Str`]) and [`BStr`] for UTF-8 data //! - [`LocatingSlice`] can track the location within the original buffer to report //! [spans][crate::Parser::with_span] //! - [`Stateful`] to thread global state through your parsers //! - [`Partial`] can mark an input as partial buffer that is being streamed into //! - [Custom stream types][crate::_topic::stream]
use core::hash::BuildHasher; use core::num::NonZeroUsize;
usecrate::ascii::Caseless as AsciiCaseless; usecrate::error::Needed; usecrate::lib::std::iter::{Cloned, Enumerate}; usecrate::lib::std::slice::Iter; usecrate::lib::std::str::from_utf8; usecrate::lib::std::str::CharIndices; usecrate::lib::std::str::FromStr;
mod bstr; mod bytes; mod locating; mod partial; mod range; #[cfg(feature = "unstable-recover")] #[cfg(feature = "std")] mod recoverable; mod stateful; #[cfg(test)] mod tests; mod token;
/// Abstract method to calculate the input length pubtrait SliceLen { /// Calculates the input length, as indicated by its name, /// and the name of the trait itself fn slice_len(&self) -> usize;
}
/// Core definition for parser input state pubtrait Stream: Offset<<Selfas Stream>::Checkpoint> + crate::lib::std::fmt::Debug { /// The smallest unit being parsed /// /// Example: `u8` for `&[u8]` or `char` for `&str` type Token: crate::lib::std::fmt::Debug; /// Sequence of `Token`s /// /// Example: `&[u8]` for `LocatingSlice<&[u8]>` or `&str` for `LocatingSlice<&str>` type Slice: crate::lib::std::fmt::Debug;
/// Iterate with the offset from the current location type IterOffsets: Iterator<Item = (usize, Self::Token)>;
/// A parse location within the stream type Checkpoint: Offset + Clone + crate::lib::std::fmt::Debug;
/// Iterate with the offset from the current location fn iter_offsets(&self) -> Self::IterOffsets;
/// Returns the offset to the end of the input fn eof_offset(&self) -> usize;
/// Split off the next token from the input fn next_token(&mutself) -> Option<Self::Token>; /// Split off the next token from the input fn peek_token(&self) -> Option<Self::Token>;
/// Finds the offset of the next matching token fn offset_for<P>(&self, predicate: P) -> Option<usize> where
P: Fn(Self::Token) -> bool; /// Get the offset for the number of `tokens` into the stream /// /// This means "0 tokens" will return `0` offset fn offset_at(&self, tokens: usize) -> Result<usize, Needed>; /// Split off a slice of tokens from the input /// /// <div class="warning"> /// /// **Note:** For inputs with variable width tokens, like `&str`'s `char`, `offset` might not correspond /// with the number of tokens. To get a valid offset, use: /// - [`Stream::eof_offset`] /// - [`Stream::iter_offsets`] /// - [`Stream::offset_for`] /// - [`Stream::offset_at`] /// /// </div> /// /// # Panic /// /// This will panic if /// /// * Indexes must be within bounds of the original input; /// * Indexes must uphold invariants of the stream, like for `str` they must lie on UTF-8 /// sequence boundaries. /// fn next_slice(&mutself, offset: usize) -> Self::Slice; /// Split off a slice of tokens from the input /// /// <div class="warning"> /// /// **Note:** For inputs with variable width tokens, like `&str`'s `char`, `offset` might not correspond /// with the number of tokens. To get a valid offset, use: /// - [`Stream::eof_offset`] /// - [`Stream::iter_offsets`] /// - [`Stream::offset_for`] /// - [`Stream::offset_at`] /// /// </div> /// /// # Safety /// /// Callers of this function are responsible that these preconditions are satisfied: /// /// * Indexes must be within bounds of the original input; /// * Indexes must uphold invariants of the stream, like for `str` they must lie on UTF-8 /// sequence boundaries. /// unsafefn next_slice_unchecked(&mutself, offset: usize) -> Self::Slice { // Inherent impl to allow callers to have `unsafe`-free code self.next_slice(offset)
} /// Split off a slice of tokens from the input fn peek_slice(&self, offset: usize) -> Self::Slice; /// Split off a slice of tokens from the input /// /// # Safety /// /// Callers of this function are responsible that these preconditions are satisfied: /// /// * Indexes must be within bounds of the original input; /// * Indexes must uphold invariants of the stream, like for `str` they must lie on UTF-8 /// sequence boundaries. unsafefn peek_slice_unchecked(&self, offset: usize) -> Self::Slice { // Inherent impl to allow callers to have `unsafe`-free code self.peek_slice(offset)
}
/// Advance to the end of the stream #[inline(always)] fn finish(&mutself) -> Self::Slice { self.next_slice(self.eof_offset())
} /// Advance to the end of the stream #[inline(always)] fn peek_finish(&self) -> Self::Slice where Self: Clone,
{ self.peek_slice(self.eof_offset())
}
/// Save the current parse location within the stream fn checkpoint(&self) -> Self::Checkpoint; /// Revert the stream to a prior [`Self::Checkpoint`] /// /// # Panic /// /// May panic if an invalid [`Self::Checkpoint`] is provided fn reset(&mutself, checkpoint: &Self::Checkpoint);
/// Deprecated for callers as of 0.7.10, instead call [`Stream::trace`] #[deprecated(since = "0.7.10", note = "Replaced with `Stream::trace`")] fn raw(&self) -> &dyncrate::lib::std::fmt::Debug;
/// Write out a single-line summary of the current parse location fn trace(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { #![allow(deprecated)]
write!(f, "{:#?}", self.raw())
}
}
impl<'i, T> Stream for &'i [T] where
T: Clone + crate::lib::std::fmt::Debug,
{ type Token = T; type Slice = &'i [T];
type IterOffsets = Enumerate<Cloned<Iter<'i, T>>>;
// SAFETY: `Stream::next_slice_unchecked` requires `offset` to be in bounds and on a UTF-8 // sequence boundary let slice = unsafe { self.get_unchecked(..offset) }; // SAFETY: `Stream::next_slice_unchecked` requires `offset` to be in bounds and on a UTF-8 // sequence boundary let next = unsafe { self.get_unchecked(offset..) };
*self = next;
slice
} #[inline(always)] fn peek_slice(&self, offset: usize) -> Self::Slice {
&self[..offset]
} #[inline(always)] unsafefn peek_slice_unchecked(&self, offset: usize) -> Self::Slice { #[cfg(debug_assertions)] self.peek_slice(offset);
// SAFETY: `Stream::next_slice_unchecked` requires `offset` to be in bounds let slice = unsafe { self.get_unchecked(..offset) };
slice
}
impl<I> Iterator for BitOffsets<I> where
I: Stream<Token = u8> + Clone,
{ type Item = (usize, bool); fn next(&mutself) -> Option<Self::Item> { let b = next_bit(&mutself.i)?; let o = self.o;
self.o += 1;
Some((o, b))
}
}
fn next_bit<I>(i: &mut (I, usize)) -> Option<bool> where
I: Stream<Token = u8> + Clone,
{ if i.eof_offset() == 0 { return None;
} let offset = i.1;
letmut next_i = i.0.clone(); let byte = next_i.next_token()?; let bit = (byte >> offset) & 0x1 == 0x1;
fn peek_bit<I>(i: &(I, usize)) -> Option<bool> where
I: Stream<Token = u8> + Clone,
{ if i.eof_offset() == 0 { return None;
} let offset = i.1;
letmut next_i = i.0.clone(); let byte = next_i.next_token()?; let bit = (byte >> offset) & 0x1 == 0x1;
let next_offset = offset + 1; if next_offset == 8 {
Some(bit)
} else {
Some(bit)
}
}
/// Current parse locations offset /// /// See [`LocatingSlice`] for adding location tracking to your [`Stream`] pubtrait Location { /// Previous token's end offset fn previous_token_end(&self) -> usize; /// Current token's start offset fn current_token_start(&self) -> usize;
}
/// Capture top-level errors in the middle of parsing so parsing can resume /// /// See [`Recoverable`] for adding error recovery tracking to your [`Stream`] #[cfg(feature = "unstable-recover")] #[cfg(feature = "std")] pubtrait Recover<E>: Stream { /// Capture a top-level error /// /// May return `Err(err)` if recovery is not possible (e.g. if [`Recover::is_recovery_supported`] /// returns `false`). fn record_err(
&mutself,
token_start: &Self::Checkpoint,
err_start: &Self::Checkpoint,
err: E,
) -> Result<(), E>;
/// Report whether the [`Stream`] can save off errors for recovery fn is_recovery_supported() -> bool;
}
/// Report whether the [`Stream`] can save off errors for recovery #[inline(always)] fn is_recovery_supported() -> bool { false
}
}
/// Marks the input as being the complete buffer or a partial buffer for streaming input /// /// See [`Partial`] for marking a presumed complete buffer type as a streaming buffer. pubtrait StreamIsPartial: Sized { /// Whether the stream is currently partial or complete type PartialState;
/// Mark the stream is complete #[must_use] fn complete(&mutself) -> Self::PartialState;
/// Restore the stream back to its previous state fn restore_partial(&mutself, state: Self::PartialState);
/// Report whether the [`Stream`] is can ever be incomplete fn is_partial_supported() -> bool;
/// Report whether the [`Stream`] is currently incomplete #[inline(always)] fn is_partial(&self) -> bool { Self::is_partial_supported()
}
}
impl<T> StreamIsPartial for &[T] { type PartialState = ();
/// Useful functions to calculate the offset between slices and show a hexdump of a slice pubtrait Offset<Start = Self> { /// Offset between the first byte of `start` and the first byte of `self`a /// /// <div class="warning"> /// /// **Note:** This is an offset, not an index, and may point to the end of input /// (`start.len()`) when `self` is exhausted. /// /// </div> fn offset_from(&self, start: &Start) -> usize;
}
impl<T> Offset for &[T] { #[inline] fn offset_from(&self, start: &Self) -> usize { let fst = (*start).as_ptr(); let snd = (*self).as_ptr();
debug_assert!(
fst <= snd, "`Offset::offset_from({snd:?}, {fst:?})` only accepts slices of `self`"
);
(snd as usize - fst as usize) / crate::lib::std::mem::size_of::<T>()
}
}
impl<'a, T> Offset<<&'a [T] as Stream>::Checkpoint> for &'a [T] where
T: Clone + crate::lib::std::fmt::Debug,
{ #[inline(always)] fn offset_from(&self, other: &<&'a [T] as Stream>::Checkpoint) -> usize { self.checkpoint().offset_from(other)
}
}
/// Helper trait for types that can be viewed as a byte slice pubtrait AsBytes { /// Casts the input type to a byte slice fn as_bytes(&self) -> &[u8];
}
/// Result of [`Compare::compare`] #[derive(Debug, Eq, PartialEq)] pubenum CompareResult { /// Comparison was successful /// /// `usize` is the end of the successful match within the buffer. /// This is most relevant for caseless UTF-8 where `Compare::compare`'s parameter might be a different /// length than the match within the buffer.
Ok(usize), /// We need more data to be sure
Incomplete, /// Comparison failed
Error,
}
/// Abstracts comparison operations pubtrait Compare<T> { /// Compares self to another value for equality fn compare(&self, t: T) -> CompareResult;
}
impl<'b> Compare<&'b [u8]> for &[u8] { #[inline] fn compare(&self, t: &'b [u8]) -> CompareResult { if t.iter().zip(*self).any(|(a, b)| a != b) {
CompareResult::Error
} elseifself.len() < t.slice_len() {
CompareResult::Incomplete
} else {
CompareResult::Ok(t.slice_len())
}
}
}
/// Look for a slice in self pubtrait FindSlice<T> { /// Returns the offset of the slice if it is found fn find_slice(&self, substr: T) -> Option<crate::lib::std::ops::Range<usize>>;
}
/// Used to integrate `str`'s `parse()` method pubtrait ParseSlice<R> { /// Succeeds if `parse()` succeeded /// /// The byte slice implementation will first convert it to a `&str`, then apply the `parse()` /// function fn parse_slice(&self) -> Option<R>;
}
/// Convert a `Stream` into an appropriate `Output` type pubtrait UpdateSlice: Stream { /// Convert an `Output` type to be used as `Stream` fn update_slice(self, inner: Self::Slice) -> Self;
}
/// Abstracts something which can extend an `Extend`. /// Used to build modified input slices in `escaped_transform` pubtrait Accumulate<T>: Sized { /// Create a new `Extend` of the correct type fn initial(capacity: Option<usize>) -> Self; /// Accumulate the input into an accumulator fn accumulate(&mutself, acc: T);
}
#[cfg(feature = "alloc")] #[inline] pub(crate) fn clamp_capacity<T>(capacity: usize) -> usize { /// Don't pre-allocate more than 64KiB when calling `Vec::with_capacity`. /// /// Pre-allocating memory is a nice optimization but count fields can't /// always be trusted. We should clamp initial capacities to some reasonable /// amount. This reduces the risk of a bogus count value triggering a panic /// due to an OOM error. /// /// This does not affect correctness. `winnow` will always read the full number /// of elements regardless of the capacity cap. const MAX_INITIAL_CAPACITY_BYTES: usize = 65536;
let max_initial_capacity =
MAX_INITIAL_CAPACITY_BYTES / crate::lib::std::mem::size_of::<T>().max(1);
capacity.min(max_initial_capacity)
}
/// Helper trait to convert numbers to usize. /// /// By default, usize implements `From<u8>` and `From<u16>` but not /// `From<u32>` and `From<u64>` because that would be invalid on some /// platforms. This trait implements the conversion for platforms /// with 32 and 64 bits pointer platforms pubtrait ToUsize { /// converts self to usize fn to_usize(&self) -> usize;
}
/// Transforms a token into a char for basic string parsing #[allow(clippy::len_without_is_empty)] #[allow(clippy::wrong_self_convention)] pubtrait AsChar { /// Makes a char from self /// /// # Example /// /// ``` /// use winnow::prelude::*; /// /// assert_eq!('a'.as_char(), 'a'); /// assert_eq!(u8::MAX.as_char(), std::char::from_u32(u8::MAX as u32).unwrap()); /// ``` fn as_char(self) -> char;
/// Tests that self is an alphabetic character /// /// <div class="warning"> /// /// **Warning:** for `&str` it matches alphabetic /// characters outside of the 52 ASCII letters /// /// </div> fn is_alpha(self) -> bool;
/// Tests that self is an alphabetic character /// or a decimal digit fn is_alphanum(self) -> bool; /// Tests that self is a decimal digit fn is_dec_digit(self) -> bool; /// Tests that self is an hex digit fn is_hex_digit(self) -> bool; /// Tests that self is an octal digit fn is_oct_digit(self) -> bool; /// Gets the len in bytes for self fn len(self) -> usize; /// Tests that self is ASCII space or tab fn is_space(self) -> bool; /// Tests if byte is ASCII newline: \n fn is_newline(self) -> bool;
}
/// Check if a token is in a set of possible tokens /// /// While this can be implemented manually, you can also build up sets using: /// - `b'c'` and `'c'` /// - `b""` /// - `|c| true` /// - `b'a'..=b'z'`, `'a'..='z'` (etc for each [range type][std::ops]) /// - `(set1, set2, ...)` /// /// # Example /// /// For example, you could implement `hex_digit0` as: /// ``` /// # use winnow::prelude::*; /// # use winnow::{error::ErrMode, error::ContextError}; /// # use winnow::token::take_while; /// fn hex_digit1<'s>(input: &mut &'s str) -> ModalResult<&'s str, ContextError> { /// take_while(1.., ('a'..='f', 'A'..='F', '0'..='9')).parse_next(input) /// } /// /// assert_eq!(hex_digit1.parse_peek("21cZ"), Ok(("Z", "21c"))); /// assert!(hex_digit1.parse_peek("H2").is_err()); /// assert!(hex_digit1.parse_peek("").is_err()); /// ``` pubtrait ContainsToken<T> { /// Returns true if self contains the token fn contains_token(&self, token: T) -> bool;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.