diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bd01ad7..978a1d0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,7 +3,7 @@ name: CI on: [push, pull_request] env: - RUST_MINVERSION: 1.48.0 + RUST_MINVERSION: 1.63.0 jobs: test: @@ -16,7 +16,7 @@ jobs: - stable - beta - nightly - - 1.48.0 + - 1.63.0 features: - '' diff --git a/Cargo.toml b/Cargo.toml index a7ea3f9..cc3f7d2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,6 +11,7 @@ readme = "README.md" repository = "https://github.com/fflorent/nom_locate" version = "4.1.0" edition = "2018" +rust-version = "1.63.0" [badges.travis-ci] repository = "fflorent/nom_locate" diff --git a/src/lib.rs b/src/lib.rs index 66a19f4..2dd6643 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -118,6 +118,50 @@ use nom::{ #[cfg(feature = "stable-deref-trait")] use stable_deref_trait::StableDeref; +/// Trait of types whose implementation of [`AsBytes`], if any, returns slices that +/// can be dereferenced with a negative offset (which is usually not allowed in Rust). +/// +/// Because the satefy of these implementations must be checked for every implementation, +/// `nom_locate` does not provide blanket implementations for any trait, but only for +/// concrete types. +/// +/// # Safety +/// +/// Implementations of `WellBehavedFragment` must uphold one invariant: each instance +/// `fragment` of the type has an `offset` property, and the fragment type satisfies +/// these assertions: +/// +/// * an instance's `offset` is constant for its lifetime (ie. it cannot change due +/// to interior mutability or global/external state) +/// * `offset` is nonnegative (ie. zero or greater; it is usually zero when passed +/// to [`LocatedSpan::new`]) +/// * if the type implements [`AsBytes`] then [`AsBytes::as_bytes`] must return a slice +/// whose underlying `*const u8` can be decremented by any number smaller or equal +/// to the `offset` and dereferenced safely. (ie. they are an offset in a larger +/// contiguous bye array) +/// * if the type implements [`Offset`], then the value returned by [`Offset::offset`] +/// must be equal to its `offset` (technically, they may safely return a value greater +/// than their `offset`, but it is unlikely to be correct, and may change in future +/// versions of `nom_locate`) +/// * if the type implements [`Slice`], then the new instance returned by [`Slice::slice`] +/// must have an `offset` equal to the original `offset` plus the `start` of the range +/// argument (ditto) +pub unsafe trait RewindableFragment {} + +unsafe impl RewindableFragment for [u8] {} +unsafe impl<'a> RewindableFragment for &'a [u8] {} + +unsafe impl RewindableFragment for [u8; N] {} +unsafe impl<'a, const N: usize> RewindableFragment for &'a [u8; N] {} + +unsafe impl RewindableFragment for str {} +unsafe impl<'a> RewindableFragment for &'a str {} + +#[cfg(any(feature = "std", feature = "alloc"))] +unsafe impl RewindableFragment for Vec {} +#[cfg(any(feature = "std", feature = "alloc"))] +unsafe impl RewindableFragment for String {} + /// A LocatedSpan is a set of meta information about the location of a token, including extra /// information. /// @@ -323,7 +367,7 @@ impl LocatedSpan { } } -impl LocatedSpan { +impl LocatedSpan { // Attempt to get the "original" data slice back, by extending // self.fragment backwards by self.offset. // Note that any bytes truncated from after self.fragment will not @@ -660,7 +704,7 @@ macro_rules! impl_slice_ranges { impl<'a, T, R, X: Clone> Slice for LocatedSpan where - T: Slice + Offset + AsBytes + Slice>, + T: Slice + Offset + AsBytes + Slice> + RewindableFragment, { fn slice(&self, range: R) -> Self { let next_fragment = self.fragment.slice(range); diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs index 74dd0db..45aafbd 100644 --- a/tests/integration_tests.rs +++ b/tests/integration_tests.rs @@ -1,5 +1,5 @@ use nom::{error::ErrorKind, error_position, AsBytes, FindSubstring, IResult, InputLength, Slice}; -use nom_locate::LocatedSpan; +use nom_locate::{LocatedSpan, RewindableFragment}; use std::cmp; use std::fmt::Debug; use std::ops::{Range, RangeFull}; @@ -59,7 +59,13 @@ struct Position { fn test_str_fragments<'a, F, T>(parser: F, input: T, positions: Vec) where F: Fn(LocatedSpan) -> IResult, Vec>>, - T: InputLength + Slice> + Slice + Debug + PartialEq + AsBytes, + T: InputLength + + Slice> + + Slice + + Debug + + PartialEq + + AsBytes + + RewindableFragment, { let res = parser(LocatedSpan::new(input.slice(..))) .map_err(|err| {