baryl 0.0.2

Public SDK for Baryl, a full-system emulation and introspection engine
//! Where a scan looks, and how it reports how far it has got.
//!
//! One [`ScanRange`] serves both the physical scans on `EngRef` and the virtual
//! ones on `ArchRef`, so sweeping physical then virtual memory takes one value
//! spelled once.

use core::ffi::c_void;

use super::ProgressFn;

/// A half-open range of guest addresses, and how far apart the addresses it
/// visits are.
///
/// `end` of 0 means "to the last byte of the space", so the whole of memory is
/// `start: 0, end: 0`. A `stride` of 0 is read as 1.
///
/// # Examples
///
/// ```ignore
/// // Everything, byte by byte.
/// let all = ScanRange::WHOLE;
///
/// // The first 64 MiB.
/// let low = ScanRange::new(0, 0x400_0000);
///
/// // From 1 MiB up, at each 4 KiB page boundary — a page-table sweep.
/// let pages = ScanRange::from(0x10_0000).stride(0x1000);
/// ```
#[derive(Debug, Clone, Copy)]
pub struct ScanRange {
    /// First address visited.
    pub start: u64,
    /// One past the last, or 0 for "to the end of the space".
    pub end: u64,
    /// Distance between visited addresses; 0 is read as 1.
    pub stride: u64,
}

impl ScanRange {
    /// The whole space, every byte.
    pub const WHOLE: ScanRange = ScanRange { start: 0, end: 0, stride: 1 };

    /// `[start, end)`, every byte in it.
    pub fn new(start: u64, end: u64) -> ScanRange {
        ScanRange { start: start, end: end, stride: 1 }
    }

    /// From `start` to the end of the space, every byte.
    pub fn from(start: u64) -> ScanRange {
        ScanRange { start: start, end: 0, stride: 1 }
    }

    /// Visit only addresses `stride` apart, leaving the bounds alone. 0 is read
    /// as 1.
    pub fn stride(mut self, stride: u64) -> ScanRange {
        self.stride = stride;
        self
    }
}

/// A borrowed `FnMut(done, total)` a scan calls as it goes, both counts in
/// bytes.
///
/// Not generic, so a closure of any capture reaches a scan without the call
/// having to be parameterized on it.
pub type ProgressSink<'a> = &'a mut dyn FnMut(u64, u64);

/// Turn an optional progress closure into the `(obj, callback)` pair a scan
/// takes; `None` becomes `(null, None)`, which is how you opt out.
///
/// The pair borrows through `sink`, so `sink` must stay put — and stay
/// untouched — until the scan it was handed to returns.
pub fn progress_thunk(sink: &mut Option<ProgressSink<'_>>) -> (*mut c_void, ProgressFn) {
    match sink {
        None => (core::ptr::null_mut(), None),
        Some(_) => (core::ptr::from_mut(sink).cast(), Some(report)),
    }
}

/// # Safety
/// `obj` is the `Option<ProgressSink>` `progress_thunk` minted the pair from,
/// and the scan that took the pair has not returned.
unsafe extern "C" fn report(obj: *mut c_void, done: u64, total: u64) {
    // SAFETY: the contract above; nothing else holds the sink for this call.
    let sink: &mut Option<ProgressSink<'_>> = unsafe { &mut *obj.cast() };
    if let Some(f) = sink {
        f(done, total);
    }
}