128 lines
4.3 KiB
Rust
128 lines
4.3 KiB
Rust
#![no_main]
|
|
use libfuzzer_sys::fuzz_target;
|
|
|
|
use board::{Board, CardType};
|
|
use enum_iterator::IntoEnumIterator;
|
|
use rand::seq::SliceRandom;
|
|
|
|
struct RandomBytes<'a> {
|
|
data: &'a [u8],
|
|
index: usize,
|
|
}
|
|
|
|
impl<'a> rand::RngCore for RandomBytes<'a> {
|
|
fn next_u32(&mut self) -> u32 {
|
|
if let Option::Some(x) = self.data.get(self.index..self.index + 4) {
|
|
self.index += 4;
|
|
return (u32::from(x[3]) << 24)
|
|
| (u32::from(x[2]) << 16)
|
|
| (u32::from(x[1]) << 8)
|
|
| u32::from(x[0]);
|
|
} else {
|
|
self.index = self.data.len();
|
|
return 0;
|
|
}
|
|
}
|
|
fn next_u64(&mut self) -> u64 {
|
|
return u64::from(self.next_u32()) << 32 | u64::from(self.next_u32());
|
|
}
|
|
fn fill_bytes(&mut self, dest: &mut [u8]) {
|
|
if (self.index >= self.data.len()) || (dest.len() > self.data.len() - self.index) {
|
|
for cell in dest.iter_mut() {
|
|
*cell = 0;
|
|
}
|
|
}
|
|
if dest.len() < self.data.len() - self.index {
|
|
dest.clone_from_slice(&self.data[self.index..self.index + dest.len()]);
|
|
self.index += dest.len()
|
|
}
|
|
}
|
|
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand::Error> {
|
|
self.fill_bytes(dest);
|
|
return Result::Ok(());
|
|
}
|
|
}
|
|
|
|
fn correct_board_permutation(data: &[u8]) -> Board {
|
|
if let Option::Some(remove_info) = data.get(0..2) {
|
|
let remove_info: u16 = u16::from(remove_info[1]) << 8 | u16::from(remove_info[0]);
|
|
let mut result = Board::default();
|
|
let mut whole_vec = Vec::<CardType>::new();
|
|
if remove_info & 1 == 1 {
|
|
result.hua_set = true;
|
|
} else {
|
|
whole_vec.push(CardType::Hua);
|
|
result.hua_set = false;
|
|
}
|
|
for (index, card) in (1_u8..).zip(board::SpecialCardType::into_enum_iter()) {
|
|
if remove_info & (1 << index) == 0 {
|
|
result.bunker[usize::from(index - 1)] =
|
|
board::BunkerSlot::Blocked(Option::Some(card.clone()));
|
|
} else {
|
|
whole_vec.push(CardType::Special(card.clone()));
|
|
whole_vec.push(CardType::Special(card.clone()));
|
|
whole_vec.push(CardType::Special(card.clone()));
|
|
whole_vec.push(CardType::Special(card.clone()));
|
|
result.bunker[usize::from(index - 1)] = board::BunkerSlot::Empty;
|
|
}
|
|
}
|
|
for (index, suit) in (4_u8..)
|
|
.step_by(4)
|
|
.zip(board::NumberCardColor::into_enum_iter())
|
|
{
|
|
let value = (((remove_info >> index) & 0b1111) % 10) as u8;
|
|
let slot_index = usize::from((index - 4) / 4);
|
|
if value == 0 {
|
|
result.goal[slot_index] = Option::None;
|
|
} else {
|
|
result.goal[slot_index] = Option::Some(board::NumberCard {
|
|
value,
|
|
suit: suit.clone(),
|
|
});
|
|
}
|
|
for value in (value + 1)..10 {
|
|
whole_vec.push(board::CardType::Number(board::NumberCard {
|
|
value,
|
|
suit: suit.clone(),
|
|
}));
|
|
}
|
|
}
|
|
whole_vec.shuffle(&mut RandomBytes { data, index: 2 });
|
|
for ((index_start, index_end), slot) in (0..)
|
|
.step_by(8)
|
|
.zip((8..).step_by(8))
|
|
.zip(result.field.iter_mut())
|
|
{
|
|
if let Option::Some(tasty_slice) = whole_vec.get(index_start..index_end) {
|
|
slot.extend_from_slice(tasty_slice);
|
|
} else if let Option::Some(tasty_slice) = whole_vec.get(index_start..) {
|
|
slot.extend_from_slice(tasty_slice);
|
|
break;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
return result;
|
|
} else {
|
|
return Board::default();
|
|
}
|
|
}
|
|
|
|
fuzz_target!(|data: &[u8]| {
|
|
if data.len() == 0 {
|
|
return;
|
|
}
|
|
let x = correct_board_permutation(&data[1..]);
|
|
assert_eq!(x.check(), Result::Ok(()));
|
|
if let Option::Some(action) = board::possibilities::all_actions(&x).choose(&mut RandomBytes {
|
|
data: &data[0..1],
|
|
index: 0,
|
|
}) {
|
|
let mut action_board = x.clone();
|
|
action.apply(&mut action_board);
|
|
assert_ne!(action_board, x);
|
|
action.undo(&mut action_board);
|
|
assert_eq!(action_board, x);
|
|
}
|
|
});
|