<commit_before>//! Bounded package merge algorithm, based on the paper
//! "A Fast and Space-Economical Algorithm for Length-Limited Coding
//! Jyrki Katajainen, Alistair Moffat, Andrew Turpin".

use std::cell::RefCell;
use std::mem;
use std::rc::Rc;

use libc::{c_void, size_t};

/// Nodes forming chains. Also used to represent leaves.
struct Node {
    /// Total weight (symbol count) of this chain.
    weight: usize,
    /// Previous node(s) of this chain, or 0 if none.
    tail: Option<Rc<RefCell<Node>>>,
    /// Leaf symbol index, or number of leaves before this chain.
    count: i32,
    /// Tracking for garbage collection.
    in_use: bool,
}

/// Memory pool for nodes.
struct NodePool {
    /// The pool.
    nodes: Vec<Rc<RefCell<Node>>>,
    /// Index of a possibly free node in the pool.
    next: u32,
    /// Size of the memory pool.
    size: u32,
}

impl NodePool {
    fn new(pool_size: u32) -> NodePool {
        NodePool {
            nodes: (0..pool_size).map(|_| {
                let node = Node {
                    weight: 0,
                    tail: None,
                    count: 0,
                    in_use: false,
                };
                Rc::new(RefCell::new(node))
            }).collect(),
            next: 0,
            size: pool_size,
        }
    }
}

/// Initializes a chain node with the given values and marks it as in use.
fn init_node(weight: usize, count: i32, tail: Option<Rc<RefCell<Node>>>, node: Rc<RefCell<Node>>) {
    let mut node = node.borrow_mut();
    node.weight = weight;
    node.count = count;
    node.tail = tail;
    node.in_use = true;
}

/**
 * Finds a free location in the memory pool. Performs garbage collection if needed.
 * lists: If given, used to mark in-use nodes during garbage collection.
 * maxbits: Size of lists.
 * pool: Memory pool to get free node from.
 */
fn get_free_node(lists: Option<&Vec<[Option<Rc<RefCell<Node>>>; 2]>>,
                 maxbits: i32,
                 pool: &mut NodePool)
                 -> Rc<RefCell<Node>> {
    loop {
        if pool.next >= pool.size {
            // Garbage collection
            for i in 0..pool.size {
                pool.nodes[i as usize].borrow_mut().in_use = false;
            }
            if let Some(lists) = lists {
                for i in 0..maxbits * 2 {
                    let mut node = lists[(i / 2) as usize][(i % 2) as usize].clone();
                    while let Some(n) = node {
                        let mut n = n.borrow_mut();
                        n.in_use = true;
                        node = n.tail.clone();
                    }
                }
            }
            pool.next = 0;
        }
        if !pool.nodes[pool.next as usize].borrow().in_use {
            break;
        }
        pool.next += 1;
    }
    let free_node = pool.nodes[pool.next as usize].clone();

    pool.next += 1;
    free_node
}

/**
 * Performs a Boundary Package-Merge step. Puts a new chain in the given list. The
 * new chain is, depending on the weights, a leaf or a combination of two chains
 * from the previous list.
 * lists: The lists of chains.
 * maxbits: Number of lists.
 * leaves: The leaves, one per symbol.
 * numsymbols: Number of leaves.
 * pool: the node memory pool.
 * index: The index of the list in which a new chain or leaf is required.
 * final: Whether this is the last time this function is called. If it is then it
 *   is no more needed to recursively call self.
 */
fn boundary_pm(lists: &mut Vec<[Option<Rc<RefCell<Node>>>; 2]>,
               maxbits: i32,
               leaves: &[Node],
               numsymbols: i32,
               pool: &mut NodePool,
               index: i32,
               is_final: bool) {
    let lastcount = lists[index as usize][1].as_ref().unwrap().borrow().count; // Count of last chain of list.

    if index == 0 && lastcount >= numsymbols {
        return;
    }

    let newchain = get_free_node(Some(&lists), maxbits, pool);
    let oldchain = lists[index as usize][1].clone();

    // These are set up before the recursive calls below, so that there is a list
    // pointing to the new node, to let the garbage collection know it's in use.
    lists[index as usize][0] = oldchain.clone();
    lists[index as usize][1] = Some(newchain.clone());

    if index == 0 {
        // New leaf node in list 0.
        init_node(leaves[lastcount as usize].weight,
                  lastcount + 1,
                  None,
                  newchain);
    } else {
        let sum = lists[(index - 1) as usize][0].as_ref().unwrap().borrow().weight +
                  lists[(index - 1) as usize][1].as_ref().unwrap().borrow().weight;
        if lastcount < numsymbols && sum > leaves[lastcount as usize].weight {
            // New leaf inserted in list, so count is incremented.
            init_node(leaves[lastcount as usize].weight,
                      lastcount + 1,
                      oldchain.as_ref().unwrap().borrow().tail.clone(),
                      newchain);
        } else {
            init_node(sum,
                      lastcount,
                      lists[(index - 1) as usize][1].clone(),
                      newchain);
            if !is_final {
                // Two lookahead chains of previous list used up, create new ones.
                boundary_pm(lists, maxbits, leaves, numsymbols, pool, index - 1, false);
                boundary_pm(lists, maxbits, leaves, numsymbols, pool, index - 1, false);
            }
        }
    }
}

/// Initializes each list with as lookahead chains the two leaves with lowest weights.
fn init_lists(pool: &mut NodePool,
              leaves: &[Node],
              maxbits: i32) -> Vec<[Option<Rc<RefCell<Node>>>; 2]> {
    let node0 = get_free_node(None, maxbits, pool);
    let node1 = get_free_node(None, maxbits, pool);
    init_node(leaves[0].weight, 1, None, node0.clone());
    init_node(leaves[1].weight, 2, None, node1.clone());
    let mut result = Vec::new();
    for _ in 0..maxbits {
        result.push([Some(node0.clone()), Some(node1.clone())]);
    }
    result
}

/**
 * Converts result of boundary package-merge to the bitlengths. The result in the
 * last chain of the last list contains the amount of active leaves in each list.
 * chain: Chain to extract the bit length from (last chain from last list).
 */
fn extract_bit_lengths(chain: Option<Rc<RefCell<Node>>>, leaves: &[Node], bitlengths: &mut [u32]) {
    let mut node = chain;
    while let Some(n) = node {
        for i in 0..n.borrow().count {
            bitlengths[leaves[i as usize].count as usize] =
                bitlengths[leaves[i as usize].count as usize] + 1;
        }
        node = n.borrow().tail.clone();
    }
}

/// Comparator for sorting the leaves. Has the function signature for qsort.
extern "C" fn leaf_comparator(a: *const c_void, b: *const c_void) -> i32 {
    unsafe {
        let node_a: *const Node = a as *const Node;
        let node_b: *const Node = b as *const Node;
        (*node_a).weight as i32 - (*node_b).weight as i32
    }
}

#[link(name="c")]
extern {
    fn qsort(base: *mut c_void,
             nmemb: size_t,
             size: size_t,
             compar: extern "C" fn(*const c_void, *const c_void) -> i32);
}

pub unsafe fn length_limited_code_lengths(frequencies: &[usize],
                                          n: i32,
                                          maxbits: i32,
                                          bitlengths: &mut [u32])
                                          -> bool {
    // One leaf per symbol. Only numsymbols leaves will be used.
    let mut leaves: Vec<Node> = Vec::with_capacity(n as usize);

    // Initialize all bitlengths at 0.
    for i in 0..n {
        bitlengths[i as usize] = 0;
    }

    // Count used symbols and place them in the leaves.
    for i in 0..n {
        if frequencies[i as usize] != 0 {
            leaves.push(Node {
                weight: frequencies[i as usize],
                count: i, // Index of symbol this leaf represents.
                tail: None,
                in_use: false,
            });
        }
    }
    // Amount of symbols with frequency > 0.
    let numsymbols = leaves.len();

    // Check special cases and error conditions.
    if (1 << maxbits) < numsymbols {
        return true; // Error, too few maxbits to represent symbols.
    }
    if numsymbols == 0 {
        return false; // No symbols at all. OK.
    }
    if numsymbols == 1 {
        bitlengths[leaves[0].count as usize] = 1;
        return false; // Only one symbol, give it bitlength 1, not 0. OK.
    }

    // Sort the leaves from lightest to heaviest.
    qsort(leaves.as_mut_ptr() as *mut c_void,
          numsymbols as size_t,
          mem::size_of::<Node>() as size_t,
          leaf_comparator);

    // Initialize node memory pool.
    let mut pool = NodePool::new(2 * maxbits as u32 * (maxbits as u32 + 1));

    // Array of lists of chains. Each list requires only two lookahead chains at
    // a time, so each list is a array of two Node*'s.
    let mut lists: Vec<[Option<Rc<RefCell<Node>>>; 2]> = init_lists(&mut pool, &leaves, maxbits);

    // In the last list, 2 * numsymbols - 2 active chains need to be created. Two
    // are already created in the initialization. Each BoundaryPM run creates one.
    let num_boundary_pm_runs = 2 * numsymbols - 4;
    for i in 0..num_boundary_pm_runs {
        let is_final = i == num_boundary_pm_runs - 1;
        boundary_pm(&mut lists,
                    maxbits,
                    &leaves,
                    numsymbols as i32,
                    &mut pool,
                    maxbits - 1,
                    is_final);
    }

    extract_bit_lengths(lists[maxbits as usize - 1][1].clone(), &leaves, bitlengths);
    false // OK.
}

#[cfg(test)]
mod tests {
    use std::mem::size_of;
    use libc::{c_void, size_t};

    use super::qsort;

    extern "C" fn comparator(a: *const c_void, b: *const c_void) -> i32 {
        unsafe { *(a as *const i32) - *(b as *const i32) }
    }

    #[test]
    fn test_qsort() {
        unsafe {
            let mut a: [i32; 5] = [3, 2, 4, 1, 5];
            qsort(a.as_mut_ptr() as *mut c_void,
                  a.len() as size_t,
                  size_of::<i32>() as size_t,
                  comparator);
            assert_eq!(a, [1, 2, 3, 4, 5]);
        }
    }
}
<commit_msg>More localized unsafe code<commit_after>//! Bounded package merge algorithm, based on the paper
//! "A Fast and Space-Economical Algorithm for Length-Limited Coding
//! Jyrki Katajainen, Alistair Moffat, Andrew Turpin".

use std::cell::RefCell;
use std::mem;
use std::rc::Rc;

use libc::{c_void, size_t};

/// Nodes forming chains. Also used to represent leaves.
struct Node {
    /// Total weight (symbol count) of this chain.
    weight: usize,
    /// Previous node(s) of this chain, or 0 if none.
    tail: Option<Rc<RefCell<Node>>>,
    /// Leaf symbol index, or number of leaves before this chain.
    count: i32,
    /// Tracking for garbage collection.
    in_use: bool,
}

/// Memory pool for nodes.
struct NodePool {
    /// The pool.
    nodes: Vec<Rc<RefCell<Node>>>,
    /// Index of a possibly free node in the pool.
    next: u32,
    /// Size of the memory pool.
    size: u32,
}

impl NodePool {
    fn new(pool_size: u32) -> NodePool {
        NodePool {
            nodes: (0..pool_size).map(|_| {
                let node = Node {
                    weight: 0,
                    tail: None,
                    count: 0,
                    in_use: false,
                };
                Rc::new(RefCell::new(node))
            }).collect(),
            next: 0,
            size: pool_size,
        }
    }
}

/// Initializes a chain node with the given values and marks it as in use.
fn init_node(weight: usize, count: i32, tail: Option<Rc<RefCell<Node>>>, node: Rc<RefCell<Node>>) {
    let mut node = node.borrow_mut();
    node.weight = weight;
    node.count = count;
    node.tail = tail;
    node.in_use = true;
}

/**
 * Finds a free location in the memory pool. Performs garbage collection if needed.
 * lists: If given, used to mark in-use nodes during garbage collection.
 * maxbits: Size of lists.
 * pool: Memory pool to get free node from.
 */
fn get_free_node(lists: Option<&Vec<[Option<Rc<RefCell<Node>>>; 2]>>,
                 maxbits: i32,
                 pool: &mut NodePool)
                 -> Rc<RefCell<Node>> {
    loop {
        if pool.next >= pool.size {
            // Garbage collection
            for i in 0..pool.size {
                pool.nodes[i as usize].borrow_mut().in_use = false;
            }
            if let Some(lists) = lists {
                for i in 0..maxbits * 2 {
                    let mut node = lists[(i / 2) as usize][(i % 2) as usize].clone();
                    while let Some(n) = node {
                        let mut n = n.borrow_mut();
                        n.in_use = true;
                        node = n.tail.clone();
                    }
                }
            }
            pool.next = 0;
        }
        if !pool.nodes[pool.next as usize].borrow().in_use {
            break;
        }
        pool.next += 1;
    }
    let free_node = pool.nodes[pool.next as usize].clone();

    pool.next += 1;
    free_node
}

/**
 * Performs a Boundary Package-Merge step. Puts a new chain in the given list. The
 * new chain is, depending on the weights, a leaf or a combination of two chains
 * from the previous list.
 * lists: The lists of chains.
 * maxbits: Number of lists.
 * leaves: The leaves, one per symbol.
 * numsymbols: Number of leaves.
 * pool: the node memory pool.
 * index: The index of the list in which a new chain or leaf is required.
 * final: Whether this is the last time this function is called. If it is then it
 *   is no more needed to recursively call self.
 */
fn boundary_pm(lists: &mut Vec<[Option<Rc<RefCell<Node>>>; 2]>,
               maxbits: i32,
               leaves: &[Node],
               numsymbols: i32,
               pool: &mut NodePool,
               index: i32,
               is_final: bool) {
    let lastcount = lists[index as usize][1].as_ref().unwrap().borrow().count; // Count of last chain of list.

    if index == 0 && lastcount >= numsymbols {
        return;
    }

    let newchain = get_free_node(Some(&lists), maxbits, pool);
    let oldchain = lists[index as usize][1].clone();

    // These are set up before the recursive calls below, so that there is a list
    // pointing to the new node, to let the garbage collection know it's in use.
    lists[index as usize][0] = oldchain.clone();
    lists[index as usize][1] = Some(newchain.clone());

    if index == 0 {
        // New leaf node in list 0.
        init_node(leaves[lastcount as usize].weight,
                  lastcount + 1,
                  None,
                  newchain);
    } else {
        let sum = lists[(index - 1) as usize][0].as_ref().unwrap().borrow().weight +
                  lists[(index - 1) as usize][1].as_ref().unwrap().borrow().weight;
        if lastcount < numsymbols && sum > leaves[lastcount as usize].weight {
            // New leaf inserted in list, so count is incremented.
            init_node(leaves[lastcount as usize].weight,
                      lastcount + 1,
                      oldchain.as_ref().unwrap().borrow().tail.clone(),
                      newchain);
        } else {
            init_node(sum,
                      lastcount,
                      lists[(index - 1) as usize][1].clone(),
                      newchain);
            if !is_final {
                // Two lookahead chains of previous list used up, create new ones.
                boundary_pm(lists, maxbits, leaves, numsymbols, pool, index - 1, false);
                boundary_pm(lists, maxbits, leaves, numsymbols, pool, index - 1, false);
            }
        }
    }
}

/// Initializes each list with as lookahead chains the two leaves with lowest weights.
fn init_lists(pool: &mut NodePool,
              leaves: &[Node],
              maxbits: i32) -> Vec<[Option<Rc<RefCell<Node>>>; 2]> {
    let node0 = get_free_node(None, maxbits, pool);
    let node1 = get_free_node(None, maxbits, pool);
    init_node(leaves[0].weight, 1, None, node0.clone());
    init_node(leaves[1].weight, 2, None, node1.clone());
    let mut result = Vec::new();
    for _ in 0..maxbits {
        result.push([Some(node0.clone()), Some(node1.clone())]);
    }
    result
}

/**
 * Converts result of boundary package-merge to the bitlengths. The result in the
 * last chain of the last list contains the amount of active leaves in each list.
 * chain: Chain to extract the bit length from (last chain from last list).
 */
fn extract_bit_lengths(chain: Option<Rc<RefCell<Node>>>, leaves: &[Node], bitlengths: &mut [u32]) {
    let mut node = chain;
    while let Some(n) = node {
        for i in 0..n.borrow().count {
            bitlengths[leaves[i as usize].count as usize] =
                bitlengths[leaves[i as usize].count as usize] + 1;
        }
        node = n.borrow().tail.clone();
    }
}

/// Comparator for sorting the leaves. Has the function signature for qsort.
extern "C" fn leaf_comparator(a: *const c_void, b: *const c_void) -> i32 {
    unsafe {
        let node_a: *const Node = a as *const Node;
        let node_b: *const Node = b as *const Node;
        (*node_a).weight as i32 - (*node_b).weight as i32
    }
}

#[link(name="c")]
extern {
    fn qsort(base: *mut c_void,
             nmemb: size_t,
             size: size_t,
             compar: extern "C" fn(*const c_void, *const c_void) -> i32);
}

pub fn length_limited_code_lengths(frequencies: &[usize],
                                   n: i32,
                                   maxbits: i32,
                                   bitlengths: &mut [u32])
                                   -> bool {
    // One leaf per symbol. Only numsymbols leaves will be used.
    let mut leaves: Vec<Node> = Vec::with_capacity(n as usize);

    // Initialize all bitlengths at 0.
    for i in 0..n {
        bitlengths[i as usize] = 0;
    }

    // Count used symbols and place them in the leaves.
    for i in 0..n {
        if frequencies[i as usize] != 0 {
            leaves.push(Node {
                weight: frequencies[i as usize],
                count: i, // Index of symbol this leaf represents.
                tail: None,
                in_use: false,
            });
        }
    }
    // Amount of symbols with frequency > 0.
    let numsymbols = leaves.len();

    // Check special cases and error conditions.
    if (1 << maxbits) < numsymbols {
        return true; // Error, too few maxbits to represent symbols.
    }
    if numsymbols == 0 {
        return false; // No symbols at all. OK.
    }
    if numsymbols == 1 {
        bitlengths[leaves[0].count as usize] = 1;
        return false; // Only one symbol, give it bitlength 1, not 0. OK.
    }

    // Sort the leaves from lightest to heaviest.
    unsafe {
        qsort(leaves.as_mut_ptr() as *mut c_void,
            numsymbols as size_t,
            mem::size_of::<Node>() as size_t,
            leaf_comparator);
    }

    // Initialize node memory pool.
    let mut pool = NodePool::new(2 * maxbits as u32 * (maxbits as u32 + 1));

    // Array of lists of chains. Each list requires only two lookahead chains at
    // a time, so each list is a array of two Node*'s.
    let mut lists: Vec<[Option<Rc<RefCell<Node>>>; 2]> = init_lists(&mut pool, &leaves, maxbits);

    // In the last list, 2 * numsymbols - 2 active chains need to be created. Two
    // are already created in the initialization. Each BoundaryPM run creates one.
    let num_boundary_pm_runs = 2 * numsymbols - 4;
    for i in 0..num_boundary_pm_runs {
        let is_final = i == num_boundary_pm_runs - 1;
        boundary_pm(&mut lists,
                    maxbits,
                    &leaves,
                    numsymbols as i32,
                    &mut pool,
                    maxbits - 1,
                    is_final);
    }

    extract_bit_lengths(lists[maxbits as usize - 1][1].clone(), &leaves, bitlengths);
    false // OK.
}

#[cfg(test)]
mod tests {
    use std::mem::size_of;
    use libc::{c_void, size_t};

    use super::qsort;

    extern "C" fn comparator(a: *const c_void, b: *const c_void) -> i32 {
        unsafe { *(a as *const i32) - *(b as *const i32) }
    }

    #[test]
    fn test_qsort() {
        unsafe {
            let mut a: [i32; 5] = [3, 2, 4, 1, 5];
            qsort(a.as_mut_ptr() as *mut c_void,
                  a.len() as size_t,
                  size_of::<i32>() as size_t,
                  comparator);
            assert_eq!(a, [1, 2, 3, 4, 5]);
        }
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2016-2020 Fabian Schuiki

//! Expression lvalue lowering to MIR.

use crate::crate_prelude::*;
use crate::{
    hir::HirNode,
    mir::{
        lower,
        lower::rvalue::{adjust_indexing, compute_indexing},
        lvalue::*,
    },
    syntax::ast::BasicNode,
    ty::{SbvType, UnpackedType},
    ParamEnv,
};
use num::ToPrimitive;

/// An internal builder for lvalue lowering.
struct Builder<'a, C> {
    cx: &'a C,
    span: Span,
    expr: NodeId,
    env: ParamEnv,
}

impl<'a, C: Context<'a>> Builder<'_, C> {
    /// Create a new builder for a different node.
    #[allow(dead_code)]
    fn with(&self, expr: NodeId) -> Self {
        Builder {
            cx: self.cx,
            span: self.cx.span(expr),
            expr,
            env: self.env,
        }
    }

    /// Intern an MIR node.
    fn build(&self, ty: &'a UnpackedType<'a>, kind: LvalueKind<'a>) -> &'a Lvalue<'a> {
        self.cx.arena().alloc_mir_lvalue(Lvalue {
            id: self.cx.alloc_id(self.span),
            origin: self.expr,
            env: self.env,
            span: self.span,
            ty,
            kind: kind,
        })
    }

    /// Create an error node.
    ///
    /// This is usually called when something goes wrong during MIR construction
    /// and a marker node is needed to indicate that part of the MIR is invalid.
    fn error(&self) -> &'a Lvalue<'a> {
        self.build(UnpackedType::make_error(), LvalueKind::Error)
    }
}

/// Lower an expression to an lvalue in the MIR.
pub fn lower_expr<'gcx>(
    cx: &impl Context<'gcx>,
    expr_id: NodeId,
    env: ParamEnv,
) -> &'gcx Lvalue<'gcx> {
    let span = cx.span(expr_id);
    let builder = Builder {
        cx,
        span,
        expr: expr_id,
        env,
    };
    try_lower_expr(&builder, expr_id).unwrap_or_else(|_| builder.error())
}

/// Lower an expression to an lvalue in the MIR.
///
/// May return an error if any of the database queries break.
fn try_lower_expr<'gcx>(
    builder: &Builder<'_, impl Context<'gcx>>,
    expr_id: NodeId,
) -> Result<&'gcx Lvalue<'gcx>> {
    let cx = builder.cx;
    let span = builder.span;
    let env = builder.env;

    // Try to extract the expr HIR for this node. Handle a few special cases
    // where the node is not technically an expression, but can be used as a
    // lvalue.
    let hir = match cx.hir_of(expr_id) {
        Ok(HirNode::Expr(x)) => x,
        Ok(x) => bug_span!(span, cx, "no lvalue for {:?}", x),
        Err(_) => return Ok(builder.error()),
    };

    // Determine the expression type.
    let ty = cx.type_of_expr(Ref(hir), env);
    if ty.is_error() {
        return Ok(builder.error());
    }

    // Match on the various forms.
    match hir.kind {
        // Identifiers and scoped identifiers we simply resolve and try to lower
        // the resolved node to an MIR node.
        hir::ExprKind::Ident(..) | hir::ExprKind::Scope(..) => {
            let binding = cx.resolve_node(expr_id, env)?;
            return match cx.hir_of(binding)? {
                HirNode::GenvarDecl(decl) => Ok(builder.build(ty, LvalueKind::Genvar(decl.id))),
                HirNode::VarDecl(decl) => Ok(builder.build(ty, LvalueKind::Var(decl.id))),
                HirNode::IntPort(port) if ty.resolve_full().core.get_interface().is_some() => {
                    Ok(builder.build(ty, LvalueKind::Intf(port.id)))
                }
                HirNode::IntPort(port) => Ok(builder.build(ty, LvalueKind::Port(port.id))),
                HirNode::Inst(inst) if ty.resolve_full().core.get_interface().is_some() => {
                    Ok(builder.build(ty, LvalueKind::Intf(inst.id)))
                }
                x => {
                    cx.emit(
                        DiagBuilder2::error(format!(
                            "{} cannot be used as the target of an assignment",
                            x.desc_full()
                        ))
                        .span(span),
                    );
                    Err(())
                }
            };
        }

        hir::ExprKind::Index(target, mode) => {
            // Compute the indexing parameters.
            let (base, length) = compute_indexing(cx, builder.expr, env, mode)?;

            // Lower the indexee and make sure it can be indexed into.
            let target = cx.mir_lvalue(target, env);
            assert_span!(
                target.ty.dims().next().is_some(),
                target.span,
                cx,
                "cannot index into `{}`; should be handled by typeck",
                target.ty
            );

            // Offset the indexing base by the dimension base, e.g. for accesses
            // such as `x[1]` into `logic [2:1] x`, which essentially accesses
            // element 0.
            let target_dim = target.ty.dims().next().unwrap();
            let rvalue_builder = lower::rvalue::Builder {
                cx,
                span: base.span,
                expr: base.id,
                env: base.env,
            };
            let base = adjust_indexing(&rvalue_builder, base, target_dim);

            // Build the cast lvalue.
            return Ok(builder.build(
                ty,
                LvalueKind::Index {
                    value: target,
                    base,
                    length,
                },
            ));
        }

        hir::ExprKind::Field(target, name) => {
            let target_ty = cx.self_determined_type(target, env);
            let value = cx.mir_lvalue(target, env);
            if let Some(intf) = target_ty.and_then(|ty| ty.get_interface()) {
                let def = cx.resolve_hierarchical_or_error(name, intf.ast)?;
                // Distinguish `intf.modport` and `intf.signal`.
                if def.node.as_all().is_modport_name() {
                    return Ok(builder.build(ty, value.kind.clone()));
                } else {
                    return Ok(builder.build(ty, LvalueKind::IntfSignal(value, def.node.id())));
                }
            } else {
                let (field, _) = cx.resolve_field_access(expr_id, env)?;
                return Ok(builder.build(ty, LvalueKind::Member { value, field }));
            }
        }

        hir::ExprKind::Concat(repeat, ref exprs) => {
            // Compute the SBVT for each expression and lower it to MIR,
            // implicitly casting to the SBVT.
            let exprs = exprs
                .iter()
                .map(|&expr| {
                    let value = builder.cx.mir_lvalue(expr, env);
                    assert_span!(value.ty.coalesces_to_llhd_scalar(), value.span, builder.cx);
                    Ok((value.ty.get_bit_size().unwrap(), value))
                })
                .collect::<Result<Vec<_>>>()?;

            // Compute the result type of the concatenation.
            let final_ty = builder.cx.need_self_determined_type(hir.id, env);
            if final_ty.is_error() {
                return Err(());
            }
            let domain = final_ty.domain();
            let concat_width = exprs.iter().map(|(w, _)| w).sum();
            let concat_ty =
                SbvType::new(domain, ty::Sign::Unsigned, concat_width).to_unpacked(builder.cx);

            // Assemble the concatenation.
            let concat = builder.build(
                concat_ty,
                LvalueKind::Concat(exprs.into_iter().map(|(_, v)| v).collect()),
            );

            // If a repetition is present, apply that.
            let repeat = if let Some(repeat) = repeat {
                let count = builder
                    .cx
                    .constant_int_value_of(repeat, env)?
                    .to_usize()
                    .unwrap();
                builder.build(final_ty, LvalueKind::Repeat(count, concat))
            } else {
                concat
            };
            return Ok(repeat);
        }

        _ => (),
    }

    // Show an error informing the user that the given expression cannot be
    // assigned to.
    error!("{:#?}", hir);
    cx.emit(DiagBuilder2::error(format!("{} cannot be assigned to", hir.desc_full())).span(span));
    Err(())
}
<commit_msg>mir: Prepare for lvalue cast lowering<commit_after>// Copyright (c) 2016-2020 Fabian Schuiki

//! Expression lvalue lowering to MIR.

use crate::crate_prelude::*;
use crate::{
    hir::HirNode,
    mir::{
        lower,
        lower::rvalue::{adjust_indexing, compute_indexing},
        lvalue::*,
    },
    syntax::ast::BasicNode,
    ty::{SbvType, UnpackedType},
    typeck::{CastOp, CastType},
    ParamEnv,
};
use num::ToPrimitive;

/// An internal builder for lvalue lowering.
struct Builder<'a, C> {
    cx: &'a C,
    span: Span,
    expr: NodeId,
    env: ParamEnv,
}

impl<'a, C: Context<'a>> Builder<'_, C> {
    /// Create a new builder for a different node.
    #[allow(dead_code)]
    fn with(&self, expr: NodeId) -> Self {
        Builder {
            cx: self.cx,
            span: self.cx.span(expr),
            expr,
            env: self.env,
        }
    }

    /// Intern an MIR node.
    fn build(&self, ty: &'a UnpackedType<'a>, kind: LvalueKind<'a>) -> &'a Lvalue<'a> {
        self.cx.arena().alloc_mir_lvalue(Lvalue {
            id: self.cx.alloc_id(self.span),
            origin: self.expr,
            env: self.env,
            span: self.span,
            ty,
            kind: kind,
        })
    }

    /// Create an error node.
    ///
    /// This is usually called when something goes wrong during MIR construction
    /// and a marker node is needed to indicate that part of the MIR is invalid.
    fn error(&self) -> &'a Lvalue<'a> {
        self.build(UnpackedType::make_error(), LvalueKind::Error)
    }
}

/// Lower an expression to an lvalue in the MIR.
pub fn lower_expr<'a>(cx: &impl Context<'a>, expr_id: NodeId, env: ParamEnv) -> &'a Lvalue<'a> {
    let span = cx.span(expr_id);
    let builder = Builder {
        cx,
        span,
        expr: expr_id,
        env,
    };

    // Try to extract the expr HIR for this node. Handle a few special cases
    // where the node is not technically an expression, but can be used as a
    // lvalue.
    let hir = match cx.hir_of(expr_id) {
        Ok(HirNode::Expr(x)) => x,
        Ok(x) => bug_span!(span, cx, "no lvalue for {:?}", x),
        Err(_) => return builder.error(),
    };

    // Determine the cast type.
    let cast = cx.cast_type(expr_id, env).unwrap();

    // Lower the expression.
    let lvalue = lower_expr_inner(&builder, hir, cast.init).unwrap_or_else(|_| builder.error());
    if lvalue.is_error() {
        return lvalue;
    }
    assert_span!(
        lvalue.ty.is_identical(cast.init),
        hir.span,
        cx,
        "lvalue lowering produced type `{}`, expected `{}`",
        lvalue.ty,
        cast.init
    );

    // Lower the casts.
    lower_cast(&builder, lvalue, cast)
}

/// Lower an expression to an rvalue in the MIR.
///
/// May return an error if any of the database queries break.
fn lower_expr_inner<'a>(
    builder: &Builder<'_, impl Context<'a>>,
    hir: &'a hir::Expr<'a>,
    ty: &'a UnpackedType<'a>,
) -> Result<&'a Lvalue<'a>> {
    let expr_id = hir.id;
    let cx = builder.cx;
    let span = cx.span(expr_id);
    let env = builder.env;
    if ty.is_error() {
        return Err(());
    }

    // Match on the various forms.
    match hir.kind {
        // Identifiers and scoped identifiers we simply resolve and try to lower
        // the resolved node to an MIR node.
        hir::ExprKind::Ident(..) | hir::ExprKind::Scope(..) => {
            let binding = cx.resolve_node(expr_id, env)?;
            return match cx.hir_of(binding)? {
                HirNode::GenvarDecl(decl) => Ok(builder.build(ty, LvalueKind::Genvar(decl.id))),
                HirNode::VarDecl(decl) => Ok(builder.build(ty, LvalueKind::Var(decl.id))),
                HirNode::IntPort(port) if ty.resolve_full().core.get_interface().is_some() => {
                    Ok(builder.build(ty, LvalueKind::Intf(port.id)))
                }
                HirNode::IntPort(port) => Ok(builder.build(ty, LvalueKind::Port(port.id))),
                HirNode::Inst(inst) if ty.resolve_full().core.get_interface().is_some() => {
                    Ok(builder.build(ty, LvalueKind::Intf(inst.id)))
                }
                x => {
                    cx.emit(
                        DiagBuilder2::error(format!(
                            "{} cannot be used as the target of an assignment",
                            x.desc_full()
                        ))
                        .span(span),
                    );
                    Err(())
                }
            };
        }

        hir::ExprKind::Index(target, mode) => {
            // Compute the indexing parameters.
            let (base, length) = compute_indexing(cx, builder.expr, env, mode)?;

            // Lower the indexee and make sure it can be indexed into.
            let target = cx.mir_lvalue(target, env);
            assert_span!(
                target.ty.dims().next().is_some(),
                target.span,
                cx,
                "cannot index into `{}`; should be handled by typeck",
                target.ty
            );

            // Offset the indexing base by the dimension base, e.g. for accesses
            // such as `x[1]` into `logic [2:1] x`, which essentially accesses
            // element 0.
            let target_dim = target.ty.dims().next().unwrap();
            let rvalue_builder = lower::rvalue::Builder {
                cx,
                span: base.span,
                expr: base.id,
                env: base.env,
            };
            let base = adjust_indexing(&rvalue_builder, base, target_dim);

            // Build the cast lvalue.
            return Ok(builder.build(
                ty,
                LvalueKind::Index {
                    value: target,
                    base,
                    length,
                },
            ));
        }

        hir::ExprKind::Field(target, name) => {
            let target_ty = cx.self_determined_type(target, env);
            let value = cx.mir_lvalue(target, env);
            if let Some(intf) = target_ty.and_then(|ty| ty.get_interface()) {
                let def = cx.resolve_hierarchical_or_error(name, intf.ast)?;
                // Distinguish `intf.modport` and `intf.signal`.
                if def.node.as_all().is_modport_name() {
                    return Ok(builder.build(ty, value.kind.clone()));
                } else {
                    return Ok(builder.build(ty, LvalueKind::IntfSignal(value, def.node.id())));
                }
            } else {
                let (field, _) = cx.resolve_field_access(expr_id, env)?;
                return Ok(builder.build(ty, LvalueKind::Member { value, field }));
            }
        }

        hir::ExprKind::Concat(repeat, ref exprs) => {
            // Compute the SBVT for each expression and lower it to MIR,
            // implicitly casting to the SBVT.
            let exprs = exprs
                .iter()
                .map(|&expr| {
                    let value = builder.cx.mir_lvalue(expr, env);
                    assert_span!(
                        value.ty.coalesces_to_llhd_scalar(),
                        value.span,
                        builder.cx,
                        "type `{}` does not coalesce to LLHD scalar",
                        value.ty
                    );
                    Ok((value.ty.get_bit_size().unwrap(), value))
                })
                .collect::<Result<Vec<_>>>()?;

            // Compute the result type of the concatenation.
            let final_ty = builder.cx.need_self_determined_type(hir.id, env);
            if final_ty.is_error() {
                return Err(());
            }
            let domain = final_ty.domain();
            let concat_width = exprs.iter().map(|(w, _)| w).sum();
            let concat_ty =
                SbvType::new(domain, ty::Sign::Unsigned, concat_width).to_unpacked(builder.cx);

            // Assemble the concatenation.
            let concat = builder.build(
                concat_ty,
                LvalueKind::Concat(exprs.into_iter().map(|(_, v)| v).collect()),
            );

            // If a repetition is present, apply that.
            let repeat = if let Some(repeat) = repeat {
                let count = builder
                    .cx
                    .constant_int_value_of(repeat, env)?
                    .to_usize()
                    .unwrap();
                builder.build(final_ty, LvalueKind::Repeat(count, concat))
            } else {
                concat
            };
            return Ok(repeat);
        }

        _ => (),
    }

    // Show an error informing the user that the given expression cannot be
    // assigned to.
    error!("{:#?}", hir);
    cx.emit(DiagBuilder2::error(format!("{} cannot be assigned to", hir.desc_full())).span(span));
    Err(())
}

/// Generate the nodes necessary for a cast operation.
fn lower_cast<'a>(
    builder: &Builder<'_, impl Context<'a>>,
    mut value: &'a Lvalue<'a>,
    to: CastType<'a>,
) -> &'a Lvalue<'a> {
    // Don't bother with errors.
    if value.is_error() {
        return value;
    }
    if to.is_error() {
        return builder.error();
    }
    assert_type!(
        value.ty,
        to.init,
        value.span,
        builder.cx,
        "lvalue type `{}` does not match initial type of cast `{}`",
        value.ty,
        to
    );
    trace!(
        "Lowering cast to `{}` from `{}` of `{}` (line {})",
        to,
        value.ty,
        value.span.extract(),
        value.span.begin().human_line()
    );

    // Lower each cast individually.
    for &(op, to) in &to.casts {
        debug!("- {:?} from `{}` to `{}`", op, value.ty, to);
        match op {
            CastOp::PickModport => {
                value = builder.build(to, value.kind.clone());
            }
            _ => {
                bug_span!(
                    value.span,
                    builder.cx,
                    "lvalue lowering of cast to `{}` not yet supported: {:?}",
                    to,
                    op
                );
            }
        }
        if !value.ty.is_identical(to) {
            error!(
                "Cast {:?} should have produced `{}`, but value is `{}`",
                op, to, value.ty
            );
        }
    }

    // Check that the casts have actually produced the expected output type.
    assert_type!(
        value.ty,
        to.ty,
        value.span,
        builder.cx,
        "lvalue type `{}` does not match final cast type `{}` after lower_cast",
        value.ty,
        to.ty
    );
    value
}
<|endoftext|>


<commit_before>use std::collections::{HashMap, HashSet};

pub struct NodeIterator<'a, V: 'a> {
    v: &'a V
}

impl<'a, V: 'a> Iterator for NodeIterator<'a, V> {
    type Item = &'a V;

    fn next(&mut self) -> Option<&'a V> {
        unimplemented!()
    }
}

pub type NodeId = usize;

pub struct GraphData<V> {
    directed: bool,
    loop_allowed: bool,

    nodes: HashMap<NodeId, V>,
    edges: HashMap<NodeId, NodeId>
}

pub struct ValueGraphData<V, E> {
    nodes: HashMap<NodeId, V>,
    edges: HashMap<NodeId, NodeId>,
    edge_labels: HashMap<(NodeId, NodeId),E>
}

pub struct GraphBuilder {
    directed: bool,
    loop_allowed: bool
}

pub trait Graph<V> {
    fn degree(&self, &V) -> usize;

    fn in_degree(&self, &V) -> usize;

    fn out_degree(&self, &V) -> usize;

    fn is_directed(&self) -> bool;

    fn contains_node(&self, &V) -> bool;

    fn nodes<'a>(&'a self) -> NodeIterator<'a, V>;

    fn adjacent_nodes<'a>(&'a self, &V) -> NodeIterator<'a, V>;

    fn predecessors<'a>(&'a self) -> NodeIterator<'a, V>;

    fn successors<'a>(&'a self) -> NodeIterator<'a, V>;

    fn edges<'a>(&'a self);

    fn is_connected(&self, v1: &V, v2: &V) -> bool;
}

pub trait MutableGraph<V>: Graph<V> {
    fn add_edge(&mut self, &V, &V);
}

pub trait ValueGraph<V, E> : Graph<V> {
    fn edge(&self, v1: &V, v2: &V) -> &E;
}

pub trait MutableValueGraph<V, E>: ValueGraph<V, E> {
    fn add_edge(&mut self, &V, &V, e: E);
}<commit_msg>Add GraphBuilder and skeleton impl for GraphData<commit_after>/// Graph data structures and algorithms

use std::collections::{HashMap, HashSet};

pub type NodeId = usize;

pub trait Graph<'a, V: 'a> {
    fn degree(&self, &V) -> usize;

    fn in_degree(&self, &V) -> usize;

    fn out_degree(&self, &V) -> usize;

    fn is_directed(&self) -> bool;

    fn contains_node(&self, &V) -> bool;

    fn nodes(&self) -> NodeIterator<'a, V>;

    fn adjacent_nodes(&self, &V) -> NodeIterator<'a, V>;

    fn predecessors(&self) -> NodeIterator<'a, V>;

    fn successors(&self) -> NodeIterator<'a, V>;

    fn edges(&self);

    fn is_connected(&self, v1: &V, v2: &V) -> bool;
}

pub trait MutableGraph<'a, V: 'a>: Graph<'a, V> {
    fn add_edge(&mut self, &V, &V);
}

pub trait ValueGraph<'a, V: 'a, E: 'a> : Graph<'a, V> {
    fn edge(&self, v1: &V, v2: &V) -> &E;
}

pub trait MutableValueGraph<'a, V: 'a, E: 'a>: ValueGraph<'a, V, E> {
    fn add_edge(&mut self, &V, &V, e: E);
}

pub struct NodeIterator<'a, V: 'a> {
    v: &'a V
}

impl<'a, V: 'a> Iterator for NodeIterator<'a, V> {
    type Item = &'a V;

    fn next(&mut self) -> Option<&'a V> {
        unimplemented!()
    }
}

pub struct GraphData<V> {
    directed: bool,
    loop_allowed: bool,

    nodes: HashMap<NodeId, V>,
    edges: HashMap<NodeId, NodeId>
}

pub struct ValueGraphData<V, E> {
    nodes: HashMap<NodeId, V>,
    edges: HashMap<NodeId, NodeId>,
    edge_labels: HashMap<(NodeId, NodeId),E>
}

pub struct GraphBuilder {
    directed: bool,
    loop_allowed: bool
}

impl GraphBuilder {
    pub fn new() -> GraphBuilder {
        GraphBuilder {
            directed: false,
            loop_allowed: false,
        }
    }

    pub fn set_directed(mut self, directed: bool) -> GraphBuilder {
        self.directed = directed;
        self
    }

    pub fn allow_loop(mut self, loop_allowed: bool) -> GraphBuilder {
        self.loop_allowed = loop_allowed;
        self
    }

    pub fn build<V: 'static>(&self) -> Box<Graph<V>> {
        Box::new(GraphData {
            directed: self.directed,
            loop_allowed: self.loop_allowed,
            nodes: HashMap::new(),
            edges: HashMap::new()
        })
    }

    pub fn build_with_labeled_edge<V, E>(&self) -> Box<ValueGraph<V, E>> {
        unimplemented!()
    }
}

impl<'a, V: 'a> Graph<'a, V> for GraphData<V> {
    fn degree(&self, v: &V) -> usize {
        unimplemented!()
    }

    fn in_degree(&self, v: &V) -> usize {
        unimplemented!()
    }

    fn out_degree(&self, v: &V) -> usize {
        unimplemented!()
    }

    fn is_directed(&self) -> bool {
        self.directed
    }

    fn contains_node(&self, v: &V) -> bool {
        unimplemented!()
    }

    fn nodes(&self) -> NodeIterator<'a, V> {
        unimplemented!()
    }

    fn adjacent_nodes(&self, v: &V) -> NodeIterator<'a, V> {
        unimplemented!()
    }

    fn predecessors(&self) -> NodeIterator<'a, V> {
        unimplemented!()
    }

    fn successors(&self) -> NodeIterator<'a, V> {
        unimplemented!()
    }

    fn edges(&self) {
        unimplemented!()
    }

    fn is_connected(&self, v1: &V, v2: &V) -> bool {
        unimplemented!()
    }
}<|endoftext|>


<commit_before>// Copyright 2016 Walter Kuppens.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use nes::cpu::CPU;
use std::io::Cursor;

// Memory partition sizes (physical).
// TODO: Calculate based on ranges below.
pub const RAM_SIZE                : usize = 0x800;
pub const PPU_CTRL_REGISTERS_SIZE : usize = 0x8;
pub const MISC_CTRL_REGISTERS_SIZE: usize = 0x20;
pub const EXPANSION_ROM_SIZE      : usize = 0x1FE0;
pub const SRAM_SIZE               : usize = 0x2000;
pub const PRG_ROM_SIZE            : usize = 0x4000;

// Partitioned virtual memory map bounds.
pub const RAM_START_ADDR                 : usize = 0x0;
pub const RAM_END_ADDR                   : usize = 0x7FF;
pub const RAM_MIRROR_START               : usize = 0x800;
pub const RAM_MIRROR_END                 : usize = 0x1FFF;
pub const PPU_CTRL_REGISTERS_START       : usize = 0x2000;
pub const PPU_CTRL_REGISTERS_END         : usize = 0x2007;
pub const PPU_CTRL_REGISTERS_MIRROR_START: usize = 0x2008;
pub const PPU_CTRL_REGISTERS_MIRROR_END  : usize = 0x3FFF;
pub const MISC_CTRL_REGISTERS_START      : usize = 0x4000;
pub const MISC_CTRL_REGISTERS_END        : usize = 0x401F;
pub const EXPANSION_ROM_START            : usize = 0x4020;
pub const EXPANSION_ROM_END              : usize = 0x5FFF;
pub const SRAM_START                     : usize = 0x6000;
pub const SRAM_END                       : usize = 0x7FFF;
pub const PRG_ROM_1_START                : usize = 0x8000;
pub const PRG_ROM_1_END                  : usize = 0xBFFF;
pub const PRG_ROM_2_START                : usize = 0xC000;
pub const PRG_ROM_2_END                  : usize = 0xFFFF;

// Constants for additional structures.
pub const TRAINER_START: usize = 0x7000;
pub const TRAINER_SIZE : usize = 512;

// Location of the first byte on the bottom of the stack. The stack starts on
// memory page 2 (0x100).
const STACK_OFFSET: usize = 0x100;

/// Partitioned physical memory layout for CPU memory. These fields are not
/// meant to be accessed directly by the CPU implementation and are instead
/// accessed through a read function that handles memory mapping.
///
/// NOTE: Currently all memory is allocated on the stack. This may not work well
/// for systems with a small stack and slices should be boxed up.
pub struct Memory {
    // 2kB of internal RAM for which it's use is entirely up to the programmer.
    ram: [u8; RAM_SIZE],

    // Contains PPU registers that allow the running application to communicate
    // with the PPU.
    ppu_ctrl_registers: [u8; PPU_CTRL_REGISTERS_SIZE],

    // Contains NES APU and I/O registers. Also allows use of APU and I/O
    // functionality that is normally disabled.
    misc_ctrl_registers: [u8; MISC_CTRL_REGISTERS_SIZE],

    expansion_rom: [u8; EXPANSION_ROM_SIZE],

    // 8kB of static RAM.
    sram: [u8; SRAM_SIZE],

    // PRG-ROM bank 1.
    prg_rom_1: [u8; PRG_ROM_SIZE],

    // PRG-ROM bank 2. Execution starts here.
    prg_rom_2: [u8; PRG_ROM_SIZE]
}

impl Memory {
    pub fn new() -> Memory {
        Memory {
            ram: [0; RAM_SIZE],
            ppu_ctrl_registers: [0; PPU_CTRL_REGISTERS_SIZE],
            misc_ctrl_registers: [0; MISC_CTRL_REGISTERS_SIZE],
            expansion_rom: [0; EXPANSION_ROM_SIZE],
            sram: [0; SRAM_SIZE],
            prg_rom_1: [0; PRG_ROM_SIZE],
            prg_rom_2: [0; PRG_ROM_SIZE]
        }
    }

    /// Reads an unsigned 8-bit byte value located at the given virtual address.
    #[inline(always)]
    pub fn read_u8(&mut self, addr: usize) -> u8 {
        let (bank, idx) = self.map(addr);
        bank[idx]
    }

    /// Writes an unsigned 8-bit byte value to the given virtual address.
    #[inline(always)]
    pub fn write_u8(&mut self, addr: usize, val: u8) {
        let (bank, idx) = self.map(addr);
        bank[idx] = val;
    }

    /// Reads an unsigned 16-bit byte value at the given virtual address
    /// (little-endian).
    #[inline(always)]
    pub fn read_u16(&mut self, addr: usize) -> u16 {
        // Reads two bytes starting at the given address and parses them.
        let mut reader = Cursor::new(vec![
            self.read_u8(addr),
            self.read_u8(addr + 1)
        ]);
        reader.read_u16::<LittleEndian>().unwrap()
    }

    /// Reads an unsigned 16-bit byte value at the given virtual address
    /// (little-endian) where the MSB is read at page start if the LSB is at
    /// the end of a page. This exists to properly emulate a hardware bug in the
    /// 2A03 where indirect jumps cannot fetch addresses outside it's own page.
    #[inline(always)]
    pub fn read_u16_wrapped_msb(&mut self, addr: usize) -> u16 {
        let lsb = self.read_u8(addr);
        let msb = if addr & 0xFF == 0xFF {
            self.read_u8(addr - 0xFF)
        } else {
            self.read_u8(addr + 1)
        };

        // Reads two bytes starting at the given address and parses them.
        let mut reader = Cursor::new(vec![lsb, msb]);
        reader.read_u16::<LittleEndian>().unwrap()
    }

    /// Writes an unsigned 16-bit byte value to the given virtual address
    /// (little-endian)
    #[inline(always)]
    pub fn write_u16(&mut self, addr: usize, val: u16) {
        let mut writer = vec![];
        writer.write_u16::<LittleEndian>(val).unwrap();
        self.write_u8(addr, writer[0]);
        self.write_u8(addr + 1, writer[1]);
    }

    /// Dumps the contents of a slice starting at a given address.
    pub fn memdump(&mut self, addr: usize, buf: &[u8]) {
        for i in 0..buf.len() {
            self.write_u8(addr + i, buf[i]);
        }
    }

    // Utility functions for managing the stack.

    /// Pushes an 8-bit number onto the stack.
    pub fn stack_push_u8(&mut self, cpu: &mut CPU, value: u8) {
        self.write_u8(STACK_OFFSET + cpu.sp as usize, value);
        cpu.sp = cpu.sp.wrapping_sub(1);
    }

    /// Pops an 8-bit number off the stack.
    pub fn stack_pop_u8(&mut self, cpu: &mut CPU) -> u8 {
        cpu.sp = cpu.sp.wrapping_add(1);
        self.read_u8(STACK_OFFSET + cpu.sp as usize)
    }

    /// Pushes a 16-bit number (usually an address) onto the stack.
    pub fn stack_push_u16(&mut self, cpu: &mut CPU, value: u16) {
        self.write_u16(STACK_OFFSET + cpu.sp as usize, value);
        cpu.sp = cpu.sp.wrapping_sub(2);
    }

    /// Pops a 16-bit number (usually an address) off the stack.
    pub fn stack_pop_u16(&mut self, cpu: &mut CPU) -> u16 {
        cpu.sp = cpu.sp.wrapping_add(2);
        self.read_u16(STACK_OFFSET + cpu.sp as usize)
    }

    // Memory mapping functions.

    /// Returns true when the provided address is in the provided range
    /// (inclusive).
    ///
    /// NOTE: Inclusive ranges are in unstable rust and this function can be
    /// replaced once it lands in stable (RFC 1192).
    fn addr_in_range(&self, addr: usize, lower: usize, upper: usize) -> bool {
        addr >= lower && addr <= upper
    }

    /// Maps a given virtual address to a physical address internal to the
    /// emulator. Returns a memory buffer and index for physical memory access.
    ///
    /// TODO: Add permissions flag for special addresses?
    fn map(&mut self, addr: usize) -> (&mut [u8], usize) {
        // Address translation for accessing system memory. No modifications
        // need to be done to the address as it's at the start of addressable
        // memory.
        if self.addr_in_range(addr, RAM_START_ADDR, RAM_END_ADDR) {
            return (&mut self.ram, addr)
        }

        // Address translation for mirroring of system memory. System memory at
        // $0000-$07FF is mirrored at $0800-$0FFF, $1000-$17FF, and $1800-$1FFF
        // - attempting to access memory at, for example, $0173 is the same as
        // accessing memory at $0973, $1173, or $1973.
        if self.addr_in_range(addr, RAM_MIRROR_START, RAM_MIRROR_END) {
            let new_addr = addr % RAM_SIZE;
            return (&mut self.ram, new_addr)
        }

        // Address translation for accessing the PPU control registers.
        if self.addr_in_range(addr, PPU_CTRL_REGISTERS_START,
                              PPU_CTRL_REGISTERS_END) {
            let new_addr = addr - PPU_CTRL_REGISTERS_START;
            return (&mut self.ppu_ctrl_registers, new_addr)
        }

        // Address translation for mirroring of the PPU control registers. PPU
        // control at $2000-$2007 is mirrored 1023 times at $2008-$3FFF.
        if self.addr_in_range(addr, PPU_CTRL_REGISTERS_MIRROR_START,
                              PPU_CTRL_REGISTERS_MIRROR_END) {
            let new_addr = (addr - PPU_CTRL_REGISTERS_START) %
                PPU_CTRL_REGISTERS_SIZE;
            return (&mut self.ppu_ctrl_registers, new_addr)
        }

        // Address translation for miscellaneous registers (such as APU etc).
        if self.addr_in_range(addr, MISC_CTRL_REGISTERS_START,
                              MISC_CTRL_REGISTERS_END) {
            let new_addr = addr - MISC_CTRL_REGISTERS_START;
            return (&mut self.misc_ctrl_registers, new_addr)
        }

        // Address translation for accessing cartridge expansion ROM.
        if self.addr_in_range(addr, EXPANSION_ROM_START, EXPANSION_ROM_END) {
            let new_addr = addr - EXPANSION_ROM_START;
            return (&mut self.expansion_rom, new_addr)
        }

        // Address translation for accessing SRAM.
        if self.addr_in_range(addr, SRAM_START, SRAM_END) {
            let new_addr = addr - SRAM_START;
            return (&mut self.sram, new_addr)
        }

        // Address translation for accessing the program rom (bank 1).
        if self.addr_in_range(addr, PRG_ROM_1_START, PRG_ROM_1_END) {
            let new_addr = addr - PRG_ROM_1_START;
            return (&mut self.prg_rom_1, new_addr)
        }

        // Address translation for accessing the program rom (bank 2).
        if self.addr_in_range(addr, PRG_ROM_2_START, PRG_ROM_2_END) {
            let new_addr = addr - PRG_ROM_2_START;
            return (&mut self.prg_rom_2, new_addr)
        }

        panic!("Unable to map virtual address {:#X} to any physical address",
               addr);
    }
}
<commit_msg>Simplified default memory mapper<commit_after>// Copyright 2016 Walter Kuppens.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use nes::cpu::CPU;
use std::io::Cursor;

// Memory partition sizes (physical).
// TODO: Calculate based on ranges below.
pub const RAM_SIZE                : usize = 0x800;
pub const PPU_CTRL_REGISTERS_SIZE : usize = 0x8;
pub const MISC_CTRL_REGISTERS_SIZE: usize = 0x20;
pub const EXPANSION_ROM_SIZE      : usize = 0x1FE0;
pub const SRAM_SIZE               : usize = 0x2000;
pub const PRG_ROM_SIZE            : usize = 0x4000;

// Partitioned virtual memory map bounds.
pub const RAM_START_ADDR                 : usize = 0x0;
pub const RAM_END_ADDR                   : usize = 0x7FF;
pub const RAM_MIRROR_START               : usize = 0x800;
pub const RAM_MIRROR_END                 : usize = 0x1FFF;
pub const PPU_CTRL_REGISTERS_START       : usize = 0x2000;
pub const PPU_CTRL_REGISTERS_END         : usize = 0x2007;
pub const PPU_CTRL_REGISTERS_MIRROR_START: usize = 0x2008;
pub const PPU_CTRL_REGISTERS_MIRROR_END  : usize = 0x3FFF;
pub const MISC_CTRL_REGISTERS_START      : usize = 0x4000;
pub const MISC_CTRL_REGISTERS_END        : usize = 0x401F;
pub const EXPANSION_ROM_START            : usize = 0x4020;
pub const EXPANSION_ROM_END              : usize = 0x5FFF;
pub const SRAM_START                     : usize = 0x6000;
pub const SRAM_END                       : usize = 0x7FFF;
pub const PRG_ROM_1_START                : usize = 0x8000;
pub const PRG_ROM_1_END                  : usize = 0xBFFF;
pub const PRG_ROM_2_START                : usize = 0xC000;
pub const PRG_ROM_2_END                  : usize = 0xFFFF;

// Constants for additional structures.
pub const TRAINER_START: usize = 0x7000;
pub const TRAINER_SIZE : usize = 512;

// Location of the first byte on the bottom of the stack. The stack starts on
// memory page 2 (0x100).
const STACK_OFFSET: usize = 0x100;

/// Partitioned physical memory layout for CPU memory. These fields are not
/// meant to be accessed directly by the CPU implementation and are instead
/// accessed through a read function that handles memory mapping.
///
/// NOTE: Currently all memory is allocated on the stack. This may not work well
/// for systems with a small stack and slices should be boxed up.
pub struct Memory {
    // 2kB of internal RAM for which it's use is entirely up to the programmer.
    ram: [u8; RAM_SIZE],

    // Contains PPU registers that allow the running application to communicate
    // with the PPU.
    ppu_ctrl_registers: [u8; PPU_CTRL_REGISTERS_SIZE],

    // Contains NES APU and I/O registers. Also allows use of APU and I/O
    // functionality that is normally disabled.
    misc_ctrl_registers: [u8; MISC_CTRL_REGISTERS_SIZE],

    expansion_rom: [u8; EXPANSION_ROM_SIZE],

    // 8kB of static RAM.
    sram: [u8; SRAM_SIZE],

    // PRG-ROM bank 1.
    prg_rom_1: [u8; PRG_ROM_SIZE],

    // PRG-ROM bank 2. Execution starts here.
    prg_rom_2: [u8; PRG_ROM_SIZE]
}

impl Memory {
    pub fn new() -> Memory {
        Memory {
            ram: [0; RAM_SIZE],
            ppu_ctrl_registers: [0; PPU_CTRL_REGISTERS_SIZE],
            misc_ctrl_registers: [0; MISC_CTRL_REGISTERS_SIZE],
            expansion_rom: [0; EXPANSION_ROM_SIZE],
            sram: [0; SRAM_SIZE],
            prg_rom_1: [0; PRG_ROM_SIZE],
            prg_rom_2: [0; PRG_ROM_SIZE]
        }
    }

    /// Reads an unsigned 8-bit byte value located at the given virtual address.
    #[inline(always)]
    pub fn read_u8(&mut self, addr: usize) -> u8 {
        let (bank, idx) = self.map(addr);
        bank[idx]
    }

    /// Writes an unsigned 8-bit byte value to the given virtual address.
    #[inline(always)]
    pub fn write_u8(&mut self, addr: usize, val: u8) {
        let (bank, idx) = self.map(addr);
        bank[idx] = val;
    }

    /// Reads an unsigned 16-bit byte value at the given virtual address
    /// (little-endian).
    #[inline(always)]
    pub fn read_u16(&mut self, addr: usize) -> u16 {
        // Reads two bytes starting at the given address and parses them.
        let mut reader = Cursor::new(vec![
            self.read_u8(addr),
            self.read_u8(addr + 1)
        ]);
        reader.read_u16::<LittleEndian>().unwrap()
    }

    /// Reads an unsigned 16-bit byte value at the given virtual address
    /// (little-endian) where the MSB is read at page start if the LSB is at
    /// the end of a page. This exists to properly emulate a hardware bug in the
    /// 2A03 where indirect jumps cannot fetch addresses outside it's own page.
    #[inline(always)]
    pub fn read_u16_wrapped_msb(&mut self, addr: usize) -> u16 {
        let lsb = self.read_u8(addr);
        let msb = if addr & 0xFF == 0xFF {
            self.read_u8(addr - 0xFF)
        } else {
            self.read_u8(addr + 1)
        };

        // Reads two bytes starting at the given address and parses them.
        let mut reader = Cursor::new(vec![lsb, msb]);
        reader.read_u16::<LittleEndian>().unwrap()
    }

    /// Writes an unsigned 16-bit byte value to the given virtual address
    /// (little-endian)
    #[inline(always)]
    pub fn write_u16(&mut self, addr: usize, val: u16) {
        let mut writer = vec![];
        writer.write_u16::<LittleEndian>(val).unwrap();
        self.write_u8(addr, writer[0]);
        self.write_u8(addr + 1, writer[1]);
    }

    /// Dumps the contents of a slice starting at a given address.
    pub fn memdump(&mut self, addr: usize, buf: &[u8]) {
        for i in 0..buf.len() {
            self.write_u8(addr + i, buf[i]);
        }
    }

    // Utility functions for managing the stack.

    /// Pushes an 8-bit number onto the stack.
    pub fn stack_push_u8(&mut self, cpu: &mut CPU, value: u8) {
        self.write_u8(STACK_OFFSET + cpu.sp as usize, value);
        cpu.sp = cpu.sp.wrapping_sub(1);
    }

    /// Pops an 8-bit number off the stack.
    pub fn stack_pop_u8(&mut self, cpu: &mut CPU) -> u8 {
        cpu.sp = cpu.sp.wrapping_add(1);
        self.read_u8(STACK_OFFSET + cpu.sp as usize)
    }

    /// Pushes a 16-bit number (usually an address) onto the stack.
    pub fn stack_push_u16(&mut self, cpu: &mut CPU, value: u16) {
        self.write_u16(STACK_OFFSET + cpu.sp as usize, value);
        cpu.sp = cpu.sp.wrapping_sub(2);
    }

    /// Pops a 16-bit number (usually an address) off the stack.
    pub fn stack_pop_u16(&mut self, cpu: &mut CPU) -> u16 {
        cpu.sp = cpu.sp.wrapping_add(2);
        self.read_u16(STACK_OFFSET + cpu.sp as usize)
    }

    /// Maps a given virtual address to a physical address internal to the
    /// emulator. Returns a memory buffer and index for physical memory access.
    ///
    /// TODO: Add permissions flag for special addresses?
    fn map(&mut self, addr: usize) -> (&mut [u8], usize) {
        match addr {
            RAM_START_ADDR...RAM_END_ADDR =>
                (&mut self.ram, addr),
            RAM_MIRROR_START...RAM_MIRROR_END =>
                (&mut self.ram, addr % RAM_SIZE),
            PPU_CTRL_REGISTERS_START...PPU_CTRL_REGISTERS_END =>
                (&mut self.ppu_ctrl_registers, addr - PPU_CTRL_REGISTERS_START),
            PPU_CTRL_REGISTERS_MIRROR_START...PPU_CTRL_REGISTERS_MIRROR_END =>
                (&mut self.ppu_ctrl_registers, (addr - PPU_CTRL_REGISTERS_START) % PPU_CTRL_REGISTERS_SIZE),
            MISC_CTRL_REGISTERS_START...MISC_CTRL_REGISTERS_END =>
                (&mut self.misc_ctrl_registers, addr - MISC_CTRL_REGISTERS_START),
            EXPANSION_ROM_START...EXPANSION_ROM_END =>
                (&mut self.expansion_rom, addr - EXPANSION_ROM_START),
            SRAM_START...SRAM_END =>
                (&mut self.sram, addr - SRAM_START),
            PRG_ROM_1_START...PRG_ROM_1_END =>
                (&mut self.prg_rom_1, addr - PRG_ROM_1_START),
            PRG_ROM_2_START...PRG_ROM_2_END =>
                (&mut self.prg_rom_2, addr - PRG_ROM_2_START),
            _ => { panic!("Unable to map virtual address {:#X} to any physical address", addr) }
        }
    }
}
<|endoftext|>


<commit_before>mod memory;
mod stack;
mod pc;
mod storage;
mod params;
mod eval;
mod commit;
pub mod errors;

pub use self::memory::{Memory, SeqMemory};
pub use self::stack::Stack;
pub use self::pc::{PC, Instruction};
pub use self::storage::{Storage, HashMapStorage};
pub use self::params::{Context, BlockHeader, Log, Patch};
pub use self::eval::{State, Machine, MachineStatus};
pub use self::commit::{AccountCommitment, Account};

use std::collections::hash_map;
use utils::bigint::M256;
use utils::gas::Gas;
use utils::address::Address;
use self::errors::{RequireError, CommitError, VMError};

pub type SeqVM = VM<SeqMemory, HashMapStorage>;

pub struct VM<M, S>(Vec<Machine<M, S>>, Vec<Context>);

#[derive(Debug, Clone)]
pub enum VMStatus {
    Running,
    ExitedOk,
    ExitedErr(VMError),
}

impl<M: Memory + Default, S: Storage + Default + Clone> VM<M, S> {
    pub fn new(context: Context, block: BlockHeader, patch: Patch) -> VM<M, S> {
        let mut machines = Vec::new();
        machines.push(Machine::new(context, block, patch, 0));
        VM(machines, Vec::new())
    }

    pub fn commit_account(&mut self, commitment: AccountCommitment<S>) -> Result<(), CommitError> {
        for machine in &mut self.0 {
            machine.commit_account(commitment.clone())?;
        }
        Ok(())
    }

    pub fn commit_blockhash(&mut self, number: M256, hash: M256) -> Result<(), CommitError> {
        for machine in &mut self.0 {
            machine.commit_blockhash(number, hash)?;
        }
        Ok(())
    }

    pub fn status(&self) -> VMStatus {
        match self.0[0].status() {
            MachineStatus::Running | MachineStatus::InvokeCreate(_) | MachineStatus::InvokeCall(_, _) => VMStatus::Running,
            MachineStatus::ExitedOk => VMStatus::ExitedOk,
            MachineStatus::ExitedErr(err) => VMStatus::ExitedErr(err.into()),
        }
    }

    pub fn step(&mut self) -> Result<(), RequireError> {
        if self.0.len() > 1024 {
            return Ok(());
        }
        match self.0.last().unwrap().status().clone() {
            MachineStatus::Running => {
                self.0.last_mut().unwrap().step()
            },
            MachineStatus::ExitedOk | MachineStatus::ExitedErr(_) => {
                if self.0.len() <= 1 {
                    Ok(())
                } else {
                    let finished = self.0.pop().unwrap();
                    self.0.last_mut().unwrap().apply_sub(finished);
                    Ok(())
                }
            },
            MachineStatus::InvokeCall(context, _) | MachineStatus::InvokeCreate(context) => {
                self.1.push(context.clone());
                let sub = self.0.last().unwrap().derive(context);
                self.0.push(sub);
                Ok(())
            },
        }
    }

    pub fn fire(&mut self) -> Result<(), RequireError> {
        loop {
            match self.status() {
                VMStatus::Running => self.step()?,
                VMStatus::ExitedOk | VMStatus::ExitedErr(_) => return Ok(()),
            }
        }
    }

    pub fn accounts(&self) -> hash_map::Values<Address, Account<S>> {
        self.0[0].state().account_state.accounts()
    }

    pub fn out(&self) -> &[u8] {
        self.0[0].state().out.as_slice()
    }

    pub fn available_gas(&self) -> Gas {
        self.0[0].state().available_gas()
    }

    pub fn refunded_gas(&self) -> Gas {
        self.0[0].state().refunded_gas
    }

    pub fn logs(&self) -> &[Log] {
        self.0[0].state().logs.as_slice()
    }

    pub fn history(&self) -> &[Context] {
        self.1.as_slice()
    }
}
<commit_msg>Problem: VM can still exceed 1024 callstack Solution: panic! when it happens<commit_after>mod memory;
mod stack;
mod pc;
mod storage;
mod params;
mod eval;
mod commit;
pub mod errors;

pub use self::memory::{Memory, SeqMemory};
pub use self::stack::Stack;
pub use self::pc::{PC, Instruction};
pub use self::storage::{Storage, HashMapStorage};
pub use self::params::{Context, BlockHeader, Log, Patch};
pub use self::eval::{State, Machine, MachineStatus};
pub use self::commit::{AccountCommitment, Account};

use std::collections::hash_map;
use utils::bigint::M256;
use utils::gas::Gas;
use utils::address::Address;
use self::errors::{RequireError, CommitError, VMError};

pub type SeqVM = VM<SeqMemory, HashMapStorage>;

pub struct VM<M, S>(Vec<Machine<M, S>>, Vec<Context>);

#[derive(Debug, Clone)]
pub enum VMStatus {
    Running,
    ExitedOk,
    ExitedErr(VMError),
}

impl<M: Memory + Default, S: Storage + Default + Clone> VM<M, S> {
    pub fn new(context: Context, block: BlockHeader, patch: Patch) -> VM<M, S> {
        let mut machines = Vec::new();
        machines.push(Machine::new(context, block, patch, 0));
        VM(machines, Vec::new())
    }

    pub fn commit_account(&mut self, commitment: AccountCommitment<S>) -> Result<(), CommitError> {
        for machine in &mut self.0 {
            machine.commit_account(commitment.clone())?;
        }
        Ok(())
    }

    pub fn commit_blockhash(&mut self, number: M256, hash: M256) -> Result<(), CommitError> {
        for machine in &mut self.0 {
            machine.commit_blockhash(number, hash)?;
        }
        Ok(())
    }

    pub fn status(&self) -> VMStatus {
        match self.0[0].status() {
            MachineStatus::Running | MachineStatus::InvokeCreate(_) | MachineStatus::InvokeCall(_, _) => VMStatus::Running,
            MachineStatus::ExitedOk => VMStatus::ExitedOk,
            MachineStatus::ExitedErr(err) => VMStatus::ExitedErr(err.into()),
        }
    }

    pub fn step(&mut self) -> Result<(), RequireError> {
        if self.0.len() >= 1024 {
            panic!();
        }
        match self.0.last().unwrap().status().clone() {
            MachineStatus::Running => {
                self.0.last_mut().unwrap().step()
            },
            MachineStatus::ExitedOk | MachineStatus::ExitedErr(_) => {
                if self.0.len() <= 1 {
                    Ok(())
                } else {
                    let finished = self.0.pop().unwrap();
                    self.0.last_mut().unwrap().apply_sub(finished);
                    Ok(())
                }
            },
            MachineStatus::InvokeCall(context, _) | MachineStatus::InvokeCreate(context) => {
                self.1.push(context.clone());
                let sub = self.0.last().unwrap().derive(context);
                self.0.push(sub);
                Ok(())
            },
        }
    }

    pub fn fire(&mut self) -> Result<(), RequireError> {
        loop {
            match self.status() {
                VMStatus::Running => self.step()?,
                VMStatus::ExitedOk | VMStatus::ExitedErr(_) => return Ok(()),
            }
        }
    }

    pub fn accounts(&self) -> hash_map::Values<Address, Account<S>> {
        self.0[0].state().account_state.accounts()
    }

    pub fn out(&self) -> &[u8] {
        self.0[0].state().out.as_slice()
    }

    pub fn available_gas(&self) -> Gas {
        self.0[0].state().available_gas()
    }

    pub fn refunded_gas(&self) -> Gas {
        self.0[0].state().refunded_gas
    }

    pub fn logs(&self) -> &[Log] {
        self.0[0].state().logs.as_slice()
    }

    pub fn history(&self) -> &[Context] {
        self.1.as_slice()
    }
}
<|endoftext|>


<commit_before>use std::ops::Deref;

use cgmath::Point2;

use crate::bsdf::BSDF;
use crate::color::Color;
use crate::float::*;
use crate::obj_load;
use crate::texture::Texture;

mod diffuse;
mod glossy;
mod specular;

use self::diffuse::*;
use self::glossy::*;
use self::specular::*;

/// Scattering model over the whole surface
pub trait ScatteringT {
    /// Get the local scattering functions
    fn local(&self, tex_coords: Point2<Float>) -> BSDF;
    /// The texture to use for preview rendering
    fn preview_texture(&self) -> &Texture;
}

#[derive(Debug)]
#[allow(dead_code)]
pub enum Scattering {
    DR(DiffuseReflection),
    GB(GlossyBlend),
    GR(GlossyReflection),
    GT(GlossyTransmission),
    SR(SpecularReflection),
    ST(SpecularTransmission),
}

fn diffuse_texture(obj_mat: &obj_load::Material) -> Texture {
    match &obj_mat.diffuse_texture {
        Some(path) => Texture::from_image_path(path),
        None => {
            let color = Color::from(obj_mat.diffuse_color.unwrap_or([0.0, 0.0, 0.0]));
            Texture::from_color(color)
        }
    }
}

fn specular_texture(obj_mat: &obj_load::Material) -> Texture {
    match &obj_mat.specular_texture {
        Some(path) => Texture::from_image_path(path),
        None => {
            let color = Color::from(obj_mat.specular_color.unwrap_or([0.0, 0.0, 0.0]));
            Texture::from_color(color)
        }
    }
}

fn transmission_filter(obj_mat: &obj_load::Material) -> Texture {
    let mut color = Color::from(
        obj_mat
            .transmission_filter
            .expect("No transmission filter for transmissive material"),
    );
    // MTL spec states that transmission filter defines the fraction of light
    // that is able to pass through the surface, but some scenes seem to interpret
    // it as the opposite. So we flip all low valued filters.
    if color.r() < 0.4 && color.g() < 0.4 && color.b() < 0.4 {
        println!("Flipped transmission filter!");
        color = Color::white() - color;
    }
    Texture::from_color(color)
}

impl Scattering {
    pub fn from_obj(obj_mat: &obj_load::Material) -> Self {
        use self::Scattering::*;

        let diffuse = diffuse_texture(obj_mat);
        let specular = specular_texture(obj_mat);
        match obj_mat.illumination_model {
            Some(2) => {
                let exponent = obj_mat.specular_exponent.map(|e| e.to_float());
                if diffuse.is_black() {
                    GR(GlossyReflection::new(specular, exponent.unwrap()))
                } else if specular.is_black() {
                    DR(DiffuseReflection::new(diffuse))
                } else {
                    GB(GlossyBlend::new(diffuse, specular, exponent.unwrap()))
                }
            }
            Some(5) => {
                let texture = specular_texture(obj_mat);
                SR(SpecularReflection::new(texture))
            }
            Some(4) | Some(9) => {
                let filter = transmission_filter(obj_mat);
                let eta = obj_mat
                    .index_of_refraction
                    .expect("No index of refraction for translucent material")
                    .to_float();
                ST(SpecularTransmission::new(specular, filter, eta))
            }
            Some(6) | Some(7) => {
                let filter = transmission_filter(obj_mat);
                let eta = obj_mat
                    .index_of_refraction
                    .expect("No index of refraction for translucent material")
                    .to_float();
                if (eta - 1.0).abs() < crate::consts::EPSILON {
                    // Glossy does not handle eta = 1 properly
                    // and the distribution would be the same anyways
                    ST(SpecularTransmission::new(specular, filter, eta))
                } else {
                    let exponent = obj_mat.specular_exponent.unwrap().to_float();
                    GT(GlossyTransmission::new(specular, filter, exponent, eta))
                }
            }
            Some(i) => {
                if i > 10 {
                    println!("Illumination model {} is not defined in the mtl spec!", i);
                } else {
                    println!("Unimplemented illumination model {}!", i);
                }
                println!("Defaulting to diffuse reflection.");
                DR(DiffuseReflection::new(diffuse))
            }
            None => DR(DiffuseReflection::new(diffuse)),
        }
    }
}

impl Deref for Scattering {
    type Target = dyn ScatteringT;

    fn deref(&self) -> &Self::Target {
        use self::Scattering::*;
        match self {
            DR(inner) => inner,
            GB(inner) => inner,
            GR(inner) => inner,
            GT(inner) => inner,
            SR(inner) => inner,
            ST(inner) => inner,
        }
    }
}
<commit_msg>Don't print unimplemented for illumination model 1<commit_after>use std::ops::Deref;

use cgmath::Point2;

use crate::bsdf::BSDF;
use crate::color::Color;
use crate::float::*;
use crate::obj_load;
use crate::texture::Texture;

mod diffuse;
mod glossy;
mod specular;

use self::diffuse::*;
use self::glossy::*;
use self::specular::*;

/// Scattering model over the whole surface
pub trait ScatteringT {
    /// Get the local scattering functions
    fn local(&self, tex_coords: Point2<Float>) -> BSDF;
    /// The texture to use for preview rendering
    fn preview_texture(&self) -> &Texture;
}

#[derive(Debug)]
#[allow(dead_code)]
pub enum Scattering {
    DR(DiffuseReflection),
    GB(GlossyBlend),
    GR(GlossyReflection),
    GT(GlossyTransmission),
    SR(SpecularReflection),
    ST(SpecularTransmission),
}

fn diffuse_texture(obj_mat: &obj_load::Material) -> Texture {
    match &obj_mat.diffuse_texture {
        Some(path) => Texture::from_image_path(path),
        None => {
            let color = Color::from(obj_mat.diffuse_color.unwrap_or([0.0, 0.0, 0.0]));
            Texture::from_color(color)
        }
    }
}

fn specular_texture(obj_mat: &obj_load::Material) -> Texture {
    match &obj_mat.specular_texture {
        Some(path) => Texture::from_image_path(path),
        None => {
            let color = Color::from(obj_mat.specular_color.unwrap_or([0.0, 0.0, 0.0]));
            Texture::from_color(color)
        }
    }
}

fn transmission_filter(obj_mat: &obj_load::Material) -> Texture {
    let mut color = Color::from(
        obj_mat
            .transmission_filter
            .expect("No transmission filter for transmissive material"),
    );
    // MTL spec states that transmission filter defines the fraction of light
    // that is able to pass through the surface, but some scenes seem to interpret
    // it as the opposite. So we flip all low valued filters.
    if color.r() < 0.4 && color.g() < 0.4 && color.b() < 0.4 {
        println!("Flipped transmission filter!");
        color = Color::white() - color;
    }
    Texture::from_color(color)
}

impl Scattering {
    pub fn from_obj(obj_mat: &obj_load::Material) -> Self {
        use self::Scattering::*;

        let diffuse = diffuse_texture(obj_mat);
        let specular = specular_texture(obj_mat);
        match obj_mat.illumination_model {
            Some(2) => {
                let exponent = obj_mat.specular_exponent.map(|e| e.to_float());
                if diffuse.is_black() {
                    GR(GlossyReflection::new(specular, exponent.unwrap()))
                } else if specular.is_black() {
                    DR(DiffuseReflection::new(diffuse))
                } else {
                    GB(GlossyBlend::new(diffuse, specular, exponent.unwrap()))
                }
            }
            Some(5) => {
                let texture = specular_texture(obj_mat);
                SR(SpecularReflection::new(texture))
            }
            Some(4) | Some(9) => {
                let filter = transmission_filter(obj_mat);
                let eta = obj_mat
                    .index_of_refraction
                    .expect("No index of refraction for translucent material")
                    .to_float();
                ST(SpecularTransmission::new(specular, filter, eta))
            }
            Some(6) | Some(7) => {
                let filter = transmission_filter(obj_mat);
                let eta = obj_mat
                    .index_of_refraction
                    .expect("No index of refraction for translucent material")
                    .to_float();
                if (eta - 1.0).abs() < crate::consts::EPSILON {
                    // Glossy does not handle eta = 1 properly
                    // and the distribution would be the same anyways
                    ST(SpecularTransmission::new(specular, filter, eta))
                } else {
                    let exponent = obj_mat.specular_exponent.unwrap().to_float();
                    GT(GlossyTransmission::new(specular, filter, exponent, eta))
                }
            }
            Some(i) => {
                if i > 10 {
                    println!("Illumination model {} is not defined in the mtl spec!", i);
                    println!("Defaulting to diffuse reflection.");
                } else if i != 1 {
                    println!("Unimplemented illumination model {}!", i);
                    println!("Defaulting to diffuse reflection.");
                }
                DR(DiffuseReflection::new(diffuse))
            }
            None => DR(DiffuseReflection::new(diffuse)),
        }
    }
}

impl Deref for Scattering {
    type Target = dyn ScatteringT;

    fn deref(&self) -> &Self::Target {
        use self::Scattering::*;
        match self {
            DR(inner) => inner,
            GB(inner) => inner,
            GR(inner) => inner,
            GT(inner) => inner,
            SR(inner) => inner,
            ST(inner) => inner,
        }
    }
}
<|endoftext|>


<commit_before>use std::cell::{Cell, RefCell};

use cairo;
use markup5ever::{local_name, LocalName};

use crate::drawing_ctx::DrawingCtx;
use crate::error::NodeError;
use crate::node::{NodeResult, NodeTrait, RsvgNode};
use crate::parsers::ParseError;
use crate::property_bag::PropertyBag;
use crate::surface_utils::shared_surface::SharedImageSurface;

use super::context::{FilterContext, FilterOutput, FilterResult};
use super::input::Input;
use super::{Filter, FilterError, PrimitiveWithInput};

/// Enumeration of the possible blending modes.
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
enum Mode {
    Normal,
    Multiply,
    Screen,
    Darken,
    Lighten,
}

/// The `feBlend` filter primitive.
pub struct Blend {
    base: PrimitiveWithInput,
    in2: RefCell<Option<Input>>,
    mode: Cell<Mode>,
}

impl Default for Blend {
    /// Constructs a new `Blend` with empty properties.
    #[inline]
    fn default() -> Blend {
        Blend {
            base: PrimitiveWithInput::new::<Self>(),
            in2: RefCell::new(None),
            mode: Cell::new(Mode::Normal),
        }
    }
}

impl NodeTrait for Blend {
    impl_node_as_filter!();

    fn set_atts(&mut self, parent: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> NodeResult {
        self.base.set_atts(parent, pbag)?;

        for (attr, value) in pbag.iter() {
            match attr {
                local_name!("in2") => {
                    self.in2.replace(Some(Input::parse(attr, value)?));
                }
                local_name!("mode") => self.mode.set(Mode::parse(attr, value)?),
                _ => (),
            }
        }

        Ok(())
    }
}

impl Filter for Blend {
    fn render(
        &self,
        _node: &RsvgNode,
        ctx: &FilterContext,
        draw_ctx: &mut DrawingCtx,
    ) -> Result<FilterResult, FilterError> {
        let input = self.base.get_input(ctx, draw_ctx)?;
        let input_2 = ctx.get_input(draw_ctx, self.in2.borrow().as_ref())?;
        let bounds = self
            .base
            .get_bounds(ctx)
            .add_input(&input)
            .add_input(&input_2)
            .into_irect(draw_ctx);

        // If we're combining two alpha-only surfaces, the result is alpha-only. Otherwise the
        // result is whatever the non-alpha-only type we're working on (which can be either sRGB or
        // linear sRGB depending on color-interpolation-filters).
        let surface_type = if input.surface().is_alpha_only() {
            input_2.surface().surface_type()
        } else {
            if !input_2.surface().is_alpha_only() {
                // All surface types should match (this is enforced by get_input()).
                assert_eq!(
                    input_2.surface().surface_type(),
                    input.surface().surface_type()
                );
            }

            input.surface().surface_type()
        };

        let output_surface = input_2.surface().copy_surface(bounds)?;
        {
            let cr = cairo::Context::new(&output_surface);
            cr.rectangle(
                bounds.x0 as f64,
                bounds.y0 as f64,
                (bounds.x1 - bounds.x0) as f64,
                (bounds.y1 - bounds.y0) as f64,
            );
            cr.clip();

            input.surface().set_as_source_surface(&cr, 0f64, 0f64);
            cr.set_operator(self.mode.get().into());
            cr.paint();
        }

        Ok(FilterResult {
            name: self.base.result.borrow().clone(),
            output: FilterOutput {
                surface: SharedImageSurface::new(output_surface, surface_type)?,
                bounds,
            },
        })
    }

    #[inline]
    fn is_affected_by_color_interpolation_filters(&self) -> bool {
        true
    }
}

impl Mode {
    fn parse(attr: LocalName, s: &str) -> Result<Self, NodeError> {
        match s {
            "normal" => Ok(Mode::Normal),
            "multiply" => Ok(Mode::Multiply),
            "screen" => Ok(Mode::Screen),
            "darken" => Ok(Mode::Darken),
            "lighten" => Ok(Mode::Lighten),
            _ => Err(NodeError::parse_error(
                attr,
                ParseError::new("invalid value"),
            )),
        }
    }
}

impl From<Mode> for cairo::Operator {
    #[inline]
    fn from(x: Mode) -> Self {
        match x {
            Mode::Normal => cairo::Operator::Over,
            Mode::Multiply => cairo::Operator::Multiply,
            Mode::Screen => cairo::Operator::Screen,
            Mode::Darken => cairo::Operator::Darken,
            Mode::Lighten => cairo::Operator::Lighten,
        }
    }
}
<commit_msg>filters/blend: remove interior mutability<commit_after>use cairo;
use markup5ever::{local_name, LocalName};

use crate::drawing_ctx::DrawingCtx;
use crate::error::NodeError;
use crate::node::{NodeResult, NodeTrait, RsvgNode};
use crate::parsers::ParseError;
use crate::property_bag::PropertyBag;
use crate::surface_utils::shared_surface::SharedImageSurface;

use super::context::{FilterContext, FilterOutput, FilterResult};
use super::input::Input;
use super::{Filter, FilterError, PrimitiveWithInput};

/// Enumeration of the possible blending modes.
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
enum Mode {
    Normal,
    Multiply,
    Screen,
    Darken,
    Lighten,
}

/// The `feBlend` filter primitive.
pub struct Blend {
    base: PrimitiveWithInput,
    in2: Option<Input>,
    mode: Mode,
}

impl Default for Blend {
    /// Constructs a new `Blend` with empty properties.
    #[inline]
    fn default() -> Blend {
        Blend {
            base: PrimitiveWithInput::new::<Self>(),
            in2: None,
            mode: Mode::Normal,
        }
    }
}

impl NodeTrait for Blend {
    impl_node_as_filter!();

    fn set_atts(&mut self, parent: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> NodeResult {
        self.base.set_atts(parent, pbag)?;

        for (attr, value) in pbag.iter() {
            match attr {
                local_name!("in2") => {
                    self.in2 = Some(Input::parse(attr, value)?);
                }
                local_name!("mode") => self.mode = Mode::parse(attr, value)?,
                _ => (),
            }
        }

        Ok(())
    }
}

impl Filter for Blend {
    fn render(
        &self,
        _node: &RsvgNode,
        ctx: &FilterContext,
        draw_ctx: &mut DrawingCtx,
    ) -> Result<FilterResult, FilterError> {
        let input = self.base.get_input(ctx, draw_ctx)?;
        let input_2 = ctx.get_input(draw_ctx, self.in2.as_ref())?;
        let bounds = self
            .base
            .get_bounds(ctx)
            .add_input(&input)
            .add_input(&input_2)
            .into_irect(draw_ctx);

        // If we're combining two alpha-only surfaces, the result is alpha-only. Otherwise the
        // result is whatever the non-alpha-only type we're working on (which can be either sRGB or
        // linear sRGB depending on color-interpolation-filters).
        let surface_type = if input.surface().is_alpha_only() {
            input_2.surface().surface_type()
        } else {
            if !input_2.surface().is_alpha_only() {
                // All surface types should match (this is enforced by get_input()).
                assert_eq!(
                    input_2.surface().surface_type(),
                    input.surface().surface_type()
                );
            }

            input.surface().surface_type()
        };

        let output_surface = input_2.surface().copy_surface(bounds)?;
        {
            let cr = cairo::Context::new(&output_surface);
            cr.rectangle(
                bounds.x0 as f64,
                bounds.y0 as f64,
                (bounds.x1 - bounds.x0) as f64,
                (bounds.y1 - bounds.y0) as f64,
            );
            cr.clip();

            input.surface().set_as_source_surface(&cr, 0f64, 0f64);
            cr.set_operator(self.mode.into());
            cr.paint();
        }

        Ok(FilterResult {
            name: self.base.result.borrow().clone(),
            output: FilterOutput {
                surface: SharedImageSurface::new(output_surface, surface_type)?,
                bounds,
            },
        })
    }

    #[inline]
    fn is_affected_by_color_interpolation_filters(&self) -> bool {
        true
    }
}

impl Mode {
    fn parse(attr: LocalName, s: &str) -> Result<Self, NodeError> {
        match s {
            "normal" => Ok(Mode::Normal),
            "multiply" => Ok(Mode::Multiply),
            "screen" => Ok(Mode::Screen),
            "darken" => Ok(Mode::Darken),
            "lighten" => Ok(Mode::Lighten),
            _ => Err(NodeError::parse_error(
                attr,
                ParseError::new("invalid value"),
            )),
        }
    }
}

impl From<Mode> for cairo::Operator {
    #[inline]
    fn from(x: Mode) -> Self {
        match x {
            Mode::Normal => cairo::Operator::Over,
            Mode::Multiply => cairo::Operator::Multiply,
            Mode::Screen => cairo::Operator::Screen,
            Mode::Darken => cairo::Operator::Darken,
            Mode::Lighten => cairo::Operator::Lighten,
        }
    }
}
<|endoftext|>