Path: blob/main/cranelift/control/src/chaos.rs
1692 views
use alloc::vec::Vec;1use arbitrary::{Arbitrary, Unstructured};23/// The control plane of chaos mode.4/// Please see the [crate-level documentation](crate).5#[derive(Debug, Clone, Default)]6pub struct ControlPlane {7data: Vec<u8>,8fuel: Option<u8>,9/// This is used as a little optimization to avoid additional heap10/// allocations when using `Unstructured` internally. See the source of11/// [`ControlPlane::shuffle`] for an example.12tmp: Vec<u8>,13}1415impl Arbitrary<'_> for ControlPlane {16fn arbitrary<'a>(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {17Ok(Self::new(u.arbitrary()?))18}19}2021impl ControlPlane {22fn new(data: Vec<u8>) -> Self {23Self {24data,25fuel: None,26tmp: Vec::new(),27}28}2930/// Set the [fuel limit](crate#fuel-limit). Zero is interpreted as the31/// fuel limit being deactivated, consistent with the cranelift setting32/// `control_plane_fuel`.33pub fn set_fuel(&mut self, fuel: u8) {34self.fuel = (fuel != 0).then_some(fuel)35}3637/// Tries to consume fuel, returning `true` if successful (or if38/// fuel-limiting is disabled).39fn consume_fuel(&mut self) -> bool {40match self.fuel {41None => true, // fuel deactivated42Some(f) if f == 0 => false, // no more fuel43Some(ref mut f) => {44*f -= 1;45true46}47}48}4950/// Returns a pseudo-random boolean if the control plane was constructed51/// with `arbitrary`.52///53/// The default value `false` will always be returned if the54/// pseudo-random data is exhausted or the control plane was constructed55/// with `default`.56pub fn get_decision(&mut self) -> bool {57self.consume_fuel() && self.data.pop().unwrap_or_default() & 1 == 158}5960/// Returns an arbitrary value if the control plane was constructed with61/// `arbitrary`.62///63/// The default value will always be returned if the pseudo-random data is64/// exhausted or the control plane was constructed with `default`.65pub fn get_arbitrary<T: for<'a> Arbitrary<'a> + Default>(&mut self) -> T {66if !self.consume_fuel() || self.data.is_empty() {67return T::default();68}69let mut u = Unstructured::new(&self.data);70let res = u.arbitrary().unwrap_or_default();7172// take remaining bytes73let rest = u.take_rest();74self.tmp.resize(rest.len(), 0); // allocates once per control plane75self.tmp.copy_from_slice(rest);76core::mem::swap(&mut self.data, &mut self.tmp);7778res79}8081/// Shuffles the items in the slice into a pseudo-random permutation if82/// the control plane was constructed with `arbitrary`.83///84/// The default operation, to leave the slice unchanged, will always be85/// performed if the pseudo-random data is exhausted or the control86/// plane was constructed with `default`.87pub fn shuffle<T>(&mut self, slice: &mut [T]) {88if !self.consume_fuel() || self.data.is_empty() {89return;90}91let mut u = Unstructured::new(&self.data);9293// adapted from:94// https://docs.rs/arbitrary/1.3.0/arbitrary/struct.Unstructured.html#examples-195let mut to_permute = &mut slice[..];9697while to_permute.len() > 1 {98if let Ok(idx) = u.choose_index(to_permute.len()) {99to_permute.swap(0, idx);100to_permute = &mut to_permute[1..];101} else {102break;103}104}105106// take remaining bytes107let rest = u.take_rest();108self.tmp.resize(rest.len(), 0); // allocates once per control plane109self.tmp.copy_from_slice(rest);110core::mem::swap(&mut self.data, &mut self.tmp);111}112113/// Returns a new iterator over the same items as the input iterator in114/// a pseudo-random order if the control plane was constructed with115/// `arbitrary`.116///117/// The default value, an iterator with an unchanged order, will always118/// be returned if the pseudo-random data is exhausted or the control119/// plane was constructed with `default`.120pub fn shuffled<T>(&mut self, iter: impl Iterator<Item = T>) -> impl Iterator<Item = T> {121let mut slice: Vec<_> = iter.collect();122self.shuffle(&mut slice);123slice.into_iter()124}125}126127128