liblisa-synth 0.3.0

A tool for automated discovery and analysis of the ISA of a CPU.
use std::fmt;
use std::num::NonZeroU8;

use arrayvec::ArrayVec;
use liblisa::utils::EitherIter;
use liblisa::utils::bitmap::GrowingBitmap;
use liblisa::value::{AsValue, OwnedValue};
use log::debug;

use super::MAX_INPUTS;
use super::casemap::CaseMap;
use crate::Requester;

#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum TransitionMap {
    Tiny(Vec<Option<NonZeroU8>>),
    Full(Vec<Option<CaseMap>>),
}

impl TransitionMap {
    #[must_use]
    pub fn len(&self) -> usize {
        match self {
            TransitionMap::Tiny(v) => v.len(),
            TransitionMap::Full(v) => v.len(),
        }
    }

    #[must_use]
    pub fn overlaps(&self, transition_map: &TransitionMap) -> bool {
        transition_map.iter().zip(self.iter()).all(|(a, b)| match (a, b) {
            (Some(a), Some(b)) => a.overlaps(b),
            (None, None) => true,
            _ => false,
        })
    }

    #[must_use]
    pub fn get(&self, index: usize) -> Option<CaseMap> {
        match self {
            TransitionMap::Tiny(v) => v[index].map(|m| CaseMap::new_from_u64(m.get() as u64)),
            TransitionMap::Full(v) => v[index],
        }
    }

    pub fn iter(&self) -> impl Iterator<Item = Option<CaseMap>> + '_ {
        match self {
            TransitionMap::Tiny(v) => {
                EitherIter::Left(v.iter().copied().map(|m| m.map(|m| CaseMap::new_from_u64(m.get() as u64))))
            },
            TransitionMap::Full(v) => EitherIter::Right(v.iter().copied()),
        }
    }

    fn create_tiny(item: Option<CaseMap>) -> Option<Option<NonZeroU8>> {
        if item.map(|m| m.as_u64() != 0 && m.as_u64() <= u8::MAX as u64).unwrap_or(true) {
            Some(item.map(|m| NonZeroU8::new(m.as_u64() as u8).unwrap()))
        } else {
            None
        }
    }

    fn push(&mut self, item: Option<CaseMap>) {
        match (item, &mut *self) {
            (m, TransitionMap::Tiny(v)) => {
                if let Some(m) = Self::create_tiny(m) {
                    v.push(m)
                } else {
                    self.as_full().push(m);
                }
            },
            (m, TransitionMap::Full(v)) => v.push(m),
        }
    }

    fn as_full(&mut self) -> &mut Vec<Option<CaseMap>> {
        if let TransitionMap::Full(v) = self {
            v
        } else {
            *self = TransitionMap::Full(self.iter().collect());

            match self {
                TransitionMap::Full(v) => v,
                _ => unreachable!(),
            }
        }
    }

    pub fn restrict_to(&mut self, other: &TransitionMap) {
        match self {
            TransitionMap::Tiny(v) => {
                for (item, restrict) in v.iter_mut().zip(other.iter()) {
                    let mut unpacked_item = item.map(|m| CaseMap::new_from_u64(m.get() as u64));
                    if let (Some(item), Some(restrict)) = (unpacked_item.as_mut(), restrict) {
                        item.restrict_to(restrict)
                    }

                    *item = Self::create_tiny(unpacked_item).unwrap();
                }
            },
            TransitionMap::Full(v) => {
                for (item, restrict) in v.iter_mut().zip(other.iter()) {
                    if let (Some(item), Some(restrict)) = (item.as_mut(), restrict) {
                        item.restrict_to(restrict)
                    }
                }
            },
        }
    }
}

impl FromIterator<Option<CaseMap>> for TransitionMap {
    fn from_iter<T: IntoIterator<Item = Option<CaseMap>>>(iter: T) -> Self {
        let mut result = TransitionMap::Tiny(Vec::new());

        for item in iter.into_iter() {
            result.push(item);
        }

        result
    }
}

impl TransitionMap {
    pub fn build<V: AsValue>(
        transitions: &[Transitions], inputs: &[V], requester: &mut impl Requester<CaseMap>,
    ) -> TransitionMap {
        transitions
            .iter()
            .map(|tr| tr.check_transition(inputs, requester))
            .collect::<TransitionMap>()
    }

    pub fn build_ext<V: AsValue>(
        transitions: &[Transitions], inputs: &[V], output: Option<CaseMap>, requester: &mut impl Requester<CaseMap>,
    ) -> TransitionMap {
        transitions
            .iter()
            .map(|tr| {
                if tr.0.is_empty() {
                    output
                } else {
                    tr.check_transition(inputs, requester)
                }
            })
            .collect::<TransitionMap>()
    }

    pub fn push_transition<V: AsValue>(
        &mut self, inputs: &[V], transition: &Transitions, requester: &mut impl Requester<CaseMap>,
    ) {
        self.push(transition.check_transition(inputs, requester))
    }

    pub fn remove_transitions(&mut self, transitions_to_remove: &GrowingBitmap) {
        let mut iter = transitions_to_remove.iter();

        match self {
            TransitionMap::Tiny(v) => v.retain_mut(|_| !iter.next().unwrap()),
            TransitionMap::Full(v) => v.retain_mut(|_| !iter.next().unwrap()),
        }
    }
}

impl fmt::Debug for TransitionMap {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "[")?;
        let mut first = true;
        for item in self.iter() {
            if !first {
                write!(f, " ")?;
            }

            first = false;

            match item {
                Some(m) => write!(f, "{m:?}")?,
                None => write!(f, "_")?,
            }
        }
        write!(f, "]")?;

        Ok(())
    }
}

#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Transition {
    pub input_index: usize,
    pub value: OwnedValue,
}

#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Transitions(Vec<Transition>);

impl FromIterator<Transition> for Transitions {
    fn from_iter<T: IntoIterator<Item = Transition>>(iter: T) -> Self {
        Transitions(iter.into_iter().collect())
    }
}

impl Transitions {
    pub fn from_vec(v: Vec<Transition>) -> Self {
        Transitions(v)
    }

    pub fn iter(&self) -> impl Iterator<Item = &Transition> + '_ {
        self.0.iter()
    }

    pub fn contains_input(&self, index: usize) -> bool {
        self.0.iter().any(|tr| tr.input_index == index)
    }

    pub fn check_transition<V: AsValue>(&self, inputs: &[V], requester: &mut impl Requester<CaseMap>) -> Option<CaseMap> {
        let mut m = inputs.iter().map(AsValue::as_value).collect::<ArrayVec<_, MAX_INPUTS>>();
        for transition in self.iter() {
            m[transition.input_index] = transition.value.as_value();
        }

        let result = requester.request(&m);
        debug!("{m:X?} -> {result:?}");

        result
    }
}