build(ariadne): vendor serde crates
This commit was merged in pull request #15.
This commit is contained in:
1 parent
90f46026f0
commit
64773e0948
490 files changed
+175103
No files matched your search
@@ -0,0 +1,216 @@
|
||||
use alloc::{boxed::Box, vec, vec::Vec};
|
||||
|
||||
/// A set of "packed pair" test seeds. Each seed serves as the base for the
|
||||
/// generation of many other tests. In essence, the seed captures the pair of
|
||||
/// bytes we used for a predicate and first byte among our needle. The tests
|
||||
/// generated from each seed essentially vary the length of the needle and
|
||||
/// haystack, while using the rare/first byte configuration from the seed.
|
||||
///
|
||||
/// The purpose of this is to test many different needle/haystack lengths.
|
||||
/// In particular, some of the vector optimizations might only have bugs
|
||||
/// in haystacks of a certain size.
|
||||
const SEEDS: &[Seed] = &[
|
||||
// Why not use different 'first' bytes? It seemed like a good idea to be
|
||||
// able to configure it, but when I wrote the test generator below, it
|
||||
// didn't seem necessary to use for reasons that I forget.
|
||||
Seed { first: b'x', index1: b'y', index2: b'z' },
|
||||
Seed { first: b'x', index1: b'x', index2: b'z' },
|
||||
Seed { first: b'x', index1: b'y', index2: b'x' },
|
||||
Seed { first: b'x', index1: b'x', index2: b'x' },
|
||||
Seed { first: b'x', index1: b'y', index2: b'y' },
|
||||
];
|
||||
|
||||
/// Runs a host of "packed pair" search tests.
|
||||
///
|
||||
/// These tests specifically look for the occurrence of a possible substring
|
||||
/// match based on a pair of bytes matching at the right offsets.
|
||||
pub(crate) struct Runner {
|
||||
fwd: Option<
|
||||
Box<
|
||||
dyn FnMut(&[u8], &[u8], u8, u8) -> Option<Option<usize>> + 'static,
|
||||
>,
|
||||
>,
|
||||
}
|
||||
|
||||
impl Runner {
|
||||
/// Create a new test runner for "packed pair" substring search.
|
||||
pub(crate) fn new() -> Runner {
|
||||
Runner { fwd: None }
|
||||
}
|
||||
|
||||
/// Run all tests. This panics on the first failure.
|
||||
///
|
||||
/// If the implementation being tested returns `None` for a particular
|
||||
/// haystack/needle combination, then that test is skipped.
|
||||
///
|
||||
/// This runs tests on both the forward and reverse implementations given.
|
||||
/// If either (or both) are missing, then tests for that implementation are
|
||||
/// skipped.
|
||||
pub(crate) fn run(self) {
|
||||
if let Some(mut fwd) = self.fwd {
|
||||
for seed in SEEDS.iter() {
|
||||
for t in seed.generate() {
|
||||
match fwd(&t.haystack, &t.needle, t.index1, t.index2) {
|
||||
None => continue,
|
||||
Some(result) => {
|
||||
assert_eq!(
|
||||
t.fwd, result,
|
||||
"FORWARD, needle: {:?}, haystack: {:?}, \
|
||||
index1: {:?}, index2: {:?}",
|
||||
t.needle, t.haystack, t.index1, t.index2,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the implementation for forward "packed pair" substring search.
|
||||
///
|
||||
/// If the closure returns `None`, then it is assumed that the given
|
||||
/// test cannot be applied to the particular implementation and it is
|
||||
/// skipped. For example, if a particular implementation only supports
|
||||
/// needles or haystacks for some minimum length.
|
||||
///
|
||||
/// If this is not set, then forward "packed pair" search is not tested.
|
||||
pub(crate) fn fwd(
|
||||
mut self,
|
||||
search: impl FnMut(&[u8], &[u8], u8, u8) -> Option<Option<usize>> + 'static,
|
||||
) -> Runner {
|
||||
self.fwd = Some(Box::new(search));
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// A test that represents the input and expected output to a "packed pair"
|
||||
/// search function. The test should be able to run with any "packed pair"
|
||||
/// implementation and get the expected output.
|
||||
struct Test {
|
||||
haystack: Vec<u8>,
|
||||
needle: Vec<u8>,
|
||||
index1: u8,
|
||||
index2: u8,
|
||||
fwd: Option<usize>,
|
||||
}
|
||||
|
||||
impl Test {
|
||||
/// Create a new "packed pair" test from a seed and some given offsets to
|
||||
/// the pair of bytes to use as a predicate in the seed's needle.
|
||||
///
|
||||
/// If a valid test could not be constructed, then None is returned.
|
||||
/// (Currently, we take the approach of massaging tests to be valid
|
||||
/// instead of rejecting them outright.)
|
||||
fn new(
|
||||
seed: Seed,
|
||||
index1: usize,
|
||||
index2: usize,
|
||||
haystack_len: usize,
|
||||
needle_len: usize,
|
||||
fwd: Option<usize>,
|
||||
) -> Option<Test> {
|
||||
let mut index1: u8 = index1.try_into().unwrap();
|
||||
let mut index2: u8 = index2.try_into().unwrap();
|
||||
// The '#' byte is never used in a haystack (unless we're expecting
|
||||
// a match), while the '@' byte is never used in a needle.
|
||||
let mut haystack = vec![b'@'; haystack_len];
|
||||
let mut needle = vec![b'#'; needle_len];
|
||||
needle[0] = seed.first;
|
||||
needle[index1 as usize] = seed.index1;
|
||||
needle[index2 as usize] = seed.index2;
|
||||
// If we're expecting a match, then make sure the needle occurs
|
||||
// in the haystack at the expected position.
|
||||
if let Some(i) = fwd {
|
||||
haystack[i..i + needle.len()].copy_from_slice(&needle);
|
||||
}
|
||||
// If the operations above lead to rare offsets pointing to the
|
||||
// non-first occurrence of a byte, then adjust it. This might lead
|
||||
// to redundant tests, but it's simpler than trying to change the
|
||||
// generation process I think.
|
||||
if let Some(i) = crate::memchr(seed.index1, &needle) {
|
||||
index1 = u8::try_from(i).unwrap();
|
||||
}
|
||||
if let Some(i) = crate::memchr(seed.index2, &needle) {
|
||||
index2 = u8::try_from(i).unwrap();
|
||||
}
|
||||
Some(Test { haystack, needle, index1, index2, fwd })
|
||||
}
|
||||
}
|
||||
|
||||
/// Data that describes a single prefilter test seed.
|
||||
#[derive(Clone, Copy)]
|
||||
struct Seed {
|
||||
first: u8,
|
||||
index1: u8,
|
||||
index2: u8,
|
||||
}
|
||||
|
||||
impl Seed {
|
||||
const NEEDLE_LENGTH_LIMIT: usize = {
|
||||
#[cfg(not(miri))]
|
||||
{
|
||||
33
|
||||
}
|
||||
#[cfg(miri)]
|
||||
{
|
||||
5
|
||||
}
|
||||
};
|
||||
|
||||
const HAYSTACK_LENGTH_LIMIT: usize = {
|
||||
#[cfg(not(miri))]
|
||||
{
|
||||
65
|
||||
}
|
||||
#[cfg(miri)]
|
||||
{
|
||||
8
|
||||
}
|
||||
};
|
||||
|
||||
/// Generate a series of prefilter tests from this seed.
|
||||
fn generate(self) -> impl Iterator<Item = Test> {
|
||||
let len_start = 2;
|
||||
// The iterator below generates *a lot* of tests. The number of
|
||||
// tests was chosen somewhat empirically to be "bearable" when
|
||||
// running the test suite.
|
||||
//
|
||||
// We use an iterator here because the collective haystacks of all
|
||||
// these test cases add up to enough memory to OOM a conservative
|
||||
// sandbox or a small laptop.
|
||||
(len_start..=Seed::NEEDLE_LENGTH_LIMIT).flat_map(move |needle_len| {
|
||||
let index_start = len_start - 1;
|
||||
(index_start..needle_len).flat_map(move |index1| {
|
||||
(index1..needle_len).flat_map(move |index2| {
|
||||
(needle_len..=Seed::HAYSTACK_LENGTH_LIMIT).flat_map(
|
||||
move |haystack_len| {
|
||||
Test::new(
|
||||
self,
|
||||
index1,
|
||||
index2,
|
||||
haystack_len,
|
||||
needle_len,
|
||||
None,
|
||||
)
|
||||
.into_iter()
|
||||
.chain(
|
||||
(0..=(haystack_len - needle_len)).flat_map(
|
||||
move |output| {
|
||||
Test::new(
|
||||
self,
|
||||
index1,
|
||||
index2,
|
||||
haystack_len,
|
||||
needle_len,
|
||||
Some(output),
|
||||
)
|
||||
},
|
||||
),
|
||||
)
|
||||
},
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user