<commit_before>use base::{ImplicitIntervalTree, Item, Node};

use std::mem;

pub trait TraversalDriver<T: Item> {
    fn decide(&mut self, node: &mut Node<T>) -> TraversalDecision;
}

#[derive(Clone, Copy)]
pub struct TraversalDecision {
    pub traverse_left: bool,
    pub traverse_right: bool,
    pub consume_curr: bool
}

struct RejectDriver;
impl<T: Item> TraversalDriver<T> for RejectDriver {
    fn decide(&mut self, node: &mut Node<T>) -> TraversalDecision {
        TraversalDecision { traverse_left: false, traverse_right: false, consume_curr: false }
    }
}

pub struct DeleteBulk<'a, T: 'a+Item> {
    tree: &'a mut ImplicitIntervalTree<T>,
    replacements_min: Vec<T>, replacements_max: Vec<T>,
    pub output: Vec<T>
}

impl<'a, T: Item> DeleteBulk<'a, T> {
    pub fn new(tree: &'a mut ImplicitIntervalTree<T>) -> DeleteBulk<'a, T> {
        let height = tree.node(0).height as usize;
        let replacements_min = Vec::with_capacity(height);
        let replacements_max = Vec::with_capacity(height);
        let output = Vec::with_capacity(tree.len());
        DeleteBulk { tree: tree, replacements_min: replacements_min, replacements_max: replacements_max, output: output }
    }

    pub fn delete_bulk<D: TraversalDriver<T>>(&mut self, drv: &mut D) {
        if !self.tree.is_null(0) {
            self.delete_bulk_recurse(drv, 0, 0, 0);
            assert!(self.replacements_min.is_empty() && self.replacements_max.is_empty());
        }
    }

    fn delete_bulk_recurse<D: TraversalDriver<T>>(&mut self, drv: &mut D, idx: usize,
                                                  min_reqs: usize, max_reqs: usize) {
        let decision = drv.decide(self.tree.node_mut(idx));
        if decision.consume_curr {
            let node = self.tree.node_mut(idx);
            let item = node.item.take().unwrap();
            self.output.push(item);
        }

        match (self.tree.has_left(idx), self.tree.has_right(idx)) {
            (false, false) => {
                self.traverse_shape_leaf(drv, idx, decision, min_reqs, max_reqs)
            }

            (true, false) => {
                self.traverse_shape_left(drv, idx, decision, min_reqs, max_reqs)
            }

            (false, true) => {
                self.traverse_shape_right(drv, idx, decision, min_reqs, max_reqs)
            }

            (true, true) => {
                self.traverse_shape_dual(drv, idx, decision, min_reqs, max_reqs)
            }
        }
    }

    //---- traverse_shape_* ------------------------------------------------------------------------
    #[inline]
    fn traverse_shape_leaf<D: TraversalDriver<T>>(&mut self, drv: &mut D, idx: usize, decision: TraversalDecision,
                                                  min_reqs: usize, max_reqs: usize) {
        if self.traversal_forced(decision, min_reqs, max_reqs) {
            if decision.consume_curr {
                let node = self.tree.node_mut(idx);
                node.height = 0;
            } else if self.replacements_min.len() != min_reqs {
                let node = self.tree.node_mut(idx);
                let item = node.item.take().unwrap();
                self.replacements_min.push(item);
                node.height = 0;
            } else if self.replacements_max.len() != max_reqs {
                let node = self.tree.node_mut(idx);
                let item = node.item.take().unwrap();
                self.replacements_max.push(item);
                node.height = 0;
            }
        }
    }

    #[inline]
    fn traverse_shape_left<D: TraversalDriver<T>>(&mut self, drv: &mut D, idx: usize, decision: TraversalDecision,
                                                  mut min_reqs: usize, mut max_reqs: usize) {
        if self.traversal_forced(decision, min_reqs, max_reqs) || decision.traverse_left {
            if decision.consume_curr {
                max_reqs += 1;
            }

            self.descend_minmax_left(drv, idx, min_reqs, max_reqs);

            // update height
            let height = if self.tree.node(idx).item.is_none() {
                0
            } else {
                let left = self.tree.node_mut(ImplicitIntervalTree::<T>::lefti(idx));
                left.height + 1
            };
            self.tree.node_mut(idx).height = height;
        }
    }

    #[inline]
    fn traverse_shape_right<D: TraversalDriver<T>>(&mut self, drv: &mut D, idx: usize, decision: TraversalDecision,
                                                   mut min_reqs: usize, max_reqs: usize) {
        if decision.traverse_right || self.traversal_forced(decision, min_reqs, max_reqs) {
            if decision.consume_curr {
                min_reqs += 1;
            }

            self.descend_minmax_right(drv, idx, min_reqs, max_reqs);

            // update height
            let height = if self.tree.node(idx).item.is_none() {
                0
            } else {
                let right = self.tree.node_mut(ImplicitIntervalTree::<T>::righti(idx));
                right.height + 1
            };
            self.tree.node_mut(idx).height = height;
        }
    }

    #[inline]
    fn traverse_shape_dual<D: TraversalDriver<T>>(&mut self, drv: &mut D, idx: usize, decision: TraversalDecision,
                                                  mut min_reqs: usize, mut max_reqs: usize) {
        // left subtree
        let max_reqs_left = if decision.consume_curr { 1 } else { 0 };
        if self.traversal_forced(decision, min_reqs, max_reqs_left) || decision.traverse_left {
            let len = self.replacements_max.len();
            let replacements_max_left = Self::subvec(&mut self.replacements_max, len);
            let replacements_max_orig = mem::replace(&mut self.replacements_max, replacements_max_left);
            {
                self.descend_minmax_left(drv, idx, min_reqs, max_reqs_left);
            }
            let replacements_max_left = mem::replace(&mut self.replacements_max, replacements_max_orig);
            mem::forget(replacements_max_left);
        }

        // right subtree
        let min_reqs_right = min_reqs + if self.tree.node(idx).item.is_none() { 1 } else { 0 };
        if decision.traverse_right || self.open_reqs(min_reqs_right, max_reqs) {
            self.descend_minmax_right(drv, idx, min_reqs_right, max_reqs);
        }

        // fulfill the remaining max requests from the left subtree
        {
            max_reqs += if self.tree.node(idx).item.is_none() { 1 } else { 0 };

            if max_reqs != self.replacements_max.len() && self.tree.has_left(idx) {
                self.take_max_left(idx, max_reqs)
            }
        }

        // update height
        if self.tree.node(idx).item.is_none() {
            self.tree.node_mut(idx).height = 0;
        } else {
            self.tree.update_height(idx);
        }
    }

    //---- descend_* -------------------------------------------------------------------------------
    #[inline]
    fn descend_minmax_left<D: TraversalDriver<T>>(&mut self, drv: &mut D, idx: usize,
                                                  min_reqs: usize, max_reqs: usize) {
        self.delete_bulk_recurse(drv, ImplicitIntervalTree::<T>::lefti(idx), min_reqs, max_reqs);

        let replace_max_requested = (max_reqs != self.replacements_max.len());
        if !replace_max_requested {
            let node = self.tree.node_mut(idx);
            if node.item.is_none() {
                node.item = self.replacements_max.pop();
                assert!(node.item.is_some());
            }
        }
    }

    #[inline]
    fn descend_minmax_right<D: TraversalDriver<T>>(&mut self, drv: &mut D, idx: usize,
                                                   min_reqs: usize, max_reqs: usize) {
        self.delete_bulk_recurse(drv, ImplicitIntervalTree::<T>::righti(idx), min_reqs, max_reqs);

        let replace_min_requested = (min_reqs != self.replacements_min.len());
        if !replace_min_requested {
            let node = self.tree.node_mut(idx);
            if node.item.is_none() {
                node.item = self.replacements_min.pop();
                assert!(node.item.is_some());
            }
        }
    }

    #[inline]
    fn take_max_left(&mut self, idx: usize, max_reqs: usize) {
        self.delete_bulk_recurse(&mut RejectDriver, ImplicitIntervalTree::<T>::lefti(idx), 0, max_reqs);
    }

    //---- helpers ---------------------------------------------------------------------------------
    #[inline]
    fn open_reqs(&self, min_reqs: usize, max_reqs: usize) -> bool {
        let replace_min_requested = (min_reqs != self.replacements_min.len());
        let replace_max_requested = (max_reqs != self.replacements_max.len());

        replace_min_requested | replace_max_requested
    }

    #[inline]
    fn traversal_forced(&self, decision: TraversalDecision,
                        min_reqs: usize, max_reqs: usize) -> bool {
        self.open_reqs(min_reqs, max_reqs) | decision.consume_curr
    }

    // Assumes that the returned vec will never be realloc'd!
    #[inline]
    fn subvec(v: &mut Vec<T>, from: usize) -> Vec<T> {
        let ptr = (&mut v[from..]).as_mut_ptr();
        unsafe {
            Vec::from_raw_parts(ptr, v.len() - from, v.capacity() - from)
        }
    }
}
<commit_msg>traverse_shape_left/right correctly push their items on stack in case max/min is requested (respectively)<commit_after>use base::{ImplicitIntervalTree, Item, Node};

use std::mem;

pub trait TraversalDriver<T: Item> {
    fn decide(&mut self, node: &mut Node<T>) -> TraversalDecision;
}

#[derive(Clone, Copy)]
pub struct TraversalDecision {
    pub traverse_left: bool,
    pub traverse_right: bool,
    pub consume_curr: bool
}

struct RejectDriver;
impl<T: Item> TraversalDriver<T> for RejectDriver {
    fn decide(&mut self, node: &mut Node<T>) -> TraversalDecision {
        TraversalDecision { traverse_left: false, traverse_right: false, consume_curr: false }
    }
}

pub struct DeleteBulk<'a, T: 'a+Item> {
    tree: &'a mut ImplicitIntervalTree<T>,
    replacements_min: Vec<T>, replacements_max: Vec<T>,
    pub output: Vec<T>
}

impl<'a, T: Item> DeleteBulk<'a, T> {
    pub fn new(tree: &'a mut ImplicitIntervalTree<T>) -> DeleteBulk<'a, T> {
        let height = tree.node(0).height as usize;
        let replacements_min = Vec::with_capacity(height);
        let replacements_max = Vec::with_capacity(height);
        let output = Vec::with_capacity(tree.len());
        DeleteBulk { tree: tree, replacements_min: replacements_min, replacements_max: replacements_max, output: output }
    }

    pub fn delete_bulk<D: TraversalDriver<T>>(&mut self, drv: &mut D) {
        if !self.tree.is_null(0) {
            self.delete_bulk_recurse(drv, 0, 0, 0);
            assert!(self.replacements_min.is_empty() && self.replacements_max.is_empty());
        }
    }

    fn delete_bulk_recurse<D: TraversalDriver<T>>(&mut self, drv: &mut D, idx: usize,
                                                  min_reqs: usize, max_reqs: usize) {
        let decision = drv.decide(self.tree.node_mut(idx));
        if decision.consume_curr {
            let node = self.tree.node_mut(idx);
            let item = node.item.take().unwrap();
            self.output.push(item);
        }

        match (self.tree.has_left(idx), self.tree.has_right(idx)) {
            (false, false) => {
                self.traverse_shape_leaf(drv, idx, decision, min_reqs, max_reqs)
            }

            (true, false) => {
                self.traverse_shape_left(drv, idx, decision, min_reqs, max_reqs)
            }

            (false, true) => {
                self.traverse_shape_right(drv, idx, decision, min_reqs, max_reqs)
            }

            (true, true) => {
                self.traverse_shape_dual(drv, idx, decision, min_reqs, max_reqs)
            }
        }
    }

    //---- traverse_shape_* ------------------------------------------------------------------------
    #[inline]
    fn traverse_shape_leaf<D: TraversalDriver<T>>(&mut self, drv: &mut D, idx: usize, decision: TraversalDecision,
                                                  min_reqs: usize, max_reqs: usize) {
        if self.traversal_forced(decision, min_reqs, max_reqs) {
            if decision.consume_curr {
                let node = self.tree.node_mut(idx);
                node.height = 0;
            } else if self.replacements_min.len() != min_reqs {
                let node = self.tree.node_mut(idx);
                let item = node.item.take().unwrap();
                self.replacements_min.push(item);
                node.height = 0;
            } else if self.replacements_max.len() != max_reqs {
                let node = self.tree.node_mut(idx);
                let item = node.item.take().unwrap();
                self.replacements_max.push(item);
                node.height = 0;
            }
        }
    }

    #[inline]
    fn traverse_shape_left<D: TraversalDriver<T>>(&mut self, drv: &mut D, idx: usize, decision: TraversalDecision,
                                                  mut min_reqs: usize, mut max_reqs: usize) {
        if self.traversal_forced(decision, min_reqs, max_reqs) || decision.traverse_left {
            if decision.consume_curr {
                max_reqs += 1;
            } else if max_reqs != self.replacements_max.len() {
                max_reqs += 1;
                let node = self.tree.node_mut(idx);
                let item = node.item.take().unwrap();
                self.replacements_max.push(item);
            };

            self.descend_minmax_left(drv, idx, min_reqs, max_reqs);

            // update height
            let height = if self.tree.node(idx).item.is_none() {
                0
            } else {
                let left = self.tree.node_mut(ImplicitIntervalTree::<T>::lefti(idx));
                left.height + 1
            };
            self.tree.node_mut(idx).height = height;
        }
    }

    #[inline]
    fn traverse_shape_right<D: TraversalDriver<T>>(&mut self, drv: &mut D, idx: usize, decision: TraversalDecision,
                                                   mut min_reqs: usize, max_reqs: usize) {
        if decision.traverse_right || self.traversal_forced(decision, min_reqs, max_reqs) {
            if decision.consume_curr {
                min_reqs += 1;
            } else if min_reqs != self.replacements_min.len() {
                min_reqs += 1;
                let node = self.tree.node_mut(idx);
                let item = node.item.take().unwrap();
                self.replacements_min.push(item);
            }

            self.descend_minmax_right(drv, idx, min_reqs, max_reqs);

            // update height
            let height = if self.tree.node(idx).item.is_none() {
                0
            } else {
                let right = self.tree.node_mut(ImplicitIntervalTree::<T>::righti(idx));
                right.height + 1
            };
            self.tree.node_mut(idx).height = height;
        }
    }

    #[inline]
    fn traverse_shape_dual<D: TraversalDriver<T>>(&mut self, drv: &mut D, idx: usize, decision: TraversalDecision,
                                                  mut min_reqs: usize, mut max_reqs: usize) {
        // left subtree
        let max_reqs_left = if decision.consume_curr { 1 } else { 0 };
        if self.traversal_forced(decision, min_reqs, max_reqs_left) || decision.traverse_left {
            let len = self.replacements_max.len();
            let replacements_max_left = Self::subvec(&mut self.replacements_max, len);
            let replacements_max_orig = mem::replace(&mut self.replacements_max, replacements_max_left);
            {
                self.descend_minmax_left(drv, idx, min_reqs, max_reqs_left);
            }
            let replacements_max_left = mem::replace(&mut self.replacements_max, replacements_max_orig);
            mem::forget(replacements_max_left);
        }

        // right subtree
        let min_reqs_right = min_reqs + if self.tree.node(idx).item.is_none() { 1 } else { 0 };
        if decision.traverse_right || self.open_reqs(min_reqs_right, max_reqs) {
            self.descend_minmax_right(drv, idx, min_reqs_right, max_reqs);
        }

        // fulfill the remaining max requests from the left subtree
        {
            max_reqs += if self.tree.node(idx).item.is_none() { 1 } else { 0 };

            if max_reqs != self.replacements_max.len() && self.tree.has_left(idx) {
                self.take_max_left(idx, max_reqs)
            }
        }

        // update height
        if self.tree.node(idx).item.is_none() {
            self.tree.node_mut(idx).height = 0;
        } else {
            self.tree.update_height(idx);
        }
    }

    //---- descend_* -------------------------------------------------------------------------------
    #[inline]
    fn descend_minmax_left<D: TraversalDriver<T>>(&mut self, drv: &mut D, idx: usize,
                                                  min_reqs: usize, max_reqs: usize) {
        self.delete_bulk_recurse(drv, ImplicitIntervalTree::<T>::lefti(idx), min_reqs, max_reqs);

        let replace_max_requested = (max_reqs != self.replacements_max.len());
        if !replace_max_requested {
            let node = self.tree.node_mut(idx);
            if node.item.is_none() {
                node.item = self.replacements_max.pop();
                assert!(node.item.is_some());
            }
        }
    }

    #[inline]
    fn descend_minmax_right<D: TraversalDriver<T>>(&mut self, drv: &mut D, idx: usize,
                                                   min_reqs: usize, max_reqs: usize) {
        self.delete_bulk_recurse(drv, ImplicitIntervalTree::<T>::righti(idx), min_reqs, max_reqs);

        let replace_min_requested = (min_reqs != self.replacements_min.len());
        if !replace_min_requested {
            let node = self.tree.node_mut(idx);
            if node.item.is_none() {
                node.item = self.replacements_min.pop();
                assert!(node.item.is_some());
            }
        }
    }

    #[inline]
    fn take_max_left(&mut self, idx: usize, max_reqs: usize) {
        self.delete_bulk_recurse(&mut RejectDriver, ImplicitIntervalTree::<T>::lefti(idx), 0, max_reqs);
    }

    //---- helpers ---------------------------------------------------------------------------------
    #[inline]
    fn open_reqs(&self, min_reqs: usize, max_reqs: usize) -> bool {
        let replace_min_requested = (min_reqs != self.replacements_min.len());
        let replace_max_requested = (max_reqs != self.replacements_max.len());

        replace_min_requested | replace_max_requested
    }

    #[inline]
    fn traversal_forced(&self, decision: TraversalDecision,
                        min_reqs: usize, max_reqs: usize) -> bool {
        self.open_reqs(min_reqs, max_reqs) | decision.consume_curr
    }

    // Assumes that the returned vec will never be realloc'd!
    #[inline]
    fn subvec(v: &mut Vec<T>, from: usize) -> Vec<T> {
        let ptr = (&mut v[from..]).as_mut_ptr();
        unsafe {
            Vec::from_raw_parts(ptr, v.len() - from, v.capacity() - from)
        }
    }
}
<|endoftext|>


<commit_before>use std::cell::RefCell;

use rustc_span::DUMMY_SP;

use crate::*;

/// Details of premature program termination.
pub enum TerminationInfo {
    Exit(i64),
    Abort(Option<String>),
    UnsupportedInIsolation(String),
}

/// Miri specific diagnostics
pub enum NonHaltingDiagnostic {
    PoppedTrackedPointerTag(Item),
    CreatedAlloc(AllocId),
}

/// Emit a custom diagnostic without going through the miri-engine machinery
pub fn report_error<'tcx, 'mir>(
    ecx: &InterpCx<'mir, 'tcx, Evaluator<'tcx>>,
    mut e: InterpErrorInfo<'tcx>,
) -> Option<i64> {
    use InterpError::*;

    e.print_backtrace();
    let (title, msg, help) = match e.kind {
        MachineStop(info) => {
            let info = info.downcast_ref::<TerminationInfo>().expect("invalid MachineStop payload");
            use TerminationInfo::*;
            let (title, msg) = match info {
                Exit(code) => return Some(*code),
                Abort(None) =>
                    ("abnormal termination", format!("the evaluated program aborted execution")),
                Abort(Some(msg)) =>
                    ("abnormal termination", format!("the evaluated program aborted execution: {}", msg)),
                UnsupportedInIsolation(msg) =>
                    ("unsupported operation", format!("{}", msg)),
            };
            let help = match info {
                UnsupportedInIsolation(_) =>
                    Some("pass the flag `-Zmiri-disable-isolation` to disable isolation"),
                _ => None,
            };
            (title, msg, help)
        }
        _ => {
            let (title, msg) = match e.kind {
                Unsupported(_) =>
                    ("unsupported operation", e.to_string()),
                UndefinedBehavior(_) =>
                    ("Undefined Behavior", e.to_string()),
                ResourceExhaustion(_) =>
                    ("resource exhaustion", e.to_string()),
                _ =>
                    bug!("This error should be impossible in Miri: {}", e),
            };
            let help = match e.kind {
                Unsupported(UnsupportedOpInfo::NoMirFor(..)) =>
                    Some("set `MIRI_SYSROOT` to a Miri sysroot, which you can prepare with `cargo miri setup`"),
                Unsupported(_) =>
                    Some("this is likely not a bug in the program; it indicates that the program performed an operation that the interpreter does not support"),
                UndefinedBehavior(UndefinedBehaviorInfo::UbExperimental(_)) =>
                    Some("this indicates a potential bug in the program: it performed an invalid operation, but the rules it violated are still experimental"),
                UndefinedBehavior(_) =>
                    Some("this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior"),
                _ => None,
            };
            (title, msg, help)
        }
    };
    report_msg(ecx, &format!("{}: {}", title, msg), msg, help, true)
}

/// Report an error or note (depending on the `error` argument) at the current frame's current statement.
/// Also emits a full stacktrace of the interpreter stack.
fn report_msg<'tcx, 'mir>(
    ecx: &InterpCx<'mir, 'tcx, Evaluator<'tcx>>,
    title: &str,
    span_msg: String,
    help: Option<&str>,
    error: bool,
) -> Option<i64> {
    let span = if let Some(frame) = ecx.stack().last() {
        frame.current_source_info().unwrap().span
    } else {
        DUMMY_SP
    };
    let mut err = if error {
        ecx.tcx.sess.struct_span_err(span, title)
    } else {
        ecx.tcx.sess.diagnostic().span_note_diag(span, title)
    };
    err.span_label(span, span_msg);
    if let Some(help) = help {
        err.help(help);
    }
    // Add backtrace
    let frames = ecx.generate_stacktrace(None);
    // We iterate with indices because we need to look at the next frame (the caller).
    for idx in 0..frames.len() {
        let frame_info = &frames[idx];
        let call_site_is_local = frames
            .get(idx + 1)
            .map_or(false, |caller_info| caller_info.instance.def_id().is_local());
        if call_site_is_local {
            err.span_note(frame_info.call_site, &frame_info.to_string());
        } else {
            err.note(&frame_info.to_string());
        }
    }

    err.emit();

    for (i, frame) in ecx.stack().iter().enumerate() {
        trace!("-------------------");
        trace!("Frame {}", i);
        trace!("    return: {:?}", frame.return_place.map(|p| *p));
        for (i, local) in frame.locals.iter().enumerate() {
            trace!("    local {}: {:?}", i, local.value);
        }
    }
    // Let the reported error determine the return code.
    return None;
}

thread_local! {
    static DIAGNOSTICS: RefCell<Vec<NonHaltingDiagnostic>> = RefCell::new(Vec::new());
}

/// Schedule a diagnostic for emitting. This function works even if you have no `InterpCx` available.
/// The diagnostic will be emitted after the current interpreter step is finished.
pub fn register_diagnostic(e: NonHaltingDiagnostic) {
    DIAGNOSTICS.with(|diagnostics| diagnostics.borrow_mut().push(e));
}

impl<'mir, 'tcx> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
    /// Emit all diagnostics that were registed with `register_diagnostics`
    fn process_diagnostics(&self) {
        let this = self.eval_context_ref();
        DIAGNOSTICS.with(|diagnostics| {
            for e in diagnostics.borrow_mut().drain(..) {
                use NonHaltingDiagnostic::*;
                let msg = match e {
                    PoppedTrackedPointerTag(item) =>
                        format!("popped tracked tag for item {:?}", item),
                    CreatedAlloc(AllocId(id)) =>
                        format!("created allocation with id {}", id),
                };
                report_msg(this, "tracking was triggered", msg, None, false);
            }
        });
    }
}
<commit_msg>fix outdated sysroot help message<commit_after>use std::cell::RefCell;

use rustc_span::DUMMY_SP;

use crate::*;

/// Details of premature program termination.
pub enum TerminationInfo {
    Exit(i64),
    Abort(Option<String>),
    UnsupportedInIsolation(String),
}

/// Miri specific diagnostics
pub enum NonHaltingDiagnostic {
    PoppedTrackedPointerTag(Item),
    CreatedAlloc(AllocId),
}

/// Emit a custom diagnostic without going through the miri-engine machinery
pub fn report_error<'tcx, 'mir>(
    ecx: &InterpCx<'mir, 'tcx, Evaluator<'tcx>>,
    mut e: InterpErrorInfo<'tcx>,
) -> Option<i64> {
    use InterpError::*;

    e.print_backtrace();
    let (title, msg, help) = match e.kind {
        MachineStop(info) => {
            let info = info.downcast_ref::<TerminationInfo>().expect("invalid MachineStop payload");
            use TerminationInfo::*;
            let (title, msg) = match info {
                Exit(code) => return Some(*code),
                Abort(None) =>
                    ("abnormal termination", format!("the evaluated program aborted execution")),
                Abort(Some(msg)) =>
                    ("abnormal termination", format!("the evaluated program aborted execution: {}", msg)),
                UnsupportedInIsolation(msg) =>
                    ("unsupported operation", format!("{}", msg)),
            };
            let help = match info {
                UnsupportedInIsolation(_) =>
                    Some("pass the flag `-Zmiri-disable-isolation` to disable isolation"),
                _ => None,
            };
            (title, msg, help)
        }
        _ => {
            let (title, msg) = match e.kind {
                Unsupported(_) =>
                    ("unsupported operation", e.to_string()),
                UndefinedBehavior(_) =>
                    ("Undefined Behavior", e.to_string()),
                ResourceExhaustion(_) =>
                    ("resource exhaustion", e.to_string()),
                _ =>
                    bug!("This error should be impossible in Miri: {}", e),
            };
            let help = match e.kind {
                Unsupported(UnsupportedOpInfo::NoMirFor(..)) =>
                    Some("make sure to use a Miri sysroot, which you can prepare with `cargo miri setup`"),
                Unsupported(_) =>
                    Some("this is likely not a bug in the program; it indicates that the program performed an operation that the interpreter does not support"),
                UndefinedBehavior(UndefinedBehaviorInfo::UbExperimental(_)) =>
                    Some("this indicates a potential bug in the program: it performed an invalid operation, but the rules it violated are still experimental"),
                UndefinedBehavior(_) =>
                    Some("this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior"),
                _ => None,
            };
            (title, msg, help)
        }
    };
    report_msg(ecx, &format!("{}: {}", title, msg), msg, help, true)
}

/// Report an error or note (depending on the `error` argument) at the current frame's current statement.
/// Also emits a full stacktrace of the interpreter stack.
fn report_msg<'tcx, 'mir>(
    ecx: &InterpCx<'mir, 'tcx, Evaluator<'tcx>>,
    title: &str,
    span_msg: String,
    help: Option<&str>,
    error: bool,
) -> Option<i64> {
    let span = if let Some(frame) = ecx.stack().last() {
        frame.current_source_info().unwrap().span
    } else {
        DUMMY_SP
    };
    let mut err = if error {
        ecx.tcx.sess.struct_span_err(span, title)
    } else {
        ecx.tcx.sess.diagnostic().span_note_diag(span, title)
    };
    err.span_label(span, span_msg);
    if let Some(help) = help {
        err.help(help);
    }
    // Add backtrace
    let frames = ecx.generate_stacktrace(None);
    // We iterate with indices because we need to look at the next frame (the caller).
    for idx in 0..frames.len() {
        let frame_info = &frames[idx];
        let call_site_is_local = frames
            .get(idx + 1)
            .map_or(false, |caller_info| caller_info.instance.def_id().is_local());
        if call_site_is_local {
            err.span_note(frame_info.call_site, &frame_info.to_string());
        } else {
            err.note(&frame_info.to_string());
        }
    }

    err.emit();

    for (i, frame) in ecx.stack().iter().enumerate() {
        trace!("-------------------");
        trace!("Frame {}", i);
        trace!("    return: {:?}", frame.return_place.map(|p| *p));
        for (i, local) in frame.locals.iter().enumerate() {
            trace!("    local {}: {:?}", i, local.value);
        }
    }
    // Let the reported error determine the return code.
    return None;
}

thread_local! {
    static DIAGNOSTICS: RefCell<Vec<NonHaltingDiagnostic>> = RefCell::new(Vec::new());
}

/// Schedule a diagnostic for emitting. This function works even if you have no `InterpCx` available.
/// The diagnostic will be emitted after the current interpreter step is finished.
pub fn register_diagnostic(e: NonHaltingDiagnostic) {
    DIAGNOSTICS.with(|diagnostics| diagnostics.borrow_mut().push(e));
}

impl<'mir, 'tcx> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
    /// Emit all diagnostics that were registed with `register_diagnostics`
    fn process_diagnostics(&self) {
        let this = self.eval_context_ref();
        DIAGNOSTICS.with(|diagnostics| {
            for e in diagnostics.borrow_mut().drain(..) {
                use NonHaltingDiagnostic::*;
                let msg = match e {
                    PoppedTrackedPointerTag(item) =>
                        format!("popped tracked tag for item {:?}", item),
                    CreatedAlloc(AllocId(id)) =>
                        format!("created allocation with id {}", id),
                };
                report_msg(this, "tracking was triggered", msg, None, false);
            }
        });
    }
}
<|endoftext|>


<commit_before>use std::iter;
use std::iter::FromIterator;

pub type BinOp = Fn(i32, i32) -> i32;

pub struct Fenwick<'a> {
    vals: Vec<i32>,
    operator: &'a BinOp,
    identity: i32,
}

impl<'a> Fenwick<'a> {
    pub fn new(size: usize, identity: i32, operator: &'a BinOp) -> Fenwick<'a> {
        Fenwick {
            vals: Vec::from_iter(iter::repeat(identity).take(size + 1)),
            operator: operator,
            identity: identity,
        }
    }

    fn bounds_check(index: usize, size: usize) {
        if index == 0 {
            panic!("index must be > 0 and <= size");
        }
        if index > size {
            panic!("index must be > 0 and <= size");
        }
    }

    pub fn update(&mut self, mut index: usize, value: i32) {
        let size = self.vals.len();
        let op = self.operator;
        Fenwick::bounds_check(index, size);
        while index <= size {
            self.vals[index] = op(self.vals[index], value);
            index += index & (!index + 1);
        }
    }

    pub fn get(&mut self, mut index: usize) -> i32 {
        let mut res = self.identity;
        let op = self.operator;
        Fenwick::bounds_check(index, self.vals.len());
        while index > 0 {
            res = op(res, self.vals[index]);
            index -= index & (!index + 1);
        }
        res
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn bitflip() {
        let val: usize = 24;
        assert_eq!(8, val & (!val + 1));
    }

    #[test]
    fn insert() {
        let add = &|a, b| a + b;
        let mut tree = Fenwick::new(10, 0, add);
        for i in 1..11 {
            tree.update(i as usize, i);
        }
        let mut sum = 0;
        for i in 1..11 {
            sum += i;
            assert_eq!(sum, tree.get(i as usize));
        }
    }
}
<commit_msg>Fenwick tree use 0-based indexing for API.<commit_after>use std::iter;
use std::iter::FromIterator;

pub type BinOp = Fn(i32, i32) -> i32;

pub struct Fenwick<'a> {
    vals: Vec<i32>,
    operator: &'a BinOp,
    identity: i32,
}

impl<'a> Fenwick<'a> {
    pub fn new(size: usize, identity: i32, operator: &'a BinOp) -> Fenwick<'a> {
        Fenwick {
            vals: Vec::from_iter(iter::repeat(identity).take(size + 1)),
            operator: operator,
            identity: identity,
        }
    }

    fn bounds_check(index: usize, size: usize) {
        if index >= size {
            panic!("index must less than size");
        }
    }

    pub fn update(&mut self, mut index: usize, value: i32) {
        let size = self.vals.len();
        let op = self.operator;
        Fenwick::bounds_check(index, size);
        index += 1;
        while index <= size {
            self.vals[index] = op(self.vals[index], value);
            index += index & (!index + 1);
        }
    }

    pub fn get(&mut self, mut index: usize) -> i32 {
        let mut res = self.identity;
        let op = self.operator;
        Fenwick::bounds_check(index, self.vals.len());
        index += 1;
        while index > 0 {
            res = op(res, self.vals[index]);
            index -= index & (!index + 1);
        }
        res
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn bitflip() {
        let val: usize = 24;
        assert_eq!(8, val & (!val + 1));
    }

    #[test]
    fn insert() {
        let add = &|a, b| a + b;
        let mut tree = Fenwick::new(10, 0, add);
        for i in 0..10 {
            tree.update(i as usize, i);
        }
        let mut sum = 0;
        for i in 0..10 {
            sum += i;
            assert_eq!(sum, tree.get(i as usize));
        }
    }
}
<|endoftext|>


<commit_before>//! Entry point for the CSS filters infrastructure.

use cssparser::{BasicParseError, Parser};
use markup5ever::{expanded_name, local_name, namespace_url, ns};
use std::time::Instant;

use crate::bbox::BoundingBox;
use crate::document::AcquiredNodes;
use crate::drawing_ctx::DrawingCtx;
use crate::element::{Draw, ElementResult, SetAttributes};
use crate::error::{ElementError, ParseError, RenderingError};
use crate::length::*;
use crate::node::{Node, NodeBorrow};
use crate::paint_server::UserSpacePaintSource;
use crate::parsers::{CustomIdent, Parse, ParseValue};
use crate::properties::ComputedValues;
use crate::property_defs::ColorInterpolationFilters;
use crate::surface_utils::shared_surface::{SharedImageSurface, SurfaceType};
use crate::transform::Transform;
use crate::xml::Attributes;

mod bounds;
use self::bounds::BoundsBuilder;

pub mod context;
use self::context::{FilterContext, FilterOutput, FilterResult};

mod error;
use self::error::FilterError;

/// A filter primitive interface.
pub trait FilterEffect: SetAttributes + Draw {
    fn resolve(&self, node: &Node) -> Result<(Primitive, PrimitiveParams), FilterError>;
}

// Filter Effects do not need to draw themselves
impl<T: FilterEffect> Draw for T {}

pub mod blend;
pub mod color_matrix;
pub mod component_transfer;
pub mod composite;
pub mod convolve_matrix;
pub mod displacement_map;
pub mod flood;
pub mod gaussian_blur;
pub mod image;
pub mod lighting;
pub mod merge;
pub mod morphology;
pub mod offset;
pub mod tile;
pub mod turbulence;

/// Resolved parameters for each filter primitive.
///
/// These gather all the data that a primitive may need during rendering:
/// the `feFoo` element's attributes, any computed values from its properties,
/// and parameters extracted from the element's children (for example,
/// `feMerge` gathers info from its `feMergNode` children).
pub enum PrimitiveParams {
    Blend(blend::Blend),
    ColorMatrix(color_matrix::ColorMatrix),
    ComponentTransfer(component_transfer::ComponentTransfer),
    Composite(composite::Composite),
    ConvolveMatrix(convolve_matrix::ConvolveMatrix),
    DiffuseLighting(lighting::DiffuseLighting),
    DisplacementMap(displacement_map::DisplacementMap),
    Flood(flood::Flood),
    GaussianBlur(gaussian_blur::GaussianBlur),
    Image(image::Image),
    Merge(merge::Merge),
    Morphology(morphology::Morphology),
    Offset(offset::Offset),
    SpecularLighting(lighting::SpecularLighting),
    Tile(tile::Tile),
    Turbulence(turbulence::Turbulence),
}

/// The base filter primitive node containing common properties.
#[derive(Default, Clone)]
pub struct Primitive {
    x: Option<Length<Horizontal>>,
    y: Option<Length<Vertical>>,
    width: Option<ULength<Horizontal>>,
    height: Option<ULength<Vertical>>,
    result: Option<CustomIdent>,
}

/// Common properties for filter primitives, with all lengths in user space coordinates.
pub struct UserSpacePrimitive {
    x: Option<f64>,
    y: Option<f64>,
    width: Option<f64>,
    height: Option<f64>,
    result: Option<CustomIdent>,
}

/// An enumeration of possible inputs for a filter primitive.
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub enum Input {
    Unspecified,
    SourceGraphic,
    SourceAlpha,
    BackgroundImage,
    BackgroundAlpha,
    FillPaint,
    StrokePaint,
    FilterOutput(CustomIdent),
}

enum_default!(Input, Input::Unspecified);

impl Parse for Input {
    fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<Self, ParseError<'i>> {
        parser
            .try_parse(|p| {
                parse_identifiers!(
                    p,
                    "SourceGraphic" => Input::SourceGraphic,
                    "SourceAlpha" => Input::SourceAlpha,
                    "BackgroundImage" => Input::BackgroundImage,
                    "BackgroundAlpha" => Input::BackgroundAlpha,
                    "FillPaint" => Input::FillPaint,
                    "StrokePaint" => Input::StrokePaint,
                )
            })
            .or_else(|_: BasicParseError<'_>| {
                let ident = CustomIdent::parse(parser)?;
                Ok(Input::FilterOutput(ident))
            })
    }
}

impl Primitive {
    fn to_user_space(
        &self,
        ctx: &FilterContext,
        values: &ComputedValues,
        draw_ctx: &DrawingCtx,
    ) -> UserSpacePrimitive {
        let params = draw_ctx.push_coord_units(ctx.primitive_units());

        let x = self.x.map(|l| l.normalize(values, &params));
        let y = self.y.map(|l| l.normalize(values, &params));
        let width = self.width.map(|l| l.normalize(values, &params));
        let height = self.height.map(|l| l.normalize(values, &params));

        UserSpacePrimitive {
            x,
            y,
            width,
            height,
            result: self.result.clone(),
        }
    }
}

impl UserSpacePrimitive {
    /// Validates attributes and returns the `BoundsBuilder` for bounds computation.
    #[inline]
    fn get_bounds(&self, ctx: &FilterContext) -> BoundsBuilder {
        BoundsBuilder::new(self.x, self.y, self.width, self.height, ctx.paffine())
    }
}

impl Primitive {
    fn parse_standard_attributes(
        &mut self,
        attrs: &Attributes,
    ) -> Result<(Input, Input), ElementError> {
        let mut input_1 = Input::Unspecified;
        let mut input_2 = Input::Unspecified;

        for (attr, value) in attrs.iter() {
            match attr.expanded() {
                expanded_name!("", "x") => self.x = attr.parse(value)?,
                expanded_name!("", "y") => self.y = attr.parse(value)?,
                expanded_name!("", "width") => self.width = attr.parse(value)?,
                expanded_name!("", "height") => self.height = attr.parse(value)?,
                expanded_name!("", "result") => self.result = attr.parse(value)?,
                expanded_name!("", "in") => input_1 = attr.parse(value)?,
                expanded_name!("", "in2") => input_2 = attr.parse(value)?,
                _ => (),
            }
        }

        Ok((input_1, input_2))
    }

    pub fn parse_no_inputs(&mut self, attrs: &Attributes) -> ElementResult {
        let (_, _) = self.parse_standard_attributes(attrs)?;
        Ok(())
    }

    pub fn parse_one_input(&mut self, attrs: &Attributes) -> Result<Input, ElementError> {
        let (input_1, _) = self.parse_standard_attributes(attrs)?;
        Ok(input_1)
    }

    pub fn parse_two_inputs(&mut self, attrs: &Attributes) -> Result<(Input, Input), ElementError> {
        self.parse_standard_attributes(attrs)
    }
}

/// Applies a filter and returns the resulting surface.
pub fn render(
    filter_node: &Node,
    stroke_paint_source: UserSpacePaintSource,
    fill_paint_source: UserSpacePaintSource,
    source_surface: SharedImageSurface,
    acquired_nodes: &mut AcquiredNodes<'_>,
    draw_ctx: &mut DrawingCtx,
    transform: Transform,
    node_bbox: BoundingBox,
) -> Result<SharedImageSurface, RenderingError> {
    let filter_node = &*filter_node;
    assert!(is_element_of_type!(filter_node, Filter));

    let primitive_nodes: Vec<Node> = filter_node
        .children()
        .filter(|c| c.is_element())
        // Skip nodes in error.
        .filter(|c| {
            let in_error = c.borrow_element().is_in_error();

            if in_error {
                rsvg_log!("(ignoring filter primitive {} because it is in error)", c);
            }

            !in_error
        })
        // Keep only filter primitives (those that implement the Filter trait)
        .filter(|c| c.borrow_element().as_filter_effect().is_some())
        .map(|n| n.clone())
        .collect();

    let filter_element = filter_node.borrow_element();

    if filter_element.is_in_error() {
        return Ok(source_surface);
    }

    let filter_values = filter_element.get_computed_values();

    let filter = borrow_element_as!(filter_node, Filter);

    let resolved_filter = {
        // This is in a temporary scope so we don't leave the coord_units pushed during
        // the execution of all the filter primitives.
        let params = draw_ctx.push_coord_units(filter.get_filter_units());
        filter.to_user_space(filter_values, &params)
    };

    if let Ok(mut filter_ctx) = FilterContext::new(
        &resolved_filter,
        stroke_paint_source,
        fill_paint_source,
        &source_surface,
        transform,
        node_bbox,
    ) {
        for primitive_node in primitive_nodes {
            let elt = primitive_node.borrow_element();
            let effect = elt.as_filter_effect().unwrap();

            let primitive_values = elt.get_computed_values();

            let primitive_name = format!("{}", primitive_node);

            let start = Instant::now();

            if let Err(err) = effect
                .resolve(&primitive_node)
                .and_then(|(primitive, params)| {
                    let user_space_primitive =
                        primitive.to_user_space(&filter_ctx, primitive_values, draw_ctx);

                    let output = render_primitive(
                        &user_space_primitive,
                        &params,
                        &filter_ctx,
                        acquired_nodes,
                        draw_ctx,
                    )?;

                    Ok(FilterResult {
                        name: user_space_primitive.result,
                        output,
                    })
                })
                .and_then(|result| filter_ctx.store_result(result))
            {
                rsvg_log!(
                    "(filter primitive {} returned an error: {})",
                    primitive_name,
                    err
                );

                // Exit early on Cairo errors. Continue rendering otherwise.
                if let FilterError::CairoError(status) = err {
                    return Err(RenderingError::from(status));
                }
            }

            let elapsed = start.elapsed();
            rsvg_log!(
                "(rendered filter primitive {} in\n    {} seconds)",
                primitive_name,
                elapsed.as_secs() as f64 + f64::from(elapsed.subsec_nanos()) / 1e9
            );
        }

        Ok(filter_ctx.into_output()?)
    } else {
        // Ignore errors that happened when creating the FilterContext
        Ok(SharedImageSurface::empty(
            source_surface.width(),
            source_surface.height(),
            SurfaceType::AlphaOnly,
        )?)
    }
}

#[rustfmt::skip]
fn render_primitive(
    primitive: &UserSpacePrimitive,
    params: &PrimitiveParams,
    ctx: &FilterContext,
    acquired_nodes: &mut AcquiredNodes<'_>,
    draw_ctx: &mut DrawingCtx,
) -> Result<FilterOutput, FilterError> {
    use PrimitiveParams::*;

    let bounds_builder = primitive.get_bounds(ctx);

    match params {
        Blend(p)             => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
        ColorMatrix(p)       => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
        ComponentTransfer(p) => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
        Composite(p)         => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
        ConvolveMatrix(p)    => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
        DiffuseLighting(p)   => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
        DisplacementMap(p)   => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
        Flood(p)             => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
        GaussianBlur(p)      => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
        Image(p)             => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
        Merge(p)             => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
        Morphology(p)        => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
        Offset(p)            => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
        SpecularLighting(p)  => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
        Tile(p)              => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
        Turbulence(p)        => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
    }
}

impl From<ColorInterpolationFilters> for SurfaceType {
    fn from(c: ColorInterpolationFilters) -> Self {
        match c {
            ColorInterpolationFilters::LinearRgb => SurfaceType::LinearRgb,
            _ => SurfaceType::SRgb,
        }
    }
}
<commit_msg>Only print a primtive's elapsed time if it succeeds<commit_after>//! Entry point for the CSS filters infrastructure.

use cssparser::{BasicParseError, Parser};
use markup5ever::{expanded_name, local_name, namespace_url, ns};
use std::time::Instant;

use crate::bbox::BoundingBox;
use crate::document::AcquiredNodes;
use crate::drawing_ctx::DrawingCtx;
use crate::element::{Draw, ElementResult, SetAttributes};
use crate::error::{ElementError, ParseError, RenderingError};
use crate::length::*;
use crate::node::{Node, NodeBorrow};
use crate::paint_server::UserSpacePaintSource;
use crate::parsers::{CustomIdent, Parse, ParseValue};
use crate::properties::ComputedValues;
use crate::property_defs::ColorInterpolationFilters;
use crate::surface_utils::shared_surface::{SharedImageSurface, SurfaceType};
use crate::transform::Transform;
use crate::xml::Attributes;

mod bounds;
use self::bounds::BoundsBuilder;

pub mod context;
use self::context::{FilterContext, FilterOutput, FilterResult};

mod error;
use self::error::FilterError;

/// A filter primitive interface.
pub trait FilterEffect: SetAttributes + Draw {
    fn resolve(&self, node: &Node) -> Result<(Primitive, PrimitiveParams), FilterError>;
}

// Filter Effects do not need to draw themselves
impl<T: FilterEffect> Draw for T {}

pub mod blend;
pub mod color_matrix;
pub mod component_transfer;
pub mod composite;
pub mod convolve_matrix;
pub mod displacement_map;
pub mod flood;
pub mod gaussian_blur;
pub mod image;
pub mod lighting;
pub mod merge;
pub mod morphology;
pub mod offset;
pub mod tile;
pub mod turbulence;

/// Resolved parameters for each filter primitive.
///
/// These gather all the data that a primitive may need during rendering:
/// the `feFoo` element's attributes, any computed values from its properties,
/// and parameters extracted from the element's children (for example,
/// `feMerge` gathers info from its `feMergNode` children).
pub enum PrimitiveParams {
    Blend(blend::Blend),
    ColorMatrix(color_matrix::ColorMatrix),
    ComponentTransfer(component_transfer::ComponentTransfer),
    Composite(composite::Composite),
    ConvolveMatrix(convolve_matrix::ConvolveMatrix),
    DiffuseLighting(lighting::DiffuseLighting),
    DisplacementMap(displacement_map::DisplacementMap),
    Flood(flood::Flood),
    GaussianBlur(gaussian_blur::GaussianBlur),
    Image(image::Image),
    Merge(merge::Merge),
    Morphology(morphology::Morphology),
    Offset(offset::Offset),
    SpecularLighting(lighting::SpecularLighting),
    Tile(tile::Tile),
    Turbulence(turbulence::Turbulence),
}

/// The base filter primitive node containing common properties.
#[derive(Default, Clone)]
pub struct Primitive {
    x: Option<Length<Horizontal>>,
    y: Option<Length<Vertical>>,
    width: Option<ULength<Horizontal>>,
    height: Option<ULength<Vertical>>,
    result: Option<CustomIdent>,
}

/// Common properties for filter primitives, with all lengths in user space coordinates.
pub struct UserSpacePrimitive {
    x: Option<f64>,
    y: Option<f64>,
    width: Option<f64>,
    height: Option<f64>,
    result: Option<CustomIdent>,
}

/// An enumeration of possible inputs for a filter primitive.
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub enum Input {
    Unspecified,
    SourceGraphic,
    SourceAlpha,
    BackgroundImage,
    BackgroundAlpha,
    FillPaint,
    StrokePaint,
    FilterOutput(CustomIdent),
}

enum_default!(Input, Input::Unspecified);

impl Parse for Input {
    fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<Self, ParseError<'i>> {
        parser
            .try_parse(|p| {
                parse_identifiers!(
                    p,
                    "SourceGraphic" => Input::SourceGraphic,
                    "SourceAlpha" => Input::SourceAlpha,
                    "BackgroundImage" => Input::BackgroundImage,
                    "BackgroundAlpha" => Input::BackgroundAlpha,
                    "FillPaint" => Input::FillPaint,
                    "StrokePaint" => Input::StrokePaint,
                )
            })
            .or_else(|_: BasicParseError<'_>| {
                let ident = CustomIdent::parse(parser)?;
                Ok(Input::FilterOutput(ident))
            })
    }
}

impl Primitive {
    fn to_user_space(
        &self,
        ctx: &FilterContext,
        values: &ComputedValues,
        draw_ctx: &DrawingCtx,
    ) -> UserSpacePrimitive {
        let params = draw_ctx.push_coord_units(ctx.primitive_units());

        let x = self.x.map(|l| l.normalize(values, &params));
        let y = self.y.map(|l| l.normalize(values, &params));
        let width = self.width.map(|l| l.normalize(values, &params));
        let height = self.height.map(|l| l.normalize(values, &params));

        UserSpacePrimitive {
            x,
            y,
            width,
            height,
            result: self.result.clone(),
        }
    }
}

impl UserSpacePrimitive {
    /// Validates attributes and returns the `BoundsBuilder` for bounds computation.
    #[inline]
    fn get_bounds(&self, ctx: &FilterContext) -> BoundsBuilder {
        BoundsBuilder::new(self.x, self.y, self.width, self.height, ctx.paffine())
    }
}

impl Primitive {
    fn parse_standard_attributes(
        &mut self,
        attrs: &Attributes,
    ) -> Result<(Input, Input), ElementError> {
        let mut input_1 = Input::Unspecified;
        let mut input_2 = Input::Unspecified;

        for (attr, value) in attrs.iter() {
            match attr.expanded() {
                expanded_name!("", "x") => self.x = attr.parse(value)?,
                expanded_name!("", "y") => self.y = attr.parse(value)?,
                expanded_name!("", "width") => self.width = attr.parse(value)?,
                expanded_name!("", "height") => self.height = attr.parse(value)?,
                expanded_name!("", "result") => self.result = attr.parse(value)?,
                expanded_name!("", "in") => input_1 = attr.parse(value)?,
                expanded_name!("", "in2") => input_2 = attr.parse(value)?,
                _ => (),
            }
        }

        Ok((input_1, input_2))
    }

    pub fn parse_no_inputs(&mut self, attrs: &Attributes) -> ElementResult {
        let (_, _) = self.parse_standard_attributes(attrs)?;
        Ok(())
    }

    pub fn parse_one_input(&mut self, attrs: &Attributes) -> Result<Input, ElementError> {
        let (input_1, _) = self.parse_standard_attributes(attrs)?;
        Ok(input_1)
    }

    pub fn parse_two_inputs(&mut self, attrs: &Attributes) -> Result<(Input, Input), ElementError> {
        self.parse_standard_attributes(attrs)
    }
}

/// Applies a filter and returns the resulting surface.
pub fn render(
    filter_node: &Node,
    stroke_paint_source: UserSpacePaintSource,
    fill_paint_source: UserSpacePaintSource,
    source_surface: SharedImageSurface,
    acquired_nodes: &mut AcquiredNodes<'_>,
    draw_ctx: &mut DrawingCtx,
    transform: Transform,
    node_bbox: BoundingBox,
) -> Result<SharedImageSurface, RenderingError> {
    let filter_node = &*filter_node;
    assert!(is_element_of_type!(filter_node, Filter));

    let primitive_nodes: Vec<Node> = filter_node
        .children()
        .filter(|c| c.is_element())
        // Skip nodes in error.
        .filter(|c| {
            let in_error = c.borrow_element().is_in_error();

            if in_error {
                rsvg_log!("(ignoring filter primitive {} because it is in error)", c);
            }

            !in_error
        })
        // Keep only filter primitives (those that implement the Filter trait)
        .filter(|c| c.borrow_element().as_filter_effect().is_some())
        .map(|n| n.clone())
        .collect();

    let filter_element = filter_node.borrow_element();

    if filter_element.is_in_error() {
        return Ok(source_surface);
    }

    let filter_values = filter_element.get_computed_values();

    let filter = borrow_element_as!(filter_node, Filter);

    let resolved_filter = {
        // This is in a temporary scope so we don't leave the coord_units pushed during
        // the execution of all the filter primitives.
        let params = draw_ctx.push_coord_units(filter.get_filter_units());
        filter.to_user_space(filter_values, &params)
    };

    if let Ok(mut filter_ctx) = FilterContext::new(
        &resolved_filter,
        stroke_paint_source,
        fill_paint_source,
        &source_surface,
        transform,
        node_bbox,
    ) {
        for primitive_node in primitive_nodes {
            let elt = primitive_node.borrow_element();
            let effect = elt.as_filter_effect().unwrap();

            let primitive_values = elt.get_computed_values();

            let primitive_name = format!("{}", primitive_node);

            let start = Instant::now();

            if let Err(err) = effect
                .resolve(&primitive_node)
                .and_then(|(primitive, params)| {
                    let user_space_primitive =
                        primitive.to_user_space(&filter_ctx, primitive_values, draw_ctx);

                    let output = render_primitive(
                        &user_space_primitive,
                        &params,
                        &filter_ctx,
                        acquired_nodes,
                        draw_ctx,
                    )?;

                    let elapsed = start.elapsed();
                    rsvg_log!(
                        "(rendered filter primitive {} in\n    {} seconds)",
                        primitive_name,
                        elapsed.as_secs() as f64 + f64::from(elapsed.subsec_nanos()) / 1e9
                    );

                    Ok(FilterResult {
                        name: user_space_primitive.result,
                        output,
                    })
                })
                .and_then(|result| filter_ctx.store_result(result))
            {
                rsvg_log!(
                    "(filter primitive {} returned an error: {})",
                    primitive_name,
                    err
                );

                // Exit early on Cairo errors. Continue rendering otherwise.
                if let FilterError::CairoError(status) = err {
                    return Err(RenderingError::from(status));
                }
            }
        }

        Ok(filter_ctx.into_output()?)
    } else {
        // Ignore errors that happened when creating the FilterContext
        Ok(SharedImageSurface::empty(
            source_surface.width(),
            source_surface.height(),
            SurfaceType::AlphaOnly,
        )?)
    }
}

#[rustfmt::skip]
fn render_primitive(
    primitive: &UserSpacePrimitive,
    params: &PrimitiveParams,
    ctx: &FilterContext,
    acquired_nodes: &mut AcquiredNodes<'_>,
    draw_ctx: &mut DrawingCtx,
) -> Result<FilterOutput, FilterError> {
    use PrimitiveParams::*;

    let bounds_builder = primitive.get_bounds(ctx);

    match params {
        Blend(p)             => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
        ColorMatrix(p)       => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
        ComponentTransfer(p) => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
        Composite(p)         => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
        ConvolveMatrix(p)    => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
        DiffuseLighting(p)   => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
        DisplacementMap(p)   => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
        Flood(p)             => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
        GaussianBlur(p)      => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
        Image(p)             => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
        Merge(p)             => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
        Morphology(p)        => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
        Offset(p)            => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
        SpecularLighting(p)  => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
        Tile(p)              => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
        Turbulence(p)        => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
    }
}

impl From<ColorInterpolationFilters> for SurfaceType {
    fn from(c: ColorInterpolationFilters) -> Self {
        match c {
            ColorInterpolationFilters::LinearRgb => SurfaceType::LinearRgb,
            _ => SurfaceType::SRgb,
        }
    }
}
<|endoftext|>


<commit_before>
use byteorder::{ByteOrder, LittleEndian};

use std;

use gameboy;
use gameboy::{Interconnect, Interrupt, Irq, Memory};
use gameboy::frame::{Color, Frame};

const VRAM_SIZE: usize = 0x4000;
const SPRITE_DATA_SIZE: usize = 0xA0;

pub struct GpuStat {
    pub coincidence_interrupt_enabled: bool,
    pub OAM_interrupt_enabled: bool,
    pub VBlank_interrupt_enabled: bool,
    pub HBlank_interrupt_enabled: bool,
}

impl GpuStat {
    pub fn new() -> GpuStat {
        GpuStat {
            coincidence_interrupt_enabled: false,
            OAM_interrupt_enabled: false,
            VBlank_interrupt_enabled: false,
            HBlank_interrupt_enabled: false,
        }
    }

    pub fn from_u8(b: u8) -> GpuStat {
        GpuStat {
            coincidence_interrupt_enabled: b & 0x40 == 0x40,
            OAM_interrupt_enabled: b & 0x20 == 0x20,
            VBlank_interrupt_enabled: b & 0x10 == 0x10,
            HBlank_interrupt_enabled: b & 0x08 == 0x08,
        }
    }

    pub fn to_u8(&self, gpu: &Gpu) -> u8 {
        (if self.coincidence_interrupt_enabled {
            0x40
        } else {
            0
        }) | (if self.OAM_interrupt_enabled { 0x20 } else { 0 }) |
        (if self.VBlank_interrupt_enabled {
            0x10
        } else {
            0
        }) |
        (if self.HBlank_interrupt_enabled {
            0x08
        } else {
            0
        }) | (if gpu.ly == gpu.lyc { 0x04 } else { 0 }) | gpu.mode.to_u8()
    }
}

#[derive(Eq, PartialEq, Clone)]
pub enum GpuMode {
    HBlank,
    VBlank,
    SearchingRam,
    TransferringData,
}

impl GpuMode {
    fn to_u8(&self) -> u8 {
        match *self {
            GpuMode::HBlank => 0x00,
            GpuMode::VBlank => 0x01,
            GpuMode::SearchingRam => 0x02,
            GpuMode::TransferringData => 0x03,
        }
    }
}

pub enum SpriteShape {
    Square,
    Rectangle,
}

pub struct Gpu {
    pub enabled: bool,
    pub ram: Memory,
    pub sprite_data: Memory,
    control_register: u8,
    stat: GpuStat,
    scroll_y: u8,
    scroll_x: u8,
    window_y: u8,
    window_x: u8,
    pub ly: u8,
    lyc: u8,
    bg_palette: u8,
    palette0: u8,
    palette1: u8,

    cycles: isize,

    pub frame: Frame,
    pub backbuffer: Frame,
    mode: GpuMode,

    counter: u8,
    tile_base: usize,
    background_base: usize,
    sprite_shape: SpriteShape,
}

impl Gpu {
    pub fn new() -> Gpu {
        Gpu {
            enabled: true,
            ram: Memory::new(VRAM_SIZE),
            sprite_data: Memory::new(SPRITE_DATA_SIZE),
            control_register: 0x00,
            stat: GpuStat::new(),
            scroll_y: 0x00,
            scroll_x: 0x00,
            window_y: 0x00,
            window_x: 0x00,
            ly: 0x00,
            lyc: 0x00,
            bg_palette: 0x00,
            palette0: 0x00,
            palette1: 0x00,
            cycles: 0x00,
            frame: Frame::new(),
            backbuffer: Frame::new(),
            mode: GpuMode::HBlank,
            counter: 0,
            tile_base: 0x00,
            background_base: 0xC00,
            sprite_shape: SpriteShape::Square,
        }
    }

    pub fn reset(&mut self) {
        *self = Gpu::new();
    }

    fn set_status(&mut self, irq: &mut Irq) {
        if !self.enabled {
            self.cycles = 0x1C8;
            self.ly = 0x00;
            self.stat = GpuStat::from_u8(0xFD);
        }

        let current_mode = self.mode.clone();

        if self.ly >= 0x90 {
            self.mode = GpuMode::VBlank;
        } else {
            if self.cycles >= 0x178 {
                self.mode = GpuMode::SearchingRam;
            } else if self.cycles >= 0xCC {
                self.mode = GpuMode::TransferringData;
            } else {
                self.mode = GpuMode::HBlank;
            }
        }

        if self.mode != current_mode {
            irq.request(Interrupt::Lcd);
        }

        self.check_coincidence(irq);
    }

    pub fn step(&mut self, irq: &mut Irq, cycles: usize) -> Result<(), String> {
        self.set_status(irq);

        let cycles = cycles as isize;
        if self.enabled {
            self.cycles -= cycles;
        } else {
            return Ok(());
        }

        if self.cycles <= 0 {
            self.ly += 0x01;

            self.cycles = 0x1C8;

            if self.ly == 0x90 {
                irq.request(Interrupt::Vblank);
            } else if self.ly > 0x99 {
                self.ly = 0x00;
            } else if self.ly < 0x90 {
                self.render_scanline();
            }
        }

        Ok(())
    }

    fn render_scanline(&mut self) {
        let line = self.ly.wrapping_add(self.scroll_y) as usize;
        // self.clear_scanline(line);
        self.render_background(line);
        self.render_sprites(line);
        self.frame = self.backbuffer.clone();
    }

    fn clear_scanline(&mut self, line: usize) {
        for i in 0..160 {
            self.backbuffer.pixels[line * 160 + i] = Color::new(0xFF, 0xFF, 0xFF, 0xFF);
        }
    }

    fn render_background(&mut self, line: usize) {
        let bg_base = self.background_base;
        let tile_base = self.tile_base;
        let bg_map_row = (line / 0x08) as usize;
        for i in 0..gameboy::SCREEN_WIDTH {
            let x = (i as u8).wrapping_add(self.scroll_x);
            let bg_map_col = (x / 8) as usize;
            let raw_tile_number = self.ram[bg_base + (bg_map_row * 0x20 + bg_map_col)] as usize;

            let line_offset = (line % 0x08) << 0x01;

            let tile_data_start = tile_base +
                                  (if tile_base == 0x00 {
                raw_tile_number
            } else {
                (raw_tile_number as i8 as i16 + 0x80) as usize
            }) * 0x10 + line_offset;

            let x_shift = (x % 8).wrapping_sub(0x07).wrapping_mul(0xFF);
            let tile_data1 = (self.ram[tile_data_start] >> x_shift) & 0x01;
            let tile_data2 = (self.ram[tile_data_start + 0x01] >> x_shift) & 0x01;
            let total_row_data = (tile_data2 << 1) | tile_data1;
            let color_value = total_row_data;
            let c = self.get_background_color_for_byte(color_value as u8);
            self.backbuffer.pixels[self.ly as usize * 160 + i as usize] = c;
        }
    }

    fn render_sprites(&mut self, line: usize) {
        for i in 0..40 {
            let sprite_table_entry_base = i * 0x04;
            let s_y = self.sprite_data[sprite_table_entry_base] as i16 - 0x10;
            let s_x = self.sprite_data[sprite_table_entry_base + 0x01] as i16 - 0x08;
            let sprite_height = if let SpriteShape::Rectangle = self.sprite_shape {
                0x10
            } else {
                0x08
            };

            // Ignore this scanline if the sprite doesn't sit within it
            // Should we be casting to usize here? If the number is negative the sign bit
            // will be throwing this calculation off entirely - perhaps this is why Kirby
            // disappears when sprite Y position is negative?
            if s_y as usize <= line && (s_y as usize + sprite_height) > line {
                let tile_number = self.sprite_data[sprite_table_entry_base + 0x02] as i16;
                let attributes = self.sprite_data[sprite_table_entry_base + 0x03];
                let above_background = attributes & 0x80 == 0x00;
                let flip_y = attributes & 0x40 == 0x40;
                let flip_x = attributes & 0x20 == 0x20;

                let sprite_y = (line as i16 - s_y) << 0x01;
                let tile_data_start = (tile_number * 0x10) + sprite_y;
                for x in 0..8 {
                    if s_x + x < 0 || s_x + x >= 160 {
                        continue;
                    }
                    let shift = if flip_x { x } else { 0x07 - x };
                    let tile_data1 = (self.ram[tile_data_start as usize] >> shift) & 0x01;
                    let tile_data2 = (self.ram[tile_data_start as usize + 0x01] >> shift) & 0x01;
                    let total_row_data = (tile_data2 << 1) | tile_data1;
                    let color_value = total_row_data;
                    if color_value == 0x00 {
                        continue;
                    }
                    let c = self.get_sprite_color_for_byte(color_value as u8,
                                                           ((attributes & 0x10) >> 0x04) as u8);
                    self.backbuffer.pixels[line as usize * 160 + (s_x as usize + x as usize)] = c;
                }
            }
        }
    }

    fn get_background_color_for_byte(&self, b: u8) -> Color {
        let palette_index = match b {
            0x00 => self.bg_palette & 0x0003,
            0x01 => (self.bg_palette & 0x000C) >> 0x02,
            0x02 => (self.bg_palette & 0x0030) >> 0x04,
            0x03 => (self.bg_palette & 0x00C0) >> 0x06,
            _ => panic!("err: invalid pixel color value found"),
        };

        Color::from_dmg_byte(palette_index as u8)
    }

    fn get_sprite_color_for_byte(&self, b: u8, palette_entry: u8) -> Color {
        let palette = if palette_entry == 0x00 {
            self.palette0
        } else {
            self.palette1
        };

        let palette_index = match b {
            0x00 => palette & 0x0003,
            0x01 => (palette & 0x000C) >> 0x02,
            0x02 => (palette & 0x0030) >> 0x04,
            0x03 => (palette & 0x00C0) >> 0x06,
            _ => panic!("err: invalid pixel color value found"),
        };

        Color::from_dmg_byte(palette_index as u8)
    }

    pub fn read_u8(&self, addr: u16) -> u8 {
        match addr {
            0x40 => self.control_register,
            0x41 => self.stat.to_u8(self),
            0x42 => self.scroll_y,
            0x43 => self.scroll_x,
            0x4A => self.window_y,
            0x4B => self.window_x,
            0x44 => self.ly,
            0x45 => self.lyc,
            0x47 => self.bg_palette,
            0x48 => self.palette0,
            0x49 => self.palette1,
            _ => panic!("tried to read GPU memory that is not mapped"),
        }
    }

    pub fn write_u8(&mut self, addr: u16, val: u8) {
        match addr {
            0x40 => {
                self.control_register = val;
                self.enabled = self.control_register & 0x80 == 0x80;
                self.tile_base = if self.control_register & 0x10 == 0x10 {
                    0x00
                } else {
                    0x800
                };
                self.background_base = if self.control_register & 0x08 == 0x08 {
                    0x1C00
                } else {
                    0x1800
                };
                self.sprite_shape = if self.control_register & 0x04 == 0x04 {
                    SpriteShape::Rectangle
                } else {
                    SpriteShape::Square
                };
            }
            0x41 => self.stat = GpuStat::from_u8(val),
            0x42 => self.scroll_y = val,
            0x43 => self.scroll_x = val,
            0x44 => (),
            0x45 => {
                self.lyc = val;
            }
            0x47 => self.bg_palette = val,
            0x48 => self.palette0 = val,
            0x49 => self.palette1 = val,
            0x4A => self.window_y = val,
            0x4B => self.window_x = val,
            _ => panic!("tried to write GPU memory that is not mapped: {:04}", addr),
        }
    }

    pub fn write_u16(&mut self, addr: u16, val: u16) {
        let addr = addr as usize;
        LittleEndian::write_u16(&mut self.ram[addr..], val)
    }

    fn check_coincidence(&mut self, irq: &mut Irq) {
        // If LY == LYC then set the coincidence flag
        if self.lyc == self.ly && self.stat.coincidence_interrupt_enabled {
            irq.request(Interrupt::Lcd);
        }
    }
}
<commit_msg>Fix up sprite rendering issues caused by casting negative numbers to unsigned integers<commit_after>
use byteorder::{ByteOrder, LittleEndian};

use std;

use gameboy;
use gameboy::{Interconnect, Interrupt, Irq, Memory};
use gameboy::frame::{Color, Frame};

const VRAM_SIZE: usize = 0x4000;
const SPRITE_DATA_SIZE: usize = 0xA0;

pub struct GpuStat {
    pub coincidence_interrupt_enabled: bool,
    pub OAM_interrupt_enabled: bool,
    pub VBlank_interrupt_enabled: bool,
    pub HBlank_interrupt_enabled: bool,
}

impl GpuStat {
    pub fn new() -> GpuStat {
        GpuStat {
            coincidence_interrupt_enabled: false,
            OAM_interrupt_enabled: false,
            VBlank_interrupt_enabled: false,
            HBlank_interrupt_enabled: false,
        }
    }

    pub fn from_u8(b: u8) -> GpuStat {
        GpuStat {
            coincidence_interrupt_enabled: b & 0x40 == 0x40,
            OAM_interrupt_enabled: b & 0x20 == 0x20,
            VBlank_interrupt_enabled: b & 0x10 == 0x10,
            HBlank_interrupt_enabled: b & 0x08 == 0x08,
        }
    }

    pub fn to_u8(&self, gpu: &Gpu) -> u8 {
        (if self.coincidence_interrupt_enabled {
            0x40
        } else {
            0
        }) | (if self.OAM_interrupt_enabled { 0x20 } else { 0 }) |
        (if self.VBlank_interrupt_enabled {
            0x10
        } else {
            0
        }) |
        (if self.HBlank_interrupt_enabled {
            0x08
        } else {
            0
        }) | (if gpu.ly == gpu.lyc { 0x04 } else { 0 }) | gpu.mode.to_u8()
    }
}

#[derive(Eq, PartialEq, Clone)]
pub enum GpuMode {
    HBlank,
    VBlank,
    SearchingRam,
    TransferringData,
}

impl GpuMode {
    fn to_u8(&self) -> u8 {
        match *self {
            GpuMode::HBlank => 0x00,
            GpuMode::VBlank => 0x01,
            GpuMode::SearchingRam => 0x02,
            GpuMode::TransferringData => 0x03,
        }
    }
}

pub enum SpriteShape {
    Square,
    Rectangle,
}

pub struct Gpu {
    pub enabled: bool,
    pub ram: Memory,
    pub sprite_data: Memory,
    control_register: u8,
    stat: GpuStat,
    scroll_y: u8,
    scroll_x: u8,
    window_y: u8,
    window_x: u8,
    pub ly: u8,
    lyc: u8,
    bg_palette: u8,
    palette0: u8,
    palette1: u8,

    cycles: isize,

    pub frame: Frame,
    pub backbuffer: Frame,
    mode: GpuMode,

    counter: u8,
    tile_base: usize,
    background_base: usize,
    sprite_shape: SpriteShape,
}

impl Gpu {
    pub fn new() -> Gpu {
        Gpu {
            enabled: true,
            ram: Memory::new(VRAM_SIZE),
            sprite_data: Memory::new(SPRITE_DATA_SIZE),
            control_register: 0x00,
            stat: GpuStat::new(),
            scroll_y: 0x00,
            scroll_x: 0x00,
            window_y: 0x00,
            window_x: 0x00,
            ly: 0x00,
            lyc: 0x00,
            bg_palette: 0x00,
            palette0: 0x00,
            palette1: 0x00,
            cycles: 0x00,
            frame: Frame::new(),
            backbuffer: Frame::new(),
            mode: GpuMode::HBlank,
            counter: 0,
            tile_base: 0x00,
            background_base: 0xC00,
            sprite_shape: SpriteShape::Square,
        }
    }

    pub fn reset(&mut self) {
        *self = Gpu::new();
    }

    fn set_status(&mut self, irq: &mut Irq) {
        if !self.enabled {
            self.cycles = 0x1C8;
            self.ly = 0x00;
            self.stat = GpuStat::from_u8(0xFD);
        }

        let current_mode = self.mode.clone();

        if self.ly >= 0x90 {
            self.mode = GpuMode::VBlank;
        } else {
            if self.cycles >= 0x178 {
                self.mode = GpuMode::SearchingRam;
            } else if self.cycles >= 0xCC {
                self.mode = GpuMode::TransferringData;
            } else {
                self.mode = GpuMode::HBlank;
            }
        }

        if self.mode != current_mode {
            irq.request(Interrupt::Lcd);
        }

        self.check_coincidence(irq);
    }

    pub fn step(&mut self, irq: &mut Irq, cycles: usize) -> Result<(), String> {
        self.set_status(irq);

        let cycles = cycles as isize;
        if self.enabled {
            self.cycles -= cycles;
        } else {
            return Ok(());
        }

        if self.cycles <= 0 {
            self.ly += 0x01;

            self.cycles = 0x1C8;

            if self.ly == 0x90 {
                irq.request(Interrupt::Vblank);
            } else if self.ly > 0x99 {
                self.ly = 0x00;
            } else if self.ly < 0x90 {
                self.render_scanline();
            }
        }

        Ok(())
    }

    fn render_scanline(&mut self) {
        let line = self.ly.wrapping_add(self.scroll_y) as usize;
        // self.clear_scanline(line);
        self.render_background(line);
        self.render_sprites(line);
        self.frame = self.backbuffer.clone();
    }

    fn clear_scanline(&mut self, line: usize) {
        for i in 0..160 {
            self.backbuffer.pixels[line * 160 + i] = Color::new(0xFF, 0xFF, 0xFF, 0xFF);
        }
    }

    fn render_background(&mut self, line: usize) {
        let bg_base = self.background_base;
        let tile_base = self.tile_base;
        let bg_map_row = (line / 0x08) as usize;
        for i in 0..gameboy::SCREEN_WIDTH {
            let x = (i as u8).wrapping_add(self.scroll_x);
            let bg_map_col = (x / 8) as usize;
            let raw_tile_number = self.ram[bg_base + (bg_map_row * 0x20 + bg_map_col)] as usize;

            let line_offset = (line % 0x08) << 0x01;

            let tile_data_start = tile_base +
                                  (if tile_base == 0x00 {
                raw_tile_number
            } else {
                (raw_tile_number as i8 as i16 + 0x80) as usize
            }) * 0x10 + line_offset;

            let x_shift = (x % 8).wrapping_sub(0x07).wrapping_mul(0xFF);
            let tile_data1 = (self.ram[tile_data_start] >> x_shift) & 0x01;
            let tile_data2 = (self.ram[tile_data_start + 0x01] >> x_shift) & 0x01;
            let total_row_data = (tile_data2 << 1) | tile_data1;
            let color_value = total_row_data;
            let c = self.get_background_color_for_byte(color_value as u8);
            self.backbuffer.pixels[self.ly as usize * 160 + i as usize] = c;
        }
    }

    fn render_sprites(&mut self, line: usize) {
        for i in 0..40 {
            let sprite_table_entry_base = i * 0x04;
            let s_y = self.sprite_data[sprite_table_entry_base] as i16 - 0x10;
            let s_x = self.sprite_data[sprite_table_entry_base + 0x01] as i16 - 0x08;
            let sprite_height = if let SpriteShape::Rectangle = self.sprite_shape {
                0x10
            } else {
                0x08
            };

            if s_y <= line as i16 && (s_y + sprite_height as i16) > line as i16 {
                let tile_number = self.sprite_data[sprite_table_entry_base + 0x02] as i16;
                let attributes = self.sprite_data[sprite_table_entry_base + 0x03];
                let above_background = attributes & 0x80 == 0x00;
                let flip_y = attributes & 0x40 == 0x40;
                let flip_x = attributes & 0x20 == 0x20;

                let sprite_y = (line as i16 - s_y) << 0x01;
                let tile_data_start = (tile_number * 0x10) + sprite_y;
                for x in 0..8 {
                    if s_x + x < 0 || s_x + x >= 160 {
                        continue;
                    }
                    let shift = if flip_x { x } else { 0x07 - x };
                    let tile_data1 = (self.ram[tile_data_start as usize] >> shift) & 0x01;
                    let tile_data2 = (self.ram[tile_data_start as usize + 0x01] >> shift) & 0x01;
                    let total_row_data = (tile_data2 << 1) | tile_data1;
                    let color_value = total_row_data;
                    if color_value == 0x00 {
                        continue;
                    }
                    let c = self.get_sprite_color_for_byte(color_value as u8,
                                                           ((attributes & 0x10) >> 0x04) as u8);
                    self.backbuffer.pixels[line as usize * 160 + (s_x as usize + x as usize)] = c;
                }
            }
        }
    }

    fn get_background_color_for_byte(&self, b: u8) -> Color {
        let palette_index = match b {
            0x00 => self.bg_palette & 0x0003,
            0x01 => (self.bg_palette & 0x000C) >> 0x02,
            0x02 => (self.bg_palette & 0x0030) >> 0x04,
            0x03 => (self.bg_palette & 0x00C0) >> 0x06,
            _ => panic!("err: invalid pixel color value found"),
        };

        Color::from_dmg_byte(palette_index as u8)
    }

    fn get_sprite_color_for_byte(&self, b: u8, palette_entry: u8) -> Color {
        let palette = if palette_entry == 0x00 {
            self.palette0
        } else {
            self.palette1
        };

        let palette_index = match b {
            0x00 => palette & 0x0003,
            0x01 => (palette & 0x000C) >> 0x02,
            0x02 => (palette & 0x0030) >> 0x04,
            0x03 => (palette & 0x00C0) >> 0x06,
            _ => panic!("err: invalid pixel color value found"),
        };

        Color::from_dmg_byte(palette_index as u8)
    }

    pub fn read_u8(&self, addr: u16) -> u8 {
        match addr {
            0x40 => self.control_register,
            0x41 => self.stat.to_u8(self),
            0x42 => self.scroll_y,
            0x43 => self.scroll_x,
            0x4A => self.window_y,
            0x4B => self.window_x,
            0x44 => self.ly,
            0x45 => self.lyc,
            0x47 => self.bg_palette,
            0x48 => self.palette0,
            0x49 => self.palette1,
            _ => panic!("tried to read GPU memory that is not mapped"),
        }
    }

    pub fn write_u8(&mut self, addr: u16, val: u8) {
        match addr {
            0x40 => {
                self.control_register = val;
                self.enabled = self.control_register & 0x80 == 0x80;
                self.tile_base = if self.control_register & 0x10 == 0x10 {
                    0x00
                } else {
                    0x800
                };
                self.background_base = if self.control_register & 0x08 == 0x08 {
                    0x1C00
                } else {
                    0x1800
                };
                self.sprite_shape = if self.control_register & 0x04 == 0x04 {
                    SpriteShape::Rectangle
                } else {
                    SpriteShape::Square
                };
            }
            0x41 => self.stat = GpuStat::from_u8(val),
            0x42 => self.scroll_y = val,
            0x43 => self.scroll_x = val,
            0x44 => (),
            0x45 => {
                self.lyc = val;
            }
            0x47 => self.bg_palette = val,
            0x48 => self.palette0 = val,
            0x49 => self.palette1 = val,
            0x4A => self.window_y = val,
            0x4B => self.window_x = val,
            _ => panic!("tried to write GPU memory that is not mapped: {:04}", addr),
        }
    }

    pub fn write_u16(&mut self, addr: u16, val: u16) {
        let addr = addr as usize;
        LittleEndian::write_u16(&mut self.ram[addr..], val)
    }

    fn check_coincidence(&mut self, irq: &mut Irq) {
        // If LY == LYC then set the coincidence flag
        if self.lyc == self.ly && self.stat.coincidence_interrupt_enabled {
            irq.request(Interrupt::Lcd);
        }
    }
}
<|endoftext|>