Question: What is the purpose of the DiffExecutor struct?

Answer: The DiffExecutor struct wraps a primary executor and a secondary executor. As stated in the comment, "In comparison to the [`crate::executors::CombinedExecutor`] it also runs the secondary executor in `run_target`." It is used for differential fuzzing, where the same input is run through both executors and the results are compared.

//! Executor for differential fuzzing.
//! It wraps two executors that will be run after each other with the same input.
//! In comparison to the [`crate::executors::CombinedExecutor`] it also runs the secondary executor in `run_target`.
//!
use core::{cell::UnsafeCell, fmt::Debug, ptr};

use libafl_bolts::{ownedref::OwnedMutPtr, tuples::MatchName};
use serde::{Deserialize, Serialize};

use crate::{
    executors::{Executor, ExitKind, HasObservers},
    inputs::UsesInput,
    observers::{DifferentialObserversTuple, ObserversTuple, UsesObservers},
    state::UsesState,
    Error,
};

/// A [`DiffExecutor`] wraps a primary executor, forwarding its methods, and a secondary one
#[derive(Debug)]
pub struct DiffExecutor<A, B, OTA, OTB, DOT> {
    primary: A,
    secondary: B,
    observers: UnsafeCell<ProxyObserversTuple<OTA, OTB, DOT>>,
}

impl<A, B, OTA, OTB, DOT> DiffExecutor<A, B, OTA, OTB, DOT> {
    /// Create a new `DiffExecutor`, wrapping the given `executor`s.
    pub fn new(primary: A, secondary: B, observers: DOT) -> Self
    where
        A: UsesState + HasObservers<Observers = OTA>,
        B: UsesState<State = A::State> + HasObservers<Observers = OTB>,
        DOT: DifferentialObserversTuple<OTA, OTB, A::State>,
        OTA: ObserversTuple<A::State>,
        OTB: ObserversTuple<A::State>,
    {
        Self {
            primary,
            secondary,
            observers: UnsafeCell::new(ProxyObserversTuple {
                primary: OwnedMutPtr::Ptr(ptr::null_mut()),
                secondary: OwnedMutPtr::Ptr(ptr::null_mut()),
                differential: observers,
            }),
        }
    }

    /// Retrieve the primary `Executor` that is wrapped by this `DiffExecutor`.
    pub fn primary(&mut self) -> &mut A {
        &mut self.primary
    }

    /// Retrieve the secondary `Executor` that is wrapped by this `DiffExecutor`.
    pub fn secondary(&mut self) -> &mut B {
        &mut self.secondary
    }
}

impl<A, B, EM, DOT, Z> Executor<EM, Z> for DiffExecutor<A, B, A::Observers, B::Observers, DOT>
where
    A: Executor<EM, Z> + HasObservers,
    B: Executor<EM, Z, State = A::State> + HasObservers,
    EM: UsesState<State = A::State>,
    DOT: DifferentialObserversTuple<A::Observers, B::Observers, A::State>,
    Z: UsesState<State = A::State>,
{
    fn run_target(
        &mut self,
        fuzzer: &mut Z,
        state: &mut Self::State,
        mgr: &mut EM,
        input: &Self::Input,
    ) -> Result<ExitKind, Error> {
        self.observers(); // update in advance
        let observers = self.observers.get_mut();
        observers
            .differential
            .pre_observe_first_all(observers.primary.as_mut())?;
        observers.primary.as_mut().pre_exec_all(state, input)?;
        let ret1 = self.primary.run_target(fuzzer, state, mgr, input)?;
        observers
            .primary
            .as_mut()
            .post_exec_all(state, input, &ret1)?;
        observers
            .differential
            .post_observe_first_all(observers.primary.as_mut())?;
        observers
            .differential
            .pre_observe_second_all(observers.secondary.as_mut())?;
        observers.secondary.as_mut().pre_exec_all(state, input)?;
        let ret2 = self.secondary.run_target(fuzzer, state, mgr, input)?;
        observers
            .secondary
            .as_mut()
            .post_exec_all(state, input, &ret2)?;
        observers
            .differential
            .post_observe_second_all(observers.secondary.as_mut())?;
        if ret1 == ret2 {
            Ok(ret1)
        } else {
            // We found a diff in the exit codes!
            Ok(ExitKind::Diff {
                primary: ret1.into(),
                secondary: ret2.into(),
            })
        }
    }
}

/// Proxy the observers of the inner executors
#[derive(Serialize, Deserialize, Debug)]
#[serde(
    bound = "A: serde::Serialize + serde::de::DeserializeOwned, B: serde::Serialize + serde::de::DeserializeOwned, DOT: serde::Serialize + serde::de::DeserializeOwned"
)]
pub struct ProxyObserversTuple<A, B, DOT> {
    primary: OwnedMutPtr<A>,
    secondary: OwnedMutPtr<B>,
    differential: DOT,
}

impl<A, B, DOT, S> ObserversTuple<S> for ProxyObserversTuple<A, B, DOT>
where
    A: ObserversTuple<S>,
    B: ObserversTuple<S>,
    DOT: DifferentialObserversTuple<A, B, S>,
    S: UsesInput,
{
    fn pre_exec_all(&mut self, state: &mut S, input: &S::Input) -> Result<(), Error> {
        self.differential.pre_exec_all(state, input)
    }

    fn post_exec_all(
        &mut self,
        state: &mut S,
        input: &S::Input,
        exit_kind: &ExitKind,
    ) -> Result<(), Error> {
        self.differential.post_exec_all(state, input, exit_kind)
    }

    fn pre_exec_child_all(&mut self, state: &mut S, input: &S::Input) -> Result<(), Error> {
        self.differential.pre_exec_child_all(state, input)
    }

    fn post_exec_child_all(
        &mut self,
        state: &mut S,
        input: &S::Input,
        exit_kind: &ExitKind,
    ) -> Result<(), Error> {
        self.differential
            .post_exec_child_all(state, input, exit_kind)
    }

    /// Returns true if a `stdout` observer was added to the list
    #[inline]
    fn observes_stdout(&self) -> bool {
        self.primary.as_ref().observes_stdout() || self.secondary.as_ref().observes_stdout()
    }
    /// Returns true if a `stderr` observer was added to the list
    #[inline]
    fn observes_stderr(&self) -> bool {
        self.primary.as_ref().observes_stderr() || self.secondary.as_ref().observes_stderr()
    }

    /// Runs `observe_stdout` for all stdout observers in the list
    fn observe_stdout(&mut self, stdout: &[u8]) {
        self.primary.as_mut().observe_stderr(stdout);
        self.secondary.as_mut().observe_stderr(stdout);
    }

    /// Runs `observe_stderr` for all stderr observers in the list
    fn observe_stderr(&mut self, stderr: &[u8]) {
        self.primary.as_mut().observe_stderr(stderr);
        self.secondary.as_mut().observe_stderr(stderr);
    }
}

impl<A, B, DOT> MatchName for ProxyObserversTuple<A, B, DOT>
where
    A: MatchName,
    B: MatchName,
    DOT: MatchName,
{
    fn match_name<T>(&self, name: &str) -> Option<&T> {
        if let Some(t) = self.primary.as_ref().match_name::<T>(name) {
            Some(t)
        } else if let Some(t) = self.secondary.as_ref().match_name::<T>(name) {
            Some(t)
        } else {
            self.differential.match_name::<T>(name)
        }
    }
    fn match_name_mut<T>(&mut self, name: &str) -> Option<&mut T> {
        if let Some(t) = self.primary.as_mut().match_name_mut::<T>(name) {
            Some(t)
        } else if let Some(t) = self.secondary.as_mut().match_name_mut::<T>(name) {
            Some(t)
        } else {
            self.differential.match_name_mut::<T>(name)
        }
    }
}

impl<A, B, DOT> ProxyObserversTuple<A, B, DOT> {
    fn set(&mut self, primary: &A, secondary: &B) {
        self.primary = OwnedMutPtr::Ptr(ptr::from_ref(primary) as *mut A);
        self.secondary = OwnedMutPtr::Ptr(ptr::from_ref(secondary) as *mut B);
    }
}

impl<A, B, OTA, OTB, DOT> UsesObservers for DiffExecutor<A, B, OTA, OTB, DOT>
where
    A: HasObservers<Observers = OTA>,
    B: HasObservers<Observers = OTB, State = A::State>,
    OTA: ObserversTuple<A::State>,
    OTB: ObserversTuple<A::State>,
    DOT: DifferentialObserversTuple<OTA, OTB, A::State>,
{
    type Observers = ProxyObserversTuple<OTA, OTB, DOT>;
}

impl<A, B, OTA, OTB, DOT> UsesState for DiffExecutor<A, B, OTA, OTB, DOT>
where
    A: UsesState,
    B: UsesState<State = A::State>,
{
    type State = A::State;
}

impl<A, B, OTA, OTB, DOT> HasObservers for DiffExecutor<A, B, OTA, OTB, DOT>
where
    A: HasObservers<Observers = OTA>,
    B: HasObservers<Observers = OTB, State = A::State>,
    OTA: ObserversTuple<A::State>,
    OTB: ObserversTuple<A::State>,
    DOT: DifferentialObserversTuple<OTA, OTB, A::State>,
{
    #[inline]
    fn observers(&self) -> &ProxyObserversTuple<OTA, OTB, DOT> {
        unsafe {
            self.observers
                .get()
                .as_mut()
                .unwrap()
                .set(self.primary.observers(), self.secondary.observers());
            self.observers.get().as_ref().unwrap()
        }
    }

    #[inline]
    fn observers_mut(&mut self) -> &mut ProxyObserversTuple<OTA, OTB, DOT> {
        unsafe {
            self.observers
                .get()
                .as_mut()
                .unwrap()
                .set(self.primary.observers(), self.secondary.observers());
            self.observers.get().as_mut().unwrap()
        }
    }
}


Question: How do you create a new DiffExecutor instance?

Answer:
A new DiffExecutor instance is created using the `new` method:
```rust
pub fn new(primary: A, secondary: B, observers: DOT) -> Self
```
It takes the primary executor, secondary executor, and a differential observers tuple as arguments.


Question: What happens in the `run_target` method of the DiffExecutor?

Answer:
The `run_target` method does the following:
1. Updates the observers by calling `self.observers()`.
2. Calls `pre_observe_first_all` on the differential observers for the primary executor.
3. Calls `pre_exec_all` on the primary observers.
4. Runs the primary executor's `run_target` method.
5. Calls `post_exec_all` on the primary observers.
6. Calls `post_observe_first_all` on the differential observers for the primary executor.
7. Calls `pre_observe_second_all` on the differential observers for the secondary executor.
8. Calls `pre_exec_all` on the secondary observers.
9. Runs the secondary executor's `run_target` method.
10. Calls `post_exec_all` on the secondary observers.
11. Calls `post_observe_second_all` on the differential observers for the secondary executor.
12. Compares the exit kinds of the primary and secondary executors. If they are the same, returns the exit kind. If they are different, returns an `ExitKind::Diff` containing both exit kinds.


Question: What is the purpose of the SIGNALS and SIGNALS_PTR static variables?

Answer: The SIGNALS and SIGNALS_PTR static variables are used as a coverage map to track which parts of the input have been exercised during fuzzing. SIGNALS is an array of 64 bytes initialized to 0, and SIGNALS_PTR is a mutable pointer to the SIGNALS array. The signals_set function is used to assign a signal to the signals map at a specific index.

#[cfg(windows)]
use std::ptr::write_volatile;
use std::{path::PathBuf, ptr::write};

#[cfg(feature = "tui")]
use libafl::monitors::tui::{ui::TuiUI, TuiMonitor};
#[cfg(not(feature = "tui"))]
use libafl::monitors::SimpleMonitor;
use libafl::{
    corpus::{InMemoryCorpus, OnDiskCorpus},
    events::SimpleEventManager,
    executors::{inprocess::InProcessExecutor, ExitKind},
    feedbacks::{CrashFeedback, MaxMapFeedback},
    fuzzer::{Fuzzer, StdFuzzer},
    inputs::{BytesInput, HasTargetBytes},
    mutators::{StdScheduledMutator, StringCategoryRandMutator, StringSubcategoryRandMutator},
    observers::StdMapObserver,
    schedulers::QueueScheduler,
    stages::{mutational::StdMutationalStage, StringIdentificationStage},
    state::StdState,
    Evaluator,
};
use libafl_bolts::{current_nanos, rands::StdRand, tuples::tuple_list, AsSlice};

/// Coverage map with explicit assignments due to the lack of instrumentation
static mut SIGNALS: [u8; 64] = [0; 64];
static mut SIGNALS_PTR: *mut u8 = unsafe { SIGNALS.as_mut_ptr() };

/// Assign a signal to the signals map
fn signals_set(idx: usize) {
    unsafe { write(SIGNALS_PTR.add(idx), 1) };
}

#[allow(clippy::similar_names, clippy::manual_assert)]
pub fn main() {
    // The closure that we want to fuzz
    let mut harness = |input: &BytesInput| {
        let target = input.target_bytes();
        let buf = target.as_slice();
        let goal = b"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz";
        let mut i = 0;
        for _ in buf.iter().zip(goal).take_while(|(b, c)| b == c) {
            signals_set(i);
            i += 1;
        }
        if i == goal.len() {
            #[cfg(unix)]
            panic!("Artificial bug triggered =)");

            #[cfg(windows)]
            unsafe {
                write_volatile(0 as *mut u32, 0);
            }
        }
        ExitKind::Ok
    };

    // Create an observation channel using the signals map
    let observer = unsafe { StdMapObserver::from_mut_ptr("signals", SIGNALS_PTR, SIGNALS.len()) };

    // Feedback to rate the interestingness of an input
    let mut feedback = MaxMapFeedback::new(&observer);

    // A feedback to choose if an input is a solution or not
    let mut objective = CrashFeedback::new();

    // create a State from scratch
    let mut state = StdState::new(
        // RNG
        StdRand::with_seed(current_nanos()),
        // Corpus that will be evolved, we keep it in memory for performance
        InMemoryCorpus::new(),
        // Corpus in which we store solutions (crashes in this example),
        // on disk so the user can get them after stopping the fuzzer
        OnDiskCorpus::new(PathBuf::from("./crashes")).unwrap(),
        // States of the feedbacks.
        // The feedbacks can report the data that should persist in the State.
        &mut feedback,
        // Same for objective feedbacks
        &mut objective,
    )
    .unwrap();

    // The Monitor trait define how the fuzzer stats are displayed to the user
    #[cfg(not(feature = "tui"))]
    let mon = SimpleMonitor::new(|s| println!("{s}"));
    #[cfg(feature = "tui")]
    let ui = TuiUI::with_version(String::from("Baby Fuzzer"), String::from("0.0.1"), false);
    #[cfg(feature = "tui")]
    let mon = TuiMonitor::new(ui);

    // The event manager handle the various events generated during the fuzzing loop
    // such as the notification of the addition of a new item to the corpus
    let mut mgr = SimpleEventManager::new(mon);

    // A queue policy to get testcasess from the corpus
    let scheduler = QueueScheduler::new();

    // A fuzzer with feedbacks and a corpus scheduler
    let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);

    // Create the executor for an in-process function with just one observer
    let mut executor = InProcessExecutor::new(
        &mut harness,
        tuple_list!(observer),
        &mut fuzzer,
        &mut state,
        &mut mgr,
    )
    .expect("Failed to create the Executor");

    // Generate 8 initial inputs
    fuzzer
        .evaluate_input(
            &mut state,
            &mut executor,
            &mut mgr,
            BytesInput::new(vec![b'a']),
        )
        .unwrap();

    // Setup a mutational stage with a basic bytes mutator
    let mutator = StdScheduledMutator::new(tuple_list!(
        StringCategoryRandMutator,
        StringSubcategoryRandMutator,
        StringSubcategoryRandMutator,
        StringSubcategoryRandMutator,
        StringSubcategoryRandMutator
    ));
    let mut stages = tuple_list!(
        StringIdentificationStage::new(),
        StdMutationalStage::transforming(mutator)
    );

    fuzzer
        .fuzz_loop(&mut stages, &mut executor, &mut state, &mut mgr)
        .expect("Error in the fuzzing loop");
}


Question: How does the harness closure work?

Answer: The harness closure takes a BytesInput and compares its target_bytes() against a goal byte slice. It iterates over the input and goal bytes using zip and take_while, setting signals in the SIGNALS map using signals_set for each matching byte. If the entire goal is matched, it either panics (on Unix) or writes 0 to a null pointer (on Windows) to trigger an artificial bug. Finally, it returns ExitKind::Ok.


Question: What is the purpose of the StdMapObserver?

Answer: The StdMapObserver is used to create an observation channel using the SIGNALS map. It is created using unsafe code to get a mutable pointer to the SIGNALS array and its length. This observer is used to track coverage during fuzzing.


Question: What feedbacks are used in this fuzzer?

Answer: The fuzzer uses two feedbacks: MaxMapFeedback and CrashFeedback. MaxMapFeedback is used to rate the interestingness of an input based on the coverage observed by the StdMapObserver. CrashFeedback is used to determine if an input is a solution (i.e., triggers a crash).


Question: How is the initial corpus generated?

Answer: The initial corpus is generated by evaluating a single input consisting of the byte 'a' using fuzzer.evaluate_input. This adds the input to the corpus and evaluates it using the harness.


Question: What stages are used in the fuzzer?

Answer: The fuzzer uses two stages: StringIdentificationStage and StdMutationalStage. StringIdentificationStage is used to identify string categories and subcategories in the input, which can then be used by the mutators. StdMutationalStage is a transforming stage that applies the scheduled mutators to generate new inputs.


Question: What does the `OomObserver` struct do?

Answer:
The `OomObserver` struct is an implementation of the `Observer` trait in the `libafl` library. It detects if the target program would run out of memory or otherwise violate the permissible usage of `malloc` during fuzzing. This can be seen from the struct definition and its associated documentation comment:
```rust
/// Observer which detects if the target would run out of memory or otherwise violate the permissible usage of malloc
#[derive(Debug, Serialize, Deserialize)]
pub struct OomObserver {
    oomed: bool,
}
```

use core::{ffi::c_void, fmt::Debug};
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};

use libafl::{
    events::EventFirer,
    executors::ExitKind,
    feedbacks::Feedback,
    inputs::UsesInput,
    observers::{Observer, ObserversTuple},
    state::State,
    Error,
};
use libafl_bolts::Named;
use libc::SIGABRT;
use serde::{Deserialize, Serialize};

extern "C" {
    fn libafl_check_malloc_size(ptr: *const c_void) -> usize;
}

static RUNNING: AtomicBool = AtomicBool::new(false);
static OOMED: AtomicBool = AtomicBool::new(false);
static RSS_MAX: AtomicUsize = AtomicUsize::new(2 << 30);
// 2GB, which is the default
static MALLOC_MAX: AtomicUsize = AtomicUsize::new(2 << 30);

static MALLOC_SIZE: AtomicUsize = AtomicUsize::new(0);

/// malloc hook which will be invoked if address sanitizer is present. Used to detect if the target makes a malloc call
/// that will exceed the permissible size
///
/// # Safety
/// Is only safe to call with valid freshly allocated pointers backed by allocations of `size`.
#[no_mangle]
pub unsafe extern "C" fn __sanitizer_malloc_hook(ptr: *const c_void, size: usize) {
    if RUNNING.load(Ordering::Relaxed) {
        let size = match unsafe { libafl_check_malloc_size(ptr) } {
            0 => size, // either the malloc size function didn't work or it's really zero-sized
            real => real,
        };

        let total = MALLOC_SIZE.fetch_add(size, Ordering::Relaxed) + size;
        if (size > MALLOC_MAX.load(Ordering::Relaxed) || total > RSS_MAX.load(Ordering::Relaxed))
            && !OOMED.swap(true, Ordering::Relaxed)
        {
            unsafe {
                // we need to kill the process in a way that immediately triggers the crash handler
                libc::raise(SIGABRT);
            }
        }
    }
}

/// free hook which will be invoked if ASAN is present. Used to detect if the target makes a malloc call that will
/// exceed the permissible size
///
/// # Safety
/// Is only safe to call with valid allocated pointers, about to be freed.
#[no_mangle]
pub unsafe extern "C" fn __sanitizer_free_hook(ptr: *const c_void) {
    if RUNNING.load(Ordering::Relaxed) {
        let size = unsafe { libafl_check_malloc_size(ptr) };
        MALLOC_SIZE
            .fetch_update(Ordering::Relaxed, Ordering::Relaxed, |existing| {
                Some(existing.saturating_sub(size))
            })
            .expect("must complete successfully");
    }
}

const OOM_OBS_NAME: &str = "libfuzzer-like-oom";

/// Observer which detects if the target would run out of memory or otherwise violate the permissible usage of malloc
#[derive(Debug, Serialize, Deserialize)]
pub struct OomObserver {
    oomed: bool,
}

impl OomObserver {
    /// Create a [`OomObserver`] with the provided `rss_max` (total heap size) and `malloc_max` (largest permissible malloc
    /// allocation size)
    pub fn new(rss_max: usize, malloc_max: usize) -> Self {
        RSS_MAX.store(rss_max, Ordering::Relaxed);
        MALLOC_MAX.store(malloc_max, Ordering::Relaxed);
        Self { oomed: false }
    }
}

impl Named for OomObserver {
    // strictly one name to prevent two from being registered
    fn name(&self) -> &str {
        OOM_OBS_NAME
    }
}

impl<S> Observer<S> for OomObserver
where
    S: UsesInput,
{
    fn pre_exec(&mut self, _state: &mut S, _input: &S::Input) -> Result<(), Error> {
        OOMED.store(false, Ordering::Relaxed);
        // must reset for platforms which do not offer malloc tracking
        MALLOC_SIZE.store(0, Ordering::Relaxed);
        RUNNING.store(true, Ordering::Relaxed);
        Ok(())
    }

    fn post_exec(
        &mut self,
        _state: &mut S,
        _input: &S::Input,
        _exit_kind: &ExitKind,
    ) -> Result<(), Error> {
        RUNNING.store(false, Ordering::Relaxed);
        self.oomed = OOMED.load(Ordering::Relaxed);
        Ok(())
    }

    fn pre_exec_child(&mut self, state: &mut S, input: &S::Input) -> Result<(), Error> {
        self.pre_exec(state, input)
    }

    fn post_exec_child(
        &mut self,
        state: &mut S,
        input: &S::Input,
        exit_kind: &ExitKind,
    ) -> Result<(), Error> {
        self.post_exec(state, input, exit_kind)
    }
}

/// Feedback for the similarly named [`OomObserver`] to detect if the target crashed due to an observed OOM
#[derive(Debug, Serialize, Deserialize, Copy, Clone, Default)]
pub struct OomFeedback;

impl OomFeedback {
    /// Whether the target OOM'd in the last execution
    pub fn oomed() -> bool {
        OOMED.load(Ordering::Relaxed)
    }
}

impl Named for OomFeedback {
    fn name(&self) -> &str {
        "oom"
    }
}

impl<S> Feedback<S> for OomFeedback
where
    S: State,
{
    fn is_interesting<EM, OT>(
        &mut self,
        _state: &mut S,
        _manager: &mut EM,
        _input: &S::Input,
        _observers: &OT,
        _exit_kind: &ExitKind,
    ) -> Result<bool, Error>
    where
        EM: EventFirer<State = S>,
        OT: ObserversTuple<S>,
    {
        Ok(Self::oomed())
    }
}


Question: How do I create a new `OomObserver`?

Answer:
To create a new `OomObserver`, you can use the `new` associated function and provide the `rss_max` (total heap size) and `malloc_max` (largest permissible `malloc` allocation size) parameters. This function stores these values in the `RSS_MAX` and `MALLOC_MAX` atomic variables, respectively, and returns a new `OomObserver` instance:
```rust
impl OomObserver {
    /// Create a [`OomObserver`] with the provided `rss_max` (total heap size) and `malloc_max` (largest permissible malloc
    /// allocation size)
    pub fn new(rss_max: usize, malloc_max: usize) -> Self {
        RSS_MAX.store(rss_max, Ordering::Relaxed);
        MALLOC_MAX.store(malloc_max, Ordering::Relaxed);
        Self { oomed: false }
    }
}
```


Question: What does the function `both_require` do?

Answer: The function `both_require` checks if the given byte array `bytes` of length `len` starts with the sequence 'a', 'b', 'c'. It returns `ACCEPT` if the sequence is found at the beginning of the array, and `REJECT` otherwise.

#include "common.h"

bool both_require(const uint8_t *bytes, size_t len) {
  if (len >= 1 && bytes[0] == 'a') {
    if (len >= 2 && bytes[1] == 'b') {
      if (len >= 3 && bytes[2] == 'c') { return ACCEPT; }
    }
  }
  return REJECT;
}


Question: How does `both_require` check for the sequence 'a', 'b', 'c'?

Answer: `both_require` uses a series of nested if statements to check for the sequence. First, it checks if `len >= 1 && bytes[0] == 'a'`, then if `len >= 2 && bytes[1] == 'b'`, and finally if `len >= 3 && bytes[2] == 'c'`. If all these conditions are met, it means the sequence 'a', 'b', 'c' is found at the beginning of the array, and the function returns `ACCEPT`.


Question: What is the purpose of the `len` parameter in `both_require`?

Answer: The `len` parameter represents the length of the byte array `bytes`. It is used in the if conditions to ensure that the function does not access elements beyond the end of the array. For example, `if (len >= 1 && bytes[0] == 'a')` checks if the length is at least 1 before accessing `bytes[0]`.


Question: What does the vuln() function do?

Answer:
The vuln() function compares its input string buf against the string "vuln" using strcmp(). If they are equal, it calls abort() to terminate the program abnormally, as shown in the code:
<code>
void vuln(char *buf) {
  if (strcmp(buf, "vuln") == 0) { abort(); }
}
</code>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// The following line is needed for shared memory testcase fuzzing
__AFL_FUZZ_INIT();

void vuln(char *buf) {
  if (strcmp(buf, "vuln") == 0) { abort(); }
}

int main(int argc, char **argv) {
  // Start the forkserver at this point (i.e., forks will happen here)
  __AFL_INIT();

  // The following five lines are for normal fuzzing.
  /*
  FILE *file = stdin;
  if (argc > 1) { file = fopen(argv[1], "rb"); }
  char  buf[16];
  char *p = fgets(buf, 16, file);
  buf[15] = 0;
  */

  // The following line is also needed for shared memory testcase fuzzing
  unsigned char *buf = __AFL_FUZZ_TESTCASE_BUF;  // must be after __AFL_INIT

  // printf("input: %s\n", buf);
  if (buf[0] == 'b') {
    if (buf[1] == 'a') {
      if (buf[2] == 'd') { abort(); }
    }
  }
  vuln((char *)buf);

  return 0;
}


Question: How is input normally read in the main() function?

Answer:
The code for normal input reading is commented out, but it shows that input is read from either stdin or a file specified as a command-line argument using fgets() into a buffer buf of size 16, as shown here:
<code>
FILE *file = stdin;
if (argc > 1) { file = fopen(argv[1], "rb"); }
char  buf[16];
char *p = fgets(buf, 16, file);
buf[15] = 0;
</code>


Question: What input condition causes the program to abort in the main() function?

Answer:
The main() function checks if the first three characters of the input buf are 'b', 'a', and 'd' respectively. If this condition is met, the program calls abort() to terminate abnormally, as shown in the code:
<code>
if (buf[0] == 'b') {
  if (buf[1] == 'a') {
    if (buf[2] == 'd') { abort(); }
  }
}
</code>


Question: What is the purpose of the `MergeScheduler` struct?

Answer: The `MergeScheduler` struct is a custom scheduler implementation for the libafl fuzzing library. It maintains a mapping between coverage indices and corpus IDs, as well as a set of all corpus IDs. The purpose of this scheduler is not explicitly stated, but based on the `removable()` and `current()` methods, it seems to be used for identifying and managing removable and current corpus items during the fuzzing process.

use std::{
    collections::{BTreeSet, HashMap},
    marker::PhantomData,
};

use libafl::{
    corpus::{Corpus, CorpusId, Testcase},
    feedbacks::MapNoveltiesMetadata,
    inputs::UsesInput,
    schedulers::{RemovableScheduler, Scheduler},
    state::{HasCorpus, HasMetadata, State, UsesState},
    Error,
};

#[derive(Clone, Debug)]
pub struct MergeScheduler<S> {
    mapping: HashMap<usize, CorpusId>,
    all: BTreeSet<CorpusId>,
    phantom: PhantomData<S>,
}

impl<S> UsesState for MergeScheduler<S>
where
    S: State,
{
    type State = S;
}

impl<S> RemovableScheduler for MergeScheduler<S>
where
    S: State + HasCorpus,
{
    fn on_remove(
        &mut self,
        _state: &mut Self::State,
        idx: CorpusId,
        _testcase: &Option<Testcase<<Self::State as UsesInput>::Input>>,
    ) -> Result<(), Error> {
        self.all.remove(&idx);
        Ok(())
    }
}

impl<S> Scheduler for MergeScheduler<S>
where
    S: State + HasCorpus,
{
    fn on_add(&mut self, state: &mut Self::State, idx: CorpusId) -> Result<(), Error> {
        self.all.insert(idx);
        let testcase = state.corpus().get(idx)?.borrow();
        let meta = testcase.metadata::<MapNoveltiesMetadata>()?;
        for cov_idx in &meta.list {
            self.mapping.insert(*cov_idx, idx);
        }
        Ok(())
    }

    fn next(&mut self, _state: &mut Self::State) -> Result<CorpusId, Error> {
        unimplemented!("Not suitable for actual scheduling.");
    }
}

impl<S> MergeScheduler<S> {
    pub fn new() -> Self {
        Self {
            mapping: HashMap::default(),
            all: BTreeSet::default(),
            phantom: PhantomData,
        }
    }

    pub fn removable(&self) -> BTreeSet<CorpusId> {
        self.all
            .difference(&self.mapping.values().copied().collect())
            .copied()
            .collect()
    }

    pub fn current(&self) -> &BTreeSet<CorpusId> {
        &self.all
    }
}


Question: How does the `on_add` method of the `MergeScheduler` work?

Answer:
The `on_add` method is called when a new testcase is added to the corpus. It performs the following steps:
1. Inserts the new corpus ID into the `all` set using `self.all.insert(idx)`.
2. Retrieves the testcase associated with the corpus ID from the state using `state.corpus().get(idx)?.borrow()`.
3. Retrieves the `MapNoveltiesMetadata` from the testcase using `testcase.metadata::<MapNoveltiesMetadata>()?`.
4. Iterates over the coverage indices in the metadata's `list` and inserts them into the `mapping` HashMap, associating each coverage index with the corresponding corpus ID using `self.mapping.insert(*cov_idx, idx)`.
5. Returns `Ok(())` if all operations are successful, or an error if any step fails.


Question: What is the purpose of the `removable()` method in the `MergeScheduler`?

Answer: The `removable()` method is used to identify the corpus IDs that are removable from the scheduler. It does this by performing a set difference operation between the `all` set (containing all corpus IDs) and the set of corpus IDs present in the `mapping` HashMap (obtained by collecting the values of the HashMap into a set). The resulting set contains the corpus IDs that are in the `all` set but not in the `mapping` set, indicating that they are removable. This method returns a new `BTreeSet` containing the removable corpus IDs.


Question: How does the `on_remove` method of the `MergeScheduler` work?

Answer:
The `on_remove` method is called when a testcase is removed from the corpus. It takes the following parameters:
- `_state`: a mutable reference to the fuzzer state (unused in this implementation).
- `idx`: the corpus ID of the testcase being removed.
- `_testcase`: an optional reference to the testcase being removed (unused in this implementation).

The method simply removes the provided corpus ID from the `all` set using `self.all.remove(&idx)`, indicating that the testcase is no longer part of the scheduler's managed corpus. It returns `Ok(())` to indicate a successful removal.


Question: What is the purpose of the `current()` method in the `MergeScheduler`?

Answer: The `current()` method returns a shared reference to the `all` set, which contains all the corpus IDs currently managed by the scheduler. This method allows other parts of the code to access the current set of corpus IDs without modifying it.


Question: Why is the `next()` method of the `MergeScheduler` unimplemented?

Answer: The `next()` method is declared as part of the `Scheduler` trait implementation for `MergeScheduler`, but it is intentionally left unimplemented. The code comment `unimplemented!("Not suitable for actual scheduling.");` suggests that this scheduler is not designed to be used for actual testcase scheduling during the fuzzing process. The `MergeScheduler` seems to be focused on managing the corpus and identifying removable testcases rather than selecting the next testcase to be executed.


Question: How are the `Fp` and `Lr` registers defined as aliases in the `Regs` enum?

Answer:
The `Fp` and `Lr` registers are defined as aliases using an `impl` block for the `Regs` enum:
```rust
impl Regs {
    pub const Fp: Regs = Regs::X29;
    pub const Lr: Regs = Regs::X30;
}
```
This allows using `Regs::Fp` and `Regs::Lr` to refer to the `X29` and `X30` registers, respectively.

use std::sync::OnceLock;

use capstone::arch::BuildsCapstone;
use enum_map::{enum_map, EnumMap};
use num_enum::{IntoPrimitive, TryFromPrimitive};
pub use strum_macros::EnumIter;
pub use syscall_numbers::aarch64::*;

use crate::{sync_backdoor::BackdoorArgs, CallingConvention};

#[derive(IntoPrimitive, TryFromPrimitive, Debug, Clone, Copy, EnumIter)]
#[repr(i32)]
pub enum Regs {
    X0 = 0,
    X1 = 1,
    X2 = 2,
    X3 = 3,
    X4 = 4,
    X5 = 5,
    X6 = 6,
    X7 = 7,
    X8 = 8,
    X9 = 9,
    X10 = 10,
    X11 = 11,
    X12 = 12,
    X13 = 13,
    X14 = 14,
    X15 = 15,
    X16 = 16,
    X17 = 17,
    X18 = 18,
    X19 = 19,
    X20 = 20,
    X21 = 21,
    X22 = 22,
    X23 = 23,
    X24 = 24,
    X25 = 25,
    X26 = 26,
    X27 = 27,
    X28 = 28,
    X29 = 29,
    X30 = 30,
    Sp = 31,
    Pc = 32,
    Pstate = 33,
}

static BACKDOOR_ARCH_REGS: OnceLock<EnumMap<BackdoorArgs, Regs>> = OnceLock::new();

pub fn get_backdoor_arch_regs() -> &'static EnumMap<BackdoorArgs, Regs> {
    BACKDOOR_ARCH_REGS.get_or_init(|| {
        enum_map! {
            BackdoorArgs::Ret  => Regs::X0,
            BackdoorArgs::Cmd  => Regs::X0,
            BackdoorArgs::Arg1 => Regs::X1,
            BackdoorArgs::Arg2 => Regs::X2,
            BackdoorArgs::Arg3 => Regs::X3,
            BackdoorArgs::Arg4 => Regs::X4,
            BackdoorArgs::Arg5 => Regs::X5,
            BackdoorArgs::Arg6 => Regs::X6,
        }
    })
}

/// alias registers
#[allow(non_upper_case_globals)]
impl Regs {
    pub const Fp: Regs = Regs::X29;
    pub const Lr: Regs = Regs::X30;
}

/// Return an ARM64 ArchCapstoneBuilder
pub fn capstone() -> capstone::arch::arm64::ArchCapstoneBuilder {
    capstone::Capstone::new()
        .arm64()
        .mode(capstone::arch::arm64::ArchMode::Arm)
}

pub type GuestReg = u64;

impl crate::ArchExtras for crate::CPU {
    fn read_return_address<T>(&self) -> Result<T, String>
    where
        T: From<GuestReg>,
    {
        self.read_reg(Regs::Lr)
    }

    fn write_return_address<T>(&self, val: T) -> Result<(), String>
    where
        T: Into<GuestReg>,
    {
        self.write_reg(Regs::Lr, val)
    }

    fn read_function_argument<T>(&self, conv: CallingConvention, idx: u8) -> Result<T, String>
    where
        T: From<GuestReg>,
    {
        if conv != CallingConvention::Cdecl {
            return Err(format!("Unsupported calling convention: {conv:#?}"));
        }

        let reg_id = match idx {
            0 => Regs::X0,
            1 => Regs::X1,
            2 => Regs::X2,
            3 => Regs::X3,
            4 => Regs::X4,
            5 => Regs::X5,
            r => return Err(format!("Unsupported argument: {r:}")),
        };

        self.read_reg(reg_id)
    }

    fn write_function_argument<T>(
        &self,
        conv: CallingConvention,
        idx: i32,
        val: T,
    ) -> Result<(), String>
    where
        T: Into<GuestReg>,
    {
        if conv != CallingConvention::Cdecl {
            return Err(format!("Unsupported calling convention: {conv:#?}"));
        }

        let val: GuestReg = val.into();
        match idx {
            0 => self.write_reg(Regs::X0, val),
            1 => self.write_reg(Regs::X1, val),
            _ => Err(format!("Unsupported argument: {idx:}")),
        }
    }
}


Question: What is the purpose of the `get_backdoor_arch_regs` function?

Answer:
The `get_backdoor_arch_regs` function returns a reference to a static `EnumMap` that maps `BackdoorArgs` to `Regs`. It is defined as:
```rust
pub fn get_backdoor_arch_regs() -> &'static EnumMap<BackdoorArgs, Regs> {
    BACKDOOR_ARCH_REGS.get_or_init(|| {
        enum_map! {
            BackdoorArgs::Ret  => Regs::X0,
            BackdoorArgs::Cmd  => Regs::X0,
            BackdoorArgs::Arg1 => Regs::X1,
            BackdoorArgs::Arg2 => Regs::X2,
            BackdoorArgs::Arg3 => Regs::X3,
            BackdoorArgs::Arg4 => Regs::X4,
            BackdoorArgs::Arg5 => Regs::X5,
            BackdoorArgs::Arg6 => Regs::X6,
        }
    })
}
```
It uses the `OnceLock` type to ensure that the `EnumMap` is initialized only once, and the `enum_map!` macro to define the mapping between `BackdoorArgs` and `Regs`.


Question: How do I get the `EnumMap` of backdoor architecture registers?

Answer:
To get the `EnumMap` of backdoor architecture registers, you can use the `get_backdoor_arch_regs` function. This function is defined as follows:

```rust
pub fn get_backdoor_arch_regs() -> &'static EnumMap<BackdoorArgs, Regs> {
    BACKDOOR_ARCH_REGS.get_or_init(|| {
        enum_map! {
            BackdoorArgs::Ret  => Regs::Rax,
            BackdoorArgs::Cmd  => Regs::Rax,
            BackdoorArgs::Arg1 => Regs::Rdi,
            BackdoorArgs::Arg2 => Regs::Rsi,
            BackdoorArgs::Arg3 => Regs::Rdx,
            BackdoorArgs::Arg4 => Regs::R10,
            BackdoorArgs::Arg5 => Regs::R8,
            BackdoorArgs::Arg6 => Regs::R9,
        }
    })
}
```

The `get_backdoor_arch_regs` function uses the `OnceLock` type to lazily initialize the `EnumMap` on the first call and return a reference to it. The `EnumMap` maps the `BackdoorArgs` enum to the corresponding `Regs` enum values, representing the registers used for different backdoor arguments.

use std::{mem::size_of, sync::OnceLock};

use capstone::arch::BuildsCapstone;
use enum_map::{enum_map, EnumMap};
use num_enum::{IntoPrimitive, TryFromPrimitive};
pub use strum_macros::EnumIter;
pub use syscall_numbers::x86_64::*;

use crate::{sync_backdoor::BackdoorArgs, CallingConvention};

#[derive(IntoPrimitive, TryFromPrimitive, Debug, Clone, Copy, EnumIter)]
#[repr(i32)]
pub enum Regs {
    Rax = 0,
    Rbx = 1,
    Rcx = 2,
    Rdx = 3,
    Rsi = 4,
    Rdi = 5,
    Rbp = 6,
    Rsp = 7,
    R8 = 8,
    R9 = 9,
    R10 = 10,
    R11 = 11,
    R12 = 12,
    R13 = 13,
    R14 = 14,
    R15 = 15,
    Rip = 16,
    Rflags = 17,
}

static BACKDOOR_ARCH_REGS: OnceLock<EnumMap<BackdoorArgs, Regs>> = OnceLock::new();

pub fn get_backdoor_arch_regs() -> &'static EnumMap<BackdoorArgs, Regs> {
    BACKDOOR_ARCH_REGS.get_or_init(|| {
        enum_map! {
            BackdoorArgs::Ret  => Regs::Rax,
            BackdoorArgs::Cmd  => Regs::Rax,
            BackdoorArgs::Arg1 => Regs::Rdi,
            BackdoorArgs::Arg2 => Regs::Rsi,
            BackdoorArgs::Arg3 => Regs::Rdx,
            BackdoorArgs::Arg4 => Regs::R10,
            BackdoorArgs::Arg5 => Regs::R8,
            BackdoorArgs::Arg6 => Regs::R9,
        }
    })
}

/// alias registers
#[allow(non_upper_case_globals)]
impl Regs {
    pub const Sp: Regs = Regs::Rsp;
    pub const Pc: Regs = Regs::Rip;
}

/// Return an X86 `ArchCapstoneBuilder`
#[must_use]
pub fn capstone() -> capstone::arch::x86::ArchCapstoneBuilder {
    capstone::Capstone::new()
        .x86()
        .mode(capstone::arch::x86::ArchMode::Mode64)
}

pub type GuestReg = u64;

impl crate::ArchExtras for crate::CPU {
    fn read_return_address<T>(&self) -> Result<T, String>
    where
        T: From<GuestReg>,
    {
        let stack_ptr: GuestReg = self.read_reg(Regs::Rsp)?;
        let mut ret_addr = [0; size_of::<GuestReg>()];
        unsafe { self.read_mem(stack_ptr, &mut ret_addr) };
        Ok(GuestReg::from_le_bytes(ret_addr).into())
    }

    fn write_return_address<T>(&self, val: T) -> Result<(), String>
    where
        T: Into<GuestReg>,
    {
        let stack_ptr: GuestReg = self.read_reg(Regs::Rsp)?;
        let val: GuestReg = val.into();
        let ret_addr = val.to_le_bytes();
        unsafe { self.write_mem(stack_ptr, &ret_addr) };
        Ok(())
    }

    fn read_function_argument<T>(&self, conv: CallingConvention, idx: u8) -> Result<T, String>
    where
        T: From<GuestReg>,
    {
        if conv != CallingConvention::Cdecl {
            return Err(format!("Unsupported calling convention: {conv:#?}"));
        }

        let reg_id = match idx {
            0 => Regs::Rdi,
            1 => Regs::Rsi,
            2 => Regs::Rdx,
            3 => Regs::Rcx,
            4 => Regs::R8,
            5 => Regs::R9,
            r => return Err(format!("Unsupported argument: {r:}")),
        };

        self.read_reg(reg_id)
    }

    fn write_function_argument<T>(
        &self,
        conv: CallingConvention,
        idx: i32,
        val: T,
    ) -> Result<(), String>
    where
        T: Into<GuestReg>,
    {
        if conv != CallingConvention::Cdecl {
            return Err(format!("Unsupported calling convention: {conv:#?}"));
        }

        let val: GuestReg = val.into();
        match idx {
            0 => self.write_reg(Regs::Rdi, val),
            1 => self.write_reg(Regs::Rsi, val),
            _ => Err(format!("Unsupported argument: {idx:}")),
        }
    }
}


Question: How do you convert a `NautilusInput` to a `BytesInput`?

Answer:
You can use the `NautilusToBytesInputConverter` to convert a `NautilusInput` to a `BytesInput`. First, create an instance of `NautilusToBytesInputConverter` with a `NautilusContext`:
```rust
let converter = NautilusToBytesInputConverter::new(&context);
```
Then, call the `convert` method on the converter, passing in the `NautilusInput`:
```rust
let bytes_input = converter.convert(nautilus_input)?;
```
The `convert` method internally calls the `unparse` method of `NautilusInput` to convert the input to bytes:
```rust
fn convert(&mut self, input: Self::From) -> Result<Self::To, Error> {
    let mut bytes = vec![];
    input.unparse(self.ctx, &mut bytes);
    Ok(BytesInput::new(bytes))
}
```

//! Input for the [`Nautilus`](https://github.com/RUB-SysSec/nautilus) grammar fuzzer methods
//!

//use ahash::AHasher;
//use core::hash::Hasher;

use alloc::{rc::Rc, string::String, vec::Vec};
use core::cell::RefCell;
use std::hash::{Hash, Hasher};

use grammartec::{
    newtypes::NodeID,
    rule::RuleIDOrCustom,
    tree::{Tree, TreeLike},
};
use libafl_bolts::HasLen;
use serde::{Deserialize, Serialize};

use crate::{
    generators::nautilus::NautilusContext,
    inputs::{BytesInput, Input, InputConverter},
    Error,
};

/// An [`Input`] implementation for `Nautilus` grammar.
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct NautilusInput {
    /// The input representation as Tree
    pub tree: Tree,
}

impl Input for NautilusInput {
    /// Generate a name for this input
    #[must_use]
    fn generate_name(&self, idx: usize) -> String {
        /*let mut hasher = AHasher::new_with_keys(0, 0);
        for term in &self.terms {
            hasher.write(term.symbol.as_bytes());
        }
        format!("{:016x}", hasher.finish())*/
        format!("id:{idx}")
    }
}

/// Rc Ref-cell from Input
impl From<NautilusInput> for Rc<RefCell<NautilusInput>> {
    fn from(input: NautilusInput) -> Self {
        Rc::new(RefCell::new(input))
    }
}

impl HasLen for NautilusInput {
    #[inline]
    fn len(&self) -> usize {
        self.tree.size()
    }
}

impl NautilusInput {
    /// Creates a new codes input using the given terminals
    #[must_use]
    pub fn new(tree: Tree) -> Self {
        Self { tree }
    }

    /// Create an empty [`Input`]
    #[must_use]
    pub fn empty() -> Self {
        Self {
            tree: Tree {
                rules: vec![],
                sizes: vec![],
                paren: vec![],
            },
        }
    }

    /// Generate a `Nautilus` input from the given bytes
    pub fn unparse(&self, context: &NautilusContext, bytes: &mut Vec<u8>) {
        bytes.clear();
        self.tree.unparse(NodeID::from(0), &context.ctx, bytes);
    }

    /// Get the tree representation of this input
    #[must_use]
    pub fn tree(&self) -> &Tree {
        &self.tree
    }

    /// Get the tree representation of this input, as a mutable reference
    #[must_use]
    pub fn tree_mut(&mut self) -> &mut Tree {
        &mut self.tree
    }
}

impl Hash for NautilusInput {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.tree().paren.hash(state);
        for r in &self.tree().rules {
            match r {
                RuleIDOrCustom::Custom(a, b) => {
                    a.hash(state);
                    b.hash(state);
                }
                RuleIDOrCustom::Rule(a) => a.hash(state),
            }
        }
        self.tree().sizes.hash(state);
    }
}

/// `InputConverter` to convert from `NautilusInput` to `BytesInput`
#[derive(Debug)]
pub struct NautilusToBytesInputConverter<'a> {
    ctx: &'a NautilusContext,
}

impl<'a> NautilusToBytesInputConverter<'a> {
    #[must_use]
    /// Create a new `NautilusToBytesInputConverter` from a context
    pub fn new(ctx: &'a NautilusContext) -> Self {
        Self { ctx }
    }
}

impl<'a> InputConverter for NautilusToBytesInputConverter<'a> {
    type From = NautilusInput;
    type To = BytesInput;

    fn convert(&mut self, input: Self::From) -> Result<Self::To, Error> {
        let mut bytes = vec![];
        input.unparse(self.ctx, &mut bytes);
        Ok(BytesInput::new(bytes))
    }
}


Question: How do you get the `Tree` representation of a `NautilusInput`?

Answer:
You can get the `Tree` representation of a `NautilusInput` using the `tree` method, which returns an immutable reference to the `Tree`:
```rust
pub fn tree(&self) -> &Tree {
    &self.tree
}
```
If you need a mutable reference to the `Tree`, you can use the `tree_mut` method instead:
```rust
pub fn tree_mut(&mut self) -> &mut Tree {
    &mut self.tree
}
```


Question: What traits does `NautilusInput` implement?

Answer:
The `NautilusInput` struct implements several traits:
- `Input`: Allows `NautilusInput` to be used as an input type in the fuzzer.
- `From<NautilusInput> for Rc<RefCell<NautilusInput>>`: Enables creating an `Rc<RefCell<NautilusInput>>` from a `NautilusInput`.
- `HasLen`: Provides a `len` method that returns the size of the input's `Tree`.
- `Hash`: Allows hashing of `NautilusInput` instances based on their `Tree` structure.


Question: How do you initialize the logger?

Answer: To initialize the logger, you need to call the Logger::init function and pass an instance that implements the LogArgs trait. This can be seen in the function signature: `pub fn init(args: &impl LogArgs) -> Result<()>`.

use {
    crate::args::LogArgs,
    anyhow::{anyhow, Result},
    simplelog::{Config, LevelFilter, WriteLogger},
    std::fs::File,
};

pub struct Logger;

impl Logger {
    pub fn init(args: &impl LogArgs) -> Result<()> {
        let filter: LevelFilter = args.log_level().into();
        if filter != LevelFilter::Off {
            let logfile = File::create(args.log_file())
                .map_err(|e| anyhow!("Failed to open log file: {e:}"))?;
            WriteLogger::init(filter, Config::default(), logfile)
                .map_err(|e| anyhow!("Failed to initalize logger: {e:}"))?;
        }
        Ok(())
    }
}


Question: How is the log file created?

Answer: The log file is created using `File::create(args.log_file())`. It takes the log file path obtained from `args.log_file()` and attempts to create the file. If the file creation fails, an error is returned using `map_err()` with a custom error message: `map_err(|e| anyhow!("Failed to open log file: {e:}"))?`.


Question: What logger implementation is being used?

Answer: The code uses the `simplelog` crate for logging. Specifically, it uses the `WriteLogger` from `simplelog`, as shown in the line: `WriteLogger::init(filter, Config::default(), logfile)`.


Question: How do you register a new type in the Registry?

Answer:
To register a new type in the Registry, you can use either the `register` or `register_multi` methods. The `register` method is used for registering a single type, while `register_multi` is used for registering multiple types at once. For example:
```python
registry.register(MyType)
```
or
```python
@registry.register_multi(arg1, arg2)
class MyType:
    pass
```
The `_real_register` method is called internally to handle the actual registration process.

from typing import Any, Dict, List, Optional, Set, Tuple, Type, Union

try:
    from typing import get_args, get_origin
except ImportError:
    from typing_inspect import get_origin, get_args

from . import _fwd
from ._modules import *

class Registry:
    # I was planning on using __init_subclass__, but that is incompatible with dynamic type creation when we have
    # generic keys

    RegElem = Union[List[Type], Dict[Type, "RegElem"]]

    _reg: Dict[Type, RegElem] = {}
    _names: Dict[str, Tuple[Type, Set[Type]]] = {}
    _targets: Dict[str, Dict[Type, List[Type]]] = {}
    _modules = {Checker, Cracker, Decoder, ResourceLoader, Searcher, PolymorphicChecker}

    def _register_one(self, input_type, module_base, module_args):
        if len(module_args) == 0:
            self._reg.setdefault(module_base, []).append(input_type)
            return

        target_reg = self._reg.setdefault(module_base, {})
        # Seek to the given type
        for subtype in module_args[0:-1]:
            target_reg = target_reg.setdefault(subtype, {})
        target_reg.setdefault(module_args[-1], []).append(input_type)

    def _real_register(self, input_type: type, *args) -> Type:
        name = input_type.__name__.lower()
        name_target = self._names[name] = (input_type, set())

        if issubclass(input_type, Targeted):
            target = input_type.getTarget()
        else:
            target = None

        if issubclass(input_type, Searcher):
            module_type = module_base = Searcher
            module_args = ()
        else:
            module_type: Optional[Type] = None
            module_base = None

            # Work out what module type this is
            if len(args) == 0 and hasattr(input_type, "__orig_bases__"):
                for i in input_type.__orig_bases__:
                    if module_type is not None:
                        raise TypeError(
                            f"Type derived from multiple registrable base classes {i} and {module_type}"
                        )
                    module_base = get_origin(i)
                    if module_base not in self._modules:
                        continue
                    module_type = i
            else:
                for i in self._modules:
                    if not issubclass(input_type, i):
                        continue
                    if module_type is not None:
                        raise TypeError(
                            f"Type derived from multiple registrable base classes {i} and {module_type}"
                        )
                    module_type = i
            if module_type is None:
                raise TypeError("No registrable base class")

            # Replace input type with polymorphic checker if required
            if issubclass(input_type, Checker):
                if len(args) == 0:
                    arg = [
                        get_args(i)
                        for i in input_type.__orig_bases__
                        if get_origin(i) == Checker
                    ][0]
                    if len(arg) != 1:
                        raise TypeError("No argument for Checker")
                    input_type = input_type.convert({arg[0]})
                else:
                    input_type = input_type.convert(set(args))
                self._register_one(input_type, PolymorphicChecker, [])
                # Refresh the names with the new type
                name_target = self._names[name] = (input_type, {PolymorphicChecker})

            # Now handle the difference between register and register_multi
            if len(args) == 0:
                if module_type is PolymorphicChecker:
                    module_base = PolymorphicChecker
                elif module_base is None:
                    raise TypeError("No type argument given")
                self._register_one(input_type, module_base, get_args(module_type))
                name_target[1].add(module_base)
            else:
                if module_base is not None:
                    raise TypeError(f"Redundant type argument for {module_type}")
                module_base = module_type
                for module_args in args:
                    # Correct missing brackets
                    if not isinstance(module_args, tuple):
                        module_args = (module_args,)

                    self._register_one(input_type, module_base, module_args)
                    name_target[1].add(module_type[module_args])

        name_target[1].add(module_type)

        if target is not None and issubclass(module_base, Targeted):
            self._targets.setdefault(target, {}).setdefault(module_type, []).append(
                input_type
            )

        return input_type

    def register(self, input_type):
        return self._real_register(input_type)

    def register_multi(self, *x):
        return lambda input_type: self._real_register(input_type, *x)

    def __getitem__(self, i: type) -> Optional[Any]:
        target_type = get_origin(i)
        # Check if this is a non-generic type, and return the whole dict if it is
        if target_type is None:
            return self._reg[i]

        target_subtypes = get_args(i)
        target_list = self._reg.setdefault(target_type, {})
        for subtype in target_subtypes:
            target_list = target_list.setdefault(subtype, {})
        return target_list

    def get_named(self, name: str, type_constraint: Type = None) -> Any:
        ret = self._names[name.lower()]
        if type_constraint and type_constraint not in ret[1]:
            raise TypeError(f"Type mismatch: wanted {type_constraint}, got {ret[1]}")
        return ret[0]

    def get_targeted(
        self, target: str, type_constraint: Type = None
    ) -> Optional[Union[Dict[Type, Set[Type]], Set[Type]]]:
        x = self._targets.get(target)
        if x is None or type_constraint is None:
            return x
        return x.get(type_constraint)

    def get_all_names(self) -> List[str]:
        return list(self._names.keys())

    def __str__(self):
        return f"ciphey.iface.Registry {{_reg: {self._reg}, _names: {self._names}, _targets: {self._targets}}}"

_fwd.registry = Registry()


Question: What is the purpose of the `_real_register` method?

Answer: The `_real_register` method is responsible for the actual registration of types in the Registry. It performs various checks and operations based on the input type and any additional arguments provided. It determines the module type and base class of the input type, handles special cases like `PolymorphicChecker`, and registers the type in the appropriate data structures within the Registry. It also updates the `_names` and `_targets` dictionaries to maintain associations between names, types, and targets.


Question: How can you retrieve registered types from the Registry?

Answer:
The Registry provides several methods to retrieve registered types:
- The `__getitem__` method allows you to retrieve types based on a specific type or generic type. For example:
  ```python
  registry[MyType]
  registry[Checker[int]]
  ```
- The `get_named` method allows you to retrieve a type by its name. For example:
  ```python
  registry.get_named("mytype")
  ```
- The `get_targeted` method allows you to retrieve types associated with a specific target. For example:
  ```python
  registry.get_targeted("mytarget")
  ```
- The `get_all_names` method returns a list of all registered type names.


Question: How are targeted types handled in the Registry?

Answer:
Targeted types, which are types that inherit from the `Targeted` class, are handled specially in the Registry. When a targeted type is registered, the Registry stores the association between the target and the module type in the `_targets` dictionary. For example:
```python
if target is not None and issubclass(module_base, Targeted):
    self._targets.setdefault(target, {}).setdefault(module_type, []).append(
        input_type
    )
```
This allows for retrieval of targeted types using the `get_targeted` method based on the target and an optional type constraint.


Question: What is the purpose of the `_modules` set in the Registry?

Answer: The `_modules` set in the Registry is used to store the base classes that are considered as registrable modules. It includes classes like `Checker`, `Cracker`, `Decoder`, `ResourceLoader`, `Searcher`, and `PolymorphicChecker`. This set is used to determine the module type of a registered type during the registration process.


Question: What does the Octal class do?

Answer: The Octal class is a Decoder that performs octal decoding on a given ciphertext string, as stated in the docstring: "Performs Octal decoding". It takes a ciphertext input of type T and returns the decoded result of type U if decoding is successful, or None if decoding fails.

from typing import Dict, Optional

import logging
from rich.logging import RichHandler

from ciphey.iface import Config, Decoder, ParamSpec, T, U, registry

@registry.register
class Octal(Decoder[str]):
    def decode(self, ctext: T) -> Optional[U]:
        """
        Performs Octal decoding
        """
        str_converted = []
        octal_seq = ctext.split(" ")
        if len(octal_seq) == 1:
            # Concatted octal must be formed of octal triplets
            if len(ctext) % 3 != 0:
                return None
            octal_seq = [ctext[i : i + 3] for i in range(0, len(ctext), 3)]
            logging.debug(f"Trying chunked octal {octal_seq}")
        try:
            for octal_char in octal_seq:
                if len(octal_char) > 3:
                    logging.debug("Octal subseq too long")
                    return None
                n = int(octal_char, 8)
                if (
                    n < 0
                ):  # n cannot be greater than 255, as we checked that with the earlier length check
                    logging.debug(f"Non octal char {octal_char}")
                    return None
                str_converted.append(n)

            return bytes(str_converted)
        # Catch bad octal chars
        except ValueError:
            return None

    @staticmethod
    def priority() -> float:
        return 0.025

    def __init__(self, config: Config):
        super().__init__(config)

    @staticmethod
    def getParams() -> Optional[Dict[str, ParamSpec]]:
        return None

    @staticmethod
    def getTarget() -> str:
        return "octal"


Question: How does the decode method of the Octal class work?

Answer: The decode method first splits the input ctext string by spaces into a list of octal sequences (octal_seq). If there is only one sequence and its length is not divisible by 3, it returns None since valid octal must be formed of triplets. It then tries to convert each octal character in octal_seq to an integer using base 8. If any subsequence is too long (>3 chars) or contains non-octal characters, it returns None. Otherwise, it appends the converted integer to str_converted. Finally, it returns the bytes representation of str_converted.


Question: What does the getTarget method of the Octal class do?

Answer: The getTarget method simply returns the string "octal". Based on the @registry.register decorator used on the Octal class, this likely specifies the name or key used to look up this decoder in the Ciphey registry.


Question: What external dependencies does the Octal class have?

Answer: The Octal class imports typing for type hinting with Dict, Optional, logging for debug logging, RichHandler from rich.logging likely for formatted logging output, and Config, Decoder, ParamSpec, T, U, registry from ciphey.iface which are part of the Ciphey framework. The class does not seem to directly use RichHandler or Config in the provided code.


Question: How does the decode method of the A1z26 class work?

Answer: The decode method splits the input ciphertext on various delimiters like spaces, commas, semicolons, colons, hyphens, and newlines using a regular expression. It then checks if the resulting substrings are numeric. If any substring is not numeric or outside the range 1 to 26, it returns None indicating decoding failure. Otherwise, it converts each numeric substring to its corresponding lowercase letter (1=a, 2=b, ..., 26=z) and joins them to produce the decoded plaintext.

import re
from typing import Dict, Optional

import logging
from rich.logging import RichHandler

from ciphey.iface import Config, Decoder, ParamSpec, T, U, registry

@registry.register
class A1z26(Decoder[str]):
    def decode(self, ctext: T) -> Optional[U]:
        """
        Performs A1Z26 decoding
        """
        logging.debug("Attempting A1Z26")
        ctext_converted = []
        ctext_split = re.split(r"[ ,;:\-\n]", ctext)
        delimiters = set(sorted(re.sub(r"[^ ,;:\-\n]", "", ctext)))
        ctext_num = re.sub(r"[,;:\-\s]", "", ctext)
        ctext_decoded = ""
        if ctext_num.isnumeric() is False:
            logging.debug("Failed to decode A1Z26 due to non numeric character(s)")
            return None
        try:
            for i in ctext_split:
                val = int(i)
                if val > 26 or val < 1:
                    logging.debug(
                        f"Failed to decode A1Z26 due to invalid number '{val}'"
                    )
                    return None
                val2 = int(i) + 96
                ctext_converted.append(chr(val2))
            ctext_decoded = "".join(ctext_converted)
            logging.info(
                f"A1Z26 successful, returning '{ctext_decoded}' with delimiter(s) {delimiters}"
            )
            return ctext_decoded
        except Exception:
            return None

    @staticmethod
    def priority() -> float:
        return 0.05

    def __init__(self, config: Config):
        super().__init__(config)

    @staticmethod
    def getParams() -> Optional[Dict[str, ParamSpec]]:
        return None

    @staticmethod
    def getTarget() -> str:
        return "a1z26"


Question: What is the purpose of the priority method in the A1z26 class?

Answer: The priority method returns a float value of 0.05. This likely indicates the priority or ordering in which this decoder will be tried relative to other decoders in the Ciphey framework.


Question: What does the getParams method do in the A1z26 class?

Answer:
The getParams method returns None, indicating that this decoder class does not accept any parameters for configuration. This can be seen in the method implementation:
<code>
@staticmethod
def getParams() -> Optional[Dict[str, ParamSpec]]:
    return None
</code>


Question: What is the purpose of the getTarget method?

Answer: The getTarget method returns the string "a1z26". This likely specifies the target encoding or cipher that this class is intended to decode.


Question: How are the delimiters in the input ciphertext handled?

Answer:
The delimiters in the input ciphertext are identified using a regular expression that matches any non-alphanumeric characters: re.sub(r"[^ ,;:\-\n]", "", ctext). The sorted unique set of delimiter characters is stored in the delimiters variable and returned along with the decoded plaintext for informational purposes in the log message:
logging.info(
    f"A1Z26 successful, returning '{ctext_decoded}' with delimiter(s) {delimiters}"
)


Question: What is the purpose of the priority method in the Base58_ripple class?

Answer: The priority method returns a float value of 0.05, indicating the priority of this decoder. The comment suggests that while Base58 (Ripple) encoding is not expected to show up often, it is very fast to check. The priority helps determine the order in which decoders are tried when attempting to decode a ciphertext.

from typing import Dict, Optional

import base58

from ciphey.iface import Config, Decoder, ParamSpec, T, U, registry

@registry.register
class Base58_ripple(Decoder[str]):
    def decode(self, ctext: T) -> Optional[U]:
        """
        Performs Base58 (Ripple) decoding
        """
        try:
            return base58.b58decode(ctext, alphabet=base58.RIPPLE_ALPHABET).decode(
                "utf-8"
            )
        except Exception:
            return None

    @staticmethod
    def priority() -> float:
        # Not expected to show up often, but also very fast to check.
        return 0.05

    def __init__(self, config: Config):
        super().__init__(config)

    @staticmethod
    def getParams() -> Optional[Dict[str, ParamSpec]]:
        return None

    @staticmethod
    def getTarget() -> str:
        return "base58_ripple"


Question: What does the getParams method of the Base58_ripple class do?

Answer: The getParams method returns None, indicating that this decoder does not require any additional parameters to be specified. It is a static method decorated with @staticmethod.


Question: What is the purpose of the getTarget method in the Base58_ripple class?

Answer: The getTarget method is a static method that returns the string "base58_ripple". This likely serves as an identifier or target name for this specific decoder, allowing it to be easily referenced or searched for within the codebase.


Question: How are the character and word boundaries determined in the Morse code decoding process?

Answer: The character and word boundaries are determined based on a priority list defined in the BOUNDARIES dictionary: `BOUNDARIES = {" ": 1, "/": 2, "\n": 3}`. The decode method iterates over each character in the ciphertext and updates the char_boundary and word_boundary variables based on the encountered characters and their priorities. The character with the highest priority is considered as the character boundary, and if a character with a higher priority than the current word boundary is found, it becomes the new word boundary.

from typing import Dict, Optional

import logging
from rich.logging import RichHandler

from ciphey.iface import Config, Decoder, ParamSpec, T, Translation, U, registry

@registry.register
class Morse_code(Decoder[str]):
    # A priority list for char/word boundaries
    BOUNDARIES = {" ": 1, "/": 2, "\n": 3}
    PURGE = {ord(c): None for c in BOUNDARIES.keys()}
    MAX_PRIORITY = 3
    ALLOWED = {".", "-", " ", "/", "\n"}
    MORSE_CODE_DICT: Dict[str, str]
    MORSE_CODE_DICT_INV: Dict[str, str]

    def decode(self, ctext: T) -> Optional[U]:
        logging.debug("Attempting Morse code decoder")

        char_boundary = word_boundary = None

        char_boundary = word_boundary = None
        char_priority = word_priority = 0
        # Custom loop allows early break
        for i in ctext:
            i_priority = self.BOUNDARIES.get(i)
            if i_priority is None:
                if i in self.ALLOWED:
                    continue
                logging.debug(f"Non-morse char '{i}' found")
                return None

            if i_priority <= char_priority or i == char_boundary or i == word_boundary:
                continue
            # Default to having a char boundary over a word boundary
            if (
                i_priority > word_priority
                and word_boundary is None
                and char_boundary is not None
            ):
                word_priority = i_priority
                word_boundary = i
                continue
            char_priority = i_priority
            char_boundary = i

        logging.debug(
            f"Char boundary is unicode {ord(char_boundary)}, and word boundary is unicode {ord(word_boundary) if word_boundary is not None else None}"
        )

        result = ""

        for word in ctext.split(word_boundary) if word_boundary else [ctext]:
            logging.debug(f"Attempting to decode word {word}")
            for char in word.split(char_boundary):
                char = char.translate(self.PURGE)
                if len(char) == 0:
                    continue
                try:
                    m = self.MORSE_CODE_DICT_INV[char]
                except KeyError:
                    logging.debug(f"Invalid codeword '{char}' found")
                    return None
                result = result + m
            # after every word add a space
            result = result + " "
        if len(result) == 0:
            logging.debug("Morse code failed to match")
            return None
        # Remove trailing space
        result = result[:-1]
        logging.info(f"Morse code successful, returning {result}")
        return result.strip().upper()

    @staticmethod
    def priority() -> float:
        return 0.05

    def __init__(self, config: Config):
        super().__init__(config)
        self.MORSE_CODE_DICT = config.get_resource(self._params()["dict"], Translation)
        self.MORSE_CODE_DICT_INV = {v: k for k, v in self.MORSE_CODE_DICT.items()}

    @staticmethod
    def getParams() -> Optional[Dict[str, ParamSpec]]:
        return {
            "dict": ParamSpec(
                desc="The morse code dictionary to use",
                req=False,
                default="cipheydists::translate::morse",
            )
        }

    @staticmethod
    def getTarget() -> str:
        return "morse_code"


Question: What is the purpose of the priority method in the Morse_code class?

Answer: The priority method returns a float value indicating the priority of the Morse_code decoder. It is defined as a static method and returns a value of 0.05. This priority value is used by the decoder registry to determine the order in which decoders are attempted.


Question: What does the `getInfo` method of the `Soundex` class do?

Answer: The `getInfo` method of the `Soundex` class returns a `CrackInfo` object containing information about the likelihood and runtime of successfully cracking a Soundex-encoded ciphertext. Specifically, it returns a `CrackInfo` with `success_likelihood=0.1`, `success_runtime=1e-5`, and `failure_runtime=1e-5`.

import re
from typing import Dict, List, Optional

import logging
from rich.logging import RichHandler

from ciphey.iface import (
    Config,
    Cracker,
    CrackInfo,
    CrackResult,
    ParamSpec,
    Translation,
    registry,
)

@registry.register
class Soundex(Cracker[str]):
    def getInfo(self, ctext: str) -> CrackInfo:
        return CrackInfo(
            success_likelihood=0.1,
            success_runtime=1e-5,
            failure_runtime=1e-5,
        )

    @staticmethod
    def getTarget() -> str:
        return "soundex"

    def attemptCrack(self, ctext: str) -> List[CrackResult]:
        """
        Attempts to crack Soundex by generating all possible combinations.
        """
        logging.debug("Attempting Soundex cracker")
        word_list = []
        sentences = []
        result = []

        # Convert to uppercase and replace delimiters and whitespace with nothing
        ctext = re.sub(r"[,;:\-\s]", "", ctext.upper())

        # Make sure ctext contains only A-Z and 0-9
        if bool(re.search(r"[^A-Z0-9]", ctext)) is True:
            logging.debug("Failed to crack soundex due to non soundex character(s)")
            return None

        # Make sure ctext is divisible by 4
        ctext_len = len(ctext)
        if ctext_len % 4:
            logging.debug(
                f"Failed to decode Soundex because length must be a multiple of 4, not '{ctext_len}'"
            )
            return None

        # Split ctext into groups of 4
        ctext = " ".join(ctext[i : i + 4] for i in range(0, len(ctext), 4))
        ctext_split = ctext.split(" ")
        soundex_keys = self.SOUNDEX_DICT.keys()

        # Find all words that correspond to each given soundex code
        for code in ctext_split:
            if code in soundex_keys:
                word_list.append(self.SOUNDEX_DICT[code])

        logging.info(f"Possible words for given encoded text: {word_list}")

        # Find all possible sentences
        self.getSentenceCombo(
            word_list,
            sentences,
            self.frequency_dict,
            self.sentence_freq,
            self.word_freq,
        )

        sorted_sentences = self.sortlistwithdict(sentences, self.frequency_dict)

        for sentence in sorted_sentences:
            result.append(CrackResult(value=sentence))

        logging.debug(f"Soundex cracker - Returning results: {result}")
        return result

    def sortlistwithdict(self, listtosort, hashes):
        """
        This function uses the sum of ranks (based on frequency) of each word in each
        sentence and sorts them according to it.
        """
        return sorted(listtosort, key=lambda x: hashes[x])

    def getSentenceCombo(
        self, A, sentences, frequency_dict, sentence_freq, word_freq, result="", n=0
    ):
        """
        This function uses recursion to generate a list of sentences from all possible
        words for a given set of soundex codes.
        """
        logging.debug("Creating all possible sentences from Soundex")
        if n == len(A):
            sentences.append(result[1:])
            for word in result[1:].split():
                # Adding the rank of each word to find out the sentence's net frequency
                if word in word_freq:
                    sentence_freq += word_freq.index(word)
                # If the word isn't in the frequency list then it's a very uncommon word
                # so we add a large number (5000)
                else:
                    sentence_freq += 5000
            frequency_dict[result[1:]] = sentence_freq
            sentence_freq = 0
            return

        for word in A[n]:
            out = result + " " + word
            self.getSentenceCombo(
                A, sentences, frequency_dict, sentence_freq, word_freq, out, n + 1
            )

    @staticmethod
    def getParams() -> Optional[Dict[str, ParamSpec]]:
        return {
            "dict": ParamSpec(
                desc="The Soundex dictionary to use",
                req=False,
                default="cipheydists::translate::soundex",
            ),
            "freq": ParamSpec(
                desc="The word frequency dictionary to use",
                req=False,
                default="cipheydists::list::English5000Freq",
            ),
        }

    def __init__(self, config: Config):
        super().__init__(config)
        self.SOUNDEX_DICT = config.get_resource(self._params()["dict"], Translation)
        self.word_freq = config.get_resource(self._params()["freq"], Translation)
        self.frequency_dict = {}
        self.sentence_freq = 0


Question: What does the `getTarget` method of the `Soundex` class return?

Answer: The `getTarget` method of the `Soundex` class returns the string `"soundex"`. This indicates that the class is designed to crack Soundex-encoded ciphertexts.


Question: How does the `attemptCrack` method of the `Soundex` class attempt to crack a Soundex-encoded ciphertext?

Answer:
The `attemptCrack` method attempts to crack a Soundex-encoded ciphertext by generating all possible word combinations that match the Soundex codes. It does this by:
1. Converting the ciphertext to uppercase and removing delimiters and whitespace
2. Verifying that the ciphertext contains only A-Z and 0-9 characters
3. Verifying that the length of the ciphertext is divisible by 4
4. Splitting the ciphertext into groups of 4 characters
5. Finding all words in a Soundex dictionary that correspond to each group of 4 characters
6. Generating all possible sentences from the matched words
7. Sorting the sentences based on word frequency
8. Returning the sorted sentences as `CrackResult` objects


Question: What does the `sortlistwithdict` method of the `Soundex` class do?

Answer: The `sortlistwithdict` method sorts a list of sentences based on the sum of the frequency ranks of each word in the sentence. It takes two arguments: `listtosort` (the list of sentences to sort) and `hashes` (a dictionary mapping each sentence to its frequency rank sum). It returns the sorted list of sentences.


Question: What parameters does the `Soundex` class take in its constructor?

Answer:
The `Soundex` class takes a `Config` object as a parameter in its constructor. It uses this `Config` object to initialize two attributes:
- `self.SOUNDEX_DICT`: a Soundex dictionary, obtained by calling `config.get_resource(self._params()["dict"], Translation)`
- `self.word_freq`: a list of words ordered by frequency, obtained by calling `config.get_resource(self._params()["freq"], Translation)`

It also initializes two other attributes:
- `self.frequency_dict`: an empty dictionary to store the frequency rank sums of generated sentences
- `self.sentence_freq`: a variable initialized to 0, used to keep track of the current sentence's frequency rank sum


Question: What parameters can be configured for the Tap_code decoder?

Answer:
The Tap_code decoder has one configurable parameter, which is specified in the getParams() static method. The parameter is named "dict" and has the following specification:
```python
"dict": ParamSpec(
    desc="The table of letters used for the tap code interpretation.",
    req=False,
    default="cipheydists::translate::tap_code",
)
```
It represents the lookup table used for Tap code decoding. It is an optional parameter with a default value of "cipheydists::translate::tap_code".

# by https://github.com/RustyDucky and https://github.com/lukasgabriel

from typing import Dict, Optional

from ciphey.iface import Config, Decoder, ParamSpec, T, Translation, U, registry

@registry.register
class Tap_code(Decoder[str]):
    def decode(self, ctext: T) -> Optional[U]:
        """
        Performs Tap code decoding
        """
        try:
            result = ""
            combinations = ctext.split(" ")
            for fragment in combinations:
                result += self.TABLE.get(fragment)
            return result
        except Exception:
            return None

    @staticmethod
    def priority() -> float:
        return 0.06

    def __init__(self, config: Config):
        super().__init__(config)
        self.TABLE = config.get_resource(self._params()["dict"], Translation)

    @staticmethod
    def getParams() -> Optional[Dict[str, ParamSpec]]:
        return {
            "dict": ParamSpec(
                desc="The table of letters used for the tap code interpretation.",
                req=False,
                default="cipheydists::translate::tap_code",
            )
        }

    @staticmethod
    def getTarget() -> str:
        return "tap_code"


Question: How does the CipheyDists class handle configuration?

Answer: The CipheyDists class takes a `Config` object as a parameter in its constructor. It passes this `config` object to the superclass constructor using `super().__init__(config)`. This suggests that the CipheyDists class inherits from a base class that handles configuration, and it forwards the configuration to the superclass.

from functools import lru_cache
from typing import Any, Dict, Optional, Set

import cipheydists
import logging

from ciphey.iface import (
    Config,
    Distribution,
    ParamSpec,
    ResourceLoader,
    Translation,
    WordList,
    registry,
)

@registry.register_multi(WordList, Distribution, Translation)
class CipheyDists(ResourceLoader):
    # _wordlists: Set[str] = frozenset({"english", "english1000", "englishStopWords"})
    # _brandons: Set[str] = frozenset({"english"})
    # _dists: Set[str] = frozenset({"twist"})
    # _translates: Set[str] = frozenset({"morse"})
    _getters = {
        "list": cipheydists.get_list,
        "dist": cipheydists.get_dist,
        "brandon": cipheydists.get_brandon,
        "translate": cipheydists.get_translate,
    }

    def whatResources(self) -> Optional[Set[str]]:
        pass

    @lru_cache()
    def getResource(self, name: str) -> Any:
        logging.debug(f"Loading cipheydists resource {name}")
        prefix, name = name.split("::", 1)
        return self._getters[prefix](name)

    def __init__(self, config: Config):
        super().__init__(config)

    @staticmethod
    def getParams() -> Optional[Dict[str, ParamSpec]]:
        return None


Question: What is the priority method used for in the Base69 class?

Answer: The `priority` method is used to determine the priority of the Base69 decoder. It returns a float value of 0.2, which indicates the priority of this decoder relative to other decoders. The comment suggests that if this value becomes lower than or equal to the reverse (encoding) priority, it may break the decoding process.

# Translated to Python and adapted for Ciphey from the JS original at https://github.com/pshihn/base69

import re
from math import ceil
from typing import Dict, Optional

from ciphey.iface import Config, Decoder, ParamSpec, T, U, WordList, registry

@registry.register
class Base69(Decoder[str]):
    def decode(self, ctext: T) -> Optional[U]:
        """
        Performs Base69 decoding
        """
        # Remove whitespace
        try:
            ctext = re.sub(r"\s+", "", ctext, flags=re.UNICODE)
            extra_bytes = 0
            clen = len(ctext)

            if ctext[:-1] == "=":
                extra_bytes = int(ctext[clen - 2])

            CHUNK_COUNT = ceil(clen / 16)
            result = [0 for _ in range(CHUNK_COUNT * 7 - extra_bytes)]

            for i in range(CHUNK_COUNT):
                chunk_string = ctext[i * 16 : (i + 1) * 16]
                if extra_bytes and (i == CHUNK_COUNT - 1):
                    insert = self.decode_chunk(chunk_string)
                    for n, elem in enumerate(insert[0 : 7 - extra_bytes]):
                        result[n + i * 7] = elem
                else:
                    insert = self.decode_chunk(chunk_string)
                    for n, elem in enumerate(insert):
                        result[n + i * 7] = elem % 256
            return bytearray(result).decode().strip("\x00")
        except Exception:
            return None

    def decode_chunk(self, s: str):
        padded_bytes = s.endswith("=")

        decoded = [0 for _ in range(8)]
        for i in range(8):
            decoded[i] = (
                0
                if i == 7 and padded_bytes
                else self.chars_to_byte(s[i * 2 : i * 2 + 2])
            )

        result = [0 for _ in range(7)]
        for i in range(7):
            t1 = decoded[i] << (i + 1)
            t2 = decoded[i + 1] >> (7 - i - 1)
            result[i] = t1 | t2
        return result

    def chars_to_byte(self, s: str):
        return (69 * self.CHARS.index(s[1])) + (self.CHARS.index(s[0]))

    @staticmethod
    def priority() -> float:
        # If this becomes lower or equal to the reverse, it breaks.
        # So I'll set it to 0.2 for now since it is very fast anyways.
        return 0.2

    def __init__(self, config: Config):
        super().__init__(config)
        self.CHARS = config.get_resource(self._params()["dict"], WordList)

    @staticmethod
    def getParams() -> Optional[Dict[str, ParamSpec]]:
        return {
            "dict": ParamSpec(
                desc="The charset used for the decoder.",
                req=False,
                default="cipheydists::list::base69",
            )
        }

    @staticmethod
    def getTarget() -> str:
        return "base69"


Question: How are the parameters for the Base69 class specified?

Answer: The parameters for the Base69 class are specified using the `getParams` method. It returns a dictionary with a single parameter named "dict". The "dict" parameter is of type `ParamSpec` and specifies the character set (WordList) used for decoding. It has a default value of "cipheydists::list::base69", which is likely a reference to a predefined character set.