<commit_before>use std::collections::{BTreeMap, HashSet};
use std::ops::Not;

use cranelift_codegen::cursor::{Cursor, FuncCursor};
use cranelift_codegen::entity::EntitySet;
use cranelift_codegen::ir::{InstructionData, Opcode, ProgramOrder, ValueDef};
use cranelift_codegen::ir::immediates::Offset32;

use crate::prelude::*;

/// Workaround for `StackSlot` not implementing `Ord`.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
struct OrdStackSlot(StackSlot);

impl PartialOrd for OrdStackSlot {
    fn partial_cmp(&self, rhs: &Self) -> Option<std::cmp::Ordering> {
        self.0.as_u32().partial_cmp(&rhs.0.as_u32())
    }
}

impl Ord for OrdStackSlot {
    fn cmp(&self, rhs: &Self) -> std::cmp::Ordering {
        self.0.as_u32().cmp(&rhs.0.as_u32())
    }
}

#[derive(Debug, Default)]
struct StackSlotUsage {
    stack_addr: HashSet<Inst>,
    stack_load: HashSet<Inst>,
    stack_store: HashSet<Inst>,
}

pub(super) fn optimize_function(
    ctx: &mut Context,
    clif_comments: &mut crate::pretty_clif::CommentWriter,
    name: String, // FIXME remove
) {
    ctx.flowgraph(); // Compute cfg and domtree.

    combine_stack_addr_with_load_store(&mut ctx.func);

    // Record all stack_addr, stack_load and stack_store instructions.
    let mut stack_slot_usage_map = BTreeMap::<OrdStackSlot, StackSlotUsage>::new();

    let mut cursor = FuncCursor::new(&mut ctx.func);
    while let Some(_ebb) = cursor.next_ebb() {
        while let Some(inst) = cursor.next_inst() {
            match cursor.func.dfg[inst] {
                InstructionData::StackLoad {
                    opcode: Opcode::StackAddr,
                    stack_slot,
                    offset: _,
                } => {
                    stack_slot_usage_map.entry(OrdStackSlot(stack_slot)).or_insert_with(StackSlotUsage::default).stack_addr.insert(inst);
                }
                InstructionData::StackLoad {
                    opcode: Opcode::StackLoad,
                    stack_slot,
                    offset: _,
                } => {
                    stack_slot_usage_map.entry(OrdStackSlot(stack_slot)).or_insert_with(StackSlotUsage::default).stack_load.insert(inst);
                }
                InstructionData::StackStore {
                    opcode: Opcode::StackStore,
                    arg: _,
                    stack_slot,
                    offset: _,
                } => {
                    stack_slot_usage_map.entry(OrdStackSlot(stack_slot)).or_insert_with(StackSlotUsage::default).stack_store.insert(inst);
                }
                _ => {}
            }
        }
    }

    // FIXME Repeat following instructions until fixpoint.

    println!("{}:\nstack slot usage: {:?}", name, stack_slot_usage_map);

    remove_unused_stack_addr_and_stack_load(&mut ctx.func, &mut stack_slot_usage_map);

    println!("stack slot usage (after): {:?}", stack_slot_usage_map);

    for (stack_slot, users) in stack_slot_usage_map.iter_mut() {
        if users.stack_addr.is_empty().not() {
            // Stack addr leaked; there may be unknown loads and stores.
            // FIXME use stacked borrows to optimize
            continue;
        }

        for load in users.stack_load.clone().drain() {
            let load_ebb = ctx.func.layout.inst_ebb(load).unwrap();
            let loaded_value = ctx.func.dfg.inst_results(load)[0];
            let loaded_type = ctx.func.dfg.value_type(loaded_value);

            let potential_stores = users.stack_store.iter().cloned().filter(|&store| {
                // Check if the store modified some memory accessed by the load.

                match spatial_overlap(&ctx.func, load, store) {
                    SpatialOverlap::No => false, // Can never be the source of the loaded value.
                    SpatialOverlap::Partial | SpatialOverlap::Full => true,
                }
            }).filter(|&store| {
                match temporal_order(&*ctx, load, store) {
                    TemporalOrder::NeverBefore => false, // Can never be the source of the loaded value.
                    TemporalOrder::MaybeBefore | TemporalOrder::DefinitivelyBefore => true,
                }
            }).collect::<Vec<Inst>>();
            for &store in &potential_stores {
                println!(
                    "Potential store -> load forwarding {} -> {} ({:?}, {:?})",
                    ctx.func.dfg.display_inst(store, None),
                    ctx.func.dfg.display_inst(load, None),
                    spatial_overlap(&ctx.func, store, load),
                    temporal_order(&*ctx, store, load),
                );
            }
            match *potential_stores {
                [] => println!("[{}] [BUG?] Reading uninitialized memory", name),
                [store] if spatial_overlap(&ctx.func, load, store) == SpatialOverlap::Full && temporal_order(&ctx, load, store) == TemporalOrder::DefinitivelyBefore => {
                    let store_ebb = ctx.func.layout.inst_ebb(store).unwrap();
                    let stored_value = ctx.func.dfg.inst_args(store)[0];
                    let stored_type = ctx.func.dfg.value_type(stored_value);
                    if stored_type == loaded_type && store_ebb == load_ebb {
                        println!("Store to load forward {} -> {}", store, load);
                        ctx.func.dfg.detach_results(load);
                        ctx.func.dfg.replace(load).nop();
                        ctx.func.dfg.change_to_alias(loaded_value, stored_value);
                        users.stack_load.remove(&load);
                    }
                }
                _ => {} // FIXME implement this
            }
        }

        if users.stack_load.is_empty() {
            // Never loaded; can safely remove all stores and the stack slot.
            // FIXME also remove stores when there is always a next store before a load.
            for user in users.stack_store.drain() {
                println!("[{}] Remove dead stack store {} of {}", name, user, stack_slot.0);
                ctx.func.dfg.replace(user).nop();
            }
        }

        if users.stack_store.is_empty() && users.stack_load.is_empty() {
            // FIXME make stack_slot zero sized.
        }
    }

    println!();
}

fn combine_stack_addr_with_load_store(func: &mut Function) {
    // Turn load and store into stack_load and stack_store when possible.
    let mut cursor = FuncCursor::new(func);
    while let Some(_ebb) = cursor.next_ebb() {
        while let Some(inst) = cursor.next_inst() {
            match cursor.func.dfg[inst] {
                InstructionData::Load { opcode: Opcode::Load, arg: addr, flags: _, offset } => {
                    if cursor.func.dfg.ctrl_typevar(inst) == types::I128 || cursor.func.dfg.ctrl_typevar(inst).is_vector() {
                        continue; // WORKAROUD: stack_load.i128 not yet implemented
                    }
                    if let Some((stack_slot, stack_addr_offset)) = try_get_stack_slot_and_offset_for_addr(cursor.func, addr) {
                        if let Some(combined_offset) = offset.try_add_i64(stack_addr_offset.into()) {
                            let ty = cursor.func.dfg.ctrl_typevar(inst);
                            cursor.func.dfg.replace(inst).stack_load(ty, stack_slot, combined_offset);
                        }
                    }
                }
                InstructionData::Store { opcode: Opcode::Store, args: [value, addr], flags: _, offset } => {
                    if cursor.func.dfg.ctrl_typevar(inst) == types::I128 || cursor.func.dfg.ctrl_typevar(inst).is_vector() {
                        continue; // WORKAROUND: stack_store.i128 not yet implemented
                    }
                    if let Some((stack_slot, stack_addr_offset)) = try_get_stack_slot_and_offset_for_addr(cursor.func, addr) {
                        if let Some(combined_offset) = offset.try_add_i64(stack_addr_offset.into()) {
                            cursor.func.dfg.replace(inst).stack_store(value, stack_slot, combined_offset);
                        }
                    }
                }
                _ => {}
            }
        }
    }
}

fn remove_unused_stack_addr_and_stack_load(func: &mut Function, stack_slot_usage_map: &mut BTreeMap<OrdStackSlot, StackSlotUsage>) {
    // FIXME incrementally rebuild on each call?
    let mut stack_addr_load_insts_users = HashMap::<Inst, HashSet<Inst>>::new();

    let mut cursor = FuncCursor::new(func);
    while let Some(_ebb) = cursor.next_ebb() {
        while let Some(inst) = cursor.next_inst() {
            for &arg in cursor.func.dfg.inst_args(inst) {
                if let ValueDef::Result(arg_origin, 0) = cursor.func.dfg.value_def(arg) {
                    match cursor.func.dfg[arg_origin].opcode() {
                        Opcode::StackAddr | Opcode::StackLoad => {
                            stack_addr_load_insts_users.entry(arg_origin).or_insert_with(HashSet::new).insert(inst);
                        }
                        _ => {}
                    }
                }
            }
        }
    }

    println!("stack_addr/stack_load users: {:?}", stack_addr_load_insts_users);

    for inst in stack_addr_load_insts_users.keys() {
        let mut is_recorded_stack_addr_or_stack_load = false;
        for stack_slot_users in stack_slot_usage_map.values() {
            is_recorded_stack_addr_or_stack_load |= stack_slot_users.stack_addr.contains(inst) || stack_slot_users.stack_load.contains(inst);
        }
        assert!(is_recorded_stack_addr_or_stack_load);
    }

    // Replace all unused stack_addr and stack_load instructions with nop.
    for stack_slot_users in stack_slot_usage_map.values_mut() {
        // FIXME remove clone
        for &inst in stack_slot_users.stack_addr.clone().iter() {
            if stack_addr_load_insts_users.get(&inst).map(|users| users.is_empty()).unwrap_or(true) {
                println!("Removing unused stack_addr {}", inst);
                func.dfg.detach_results(inst);
                func.dfg.replace(inst).nop();
                stack_slot_users.stack_addr.remove(&inst);
            }
        }

        for &inst in stack_slot_users.stack_load.clone().iter() {
            if stack_addr_load_insts_users.get(&inst).map(|users| users.is_empty()).unwrap_or(true) {
                println!("Removing unused stack_addr {}", inst);
                func.dfg.detach_results(inst);
                func.dfg.replace(inst).nop();
                stack_slot_users.stack_load.remove(&inst);
            }
        }
    }
}

fn try_get_stack_slot_and_offset_for_addr(func: &Function, addr: Value) -> Option<(StackSlot, Offset32)> {
    if let ValueDef::Result(addr_inst, 0) = func.dfg.value_def(addr) {
        if let InstructionData::StackLoad {
            opcode: Opcode::StackAddr,
            stack_slot,
            offset,
        } = func.dfg[addr_inst] {
            return Some((stack_slot, offset));
        }
    }
    None
}

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
enum SpatialOverlap {
    No,
    Partial,
    Full,
}

fn spatial_overlap(func: &Function, src: Inst, dest: Inst) -> SpatialOverlap {
    fn inst_info(func: &Function, inst: Inst) -> (StackSlot, Offset32, u32) {
        match func.dfg[inst] {
            InstructionData::StackLoad {
                opcode: Opcode::StackAddr,
                stack_slot,
                offset,
            }
            | InstructionData::StackLoad {
                opcode: Opcode::StackLoad,
                stack_slot,
                offset,
            }
            | InstructionData::StackStore {
                opcode: Opcode::StackStore,
                stack_slot,
                offset,
                arg: _,
            } => (stack_slot, offset, func.dfg.ctrl_typevar(inst).bytes()),
            _ => unreachable!("{:?}", func.dfg[inst]),
        }
    }

    debug_assert_ne!(src, dest);

    let (src_ss, src_offset, src_size) = inst_info(func, src);
    let (dest_ss, dest_offset, dest_size) = inst_info(func, dest);

    if src_ss != dest_ss {
        return SpatialOverlap::No;
    }

    if src_offset == dest_offset && src_size == dest_size {
        return SpatialOverlap::Full;
    }

    let src_end: i64 = src_offset.try_add_i64(i64::from(src_size)).unwrap().into();
    let dest_end: i64 = dest_offset.try_add_i64(i64::from(dest_size)).unwrap().into();
    if src_end <= dest_offset.into() || dest_end <= src_offset.into() {
        return SpatialOverlap::No;
    }

    SpatialOverlap::Partial
}

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
enum TemporalOrder {
    /// `src` will never be executed before `dest`.
    NeverBefore,

    /// `src` may be executed before `dest`.
    MaybeBefore,

    /// `src` will always be executed before `dest`.
    /// There may still be other instructions in between.
    DefinitivelyBefore,
}

fn temporal_order(ctx: &Context, src: Inst, dest: Inst) -> TemporalOrder {
    debug_assert_ne!(src, dest);

    let src_ebb = ctx.func.layout.inst_ebb(src).unwrap();
    let dest_ebb = ctx.func.layout.inst_ebb(dest).unwrap();
    if src_ebb == dest_ebb {
        use std::cmp::Ordering::*;
        match ctx.func.layout.cmp(src, dest) {
            Less => TemporalOrder::DefinitivelyBefore,
            Equal => unreachable!(),
            Greater => TemporalOrder::MaybeBefore, // FIXME use dominator to check for loops
        }
    } else {
        // FIXME O(stack_load count * ebb count)
        // FIXME reuse memory allocations
        // FIXME return DefinitivelyBefore is src dominates dest
        let mut visited = EntitySet::new();
        let mut todo = EntitySet::new();
        todo.insert(dest_ebb);
        while let Some(ebb) = todo.pop() {
            if visited.contains(ebb) {
                continue;
            }
            visited.insert(ebb);
            if ebb == src_ebb {
                return TemporalOrder::MaybeBefore;
            }
            for bb in ctx.cfg.pred_iter(ebb) {
                todo.insert(bb.ebb);
            }
        }
        TemporalOrder::NeverBefore
    }
}
<commit_msg>Fix temporal_order argument order for store to load forwarding<commit_after>use std::collections::{BTreeMap, HashSet};
use std::ops::Not;

use cranelift_codegen::cursor::{Cursor, FuncCursor};
use cranelift_codegen::entity::EntitySet;
use cranelift_codegen::ir::{InstructionData, Opcode, ProgramOrder, ValueDef};
use cranelift_codegen::ir::immediates::Offset32;

use crate::prelude::*;

/// Workaround for `StackSlot` not implementing `Ord`.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
struct OrdStackSlot(StackSlot);

impl PartialOrd for OrdStackSlot {
    fn partial_cmp(&self, rhs: &Self) -> Option<std::cmp::Ordering> {
        self.0.as_u32().partial_cmp(&rhs.0.as_u32())
    }
}

impl Ord for OrdStackSlot {
    fn cmp(&self, rhs: &Self) -> std::cmp::Ordering {
        self.0.as_u32().cmp(&rhs.0.as_u32())
    }
}

#[derive(Debug, Default)]
struct StackSlotUsage {
    stack_addr: HashSet<Inst>,
    stack_load: HashSet<Inst>,
    stack_store: HashSet<Inst>,
}

pub(super) fn optimize_function(
    ctx: &mut Context,
    clif_comments: &mut crate::pretty_clif::CommentWriter,
    name: String, // FIXME remove
) {
    ctx.flowgraph(); // Compute cfg and domtree.

    combine_stack_addr_with_load_store(&mut ctx.func);

    // Record all stack_addr, stack_load and stack_store instructions.
    let mut stack_slot_usage_map = BTreeMap::<OrdStackSlot, StackSlotUsage>::new();

    let mut cursor = FuncCursor::new(&mut ctx.func);
    while let Some(_ebb) = cursor.next_ebb() {
        while let Some(inst) = cursor.next_inst() {
            match cursor.func.dfg[inst] {
                InstructionData::StackLoad {
                    opcode: Opcode::StackAddr,
                    stack_slot,
                    offset: _,
                } => {
                    stack_slot_usage_map.entry(OrdStackSlot(stack_slot)).or_insert_with(StackSlotUsage::default).stack_addr.insert(inst);
                }
                InstructionData::StackLoad {
                    opcode: Opcode::StackLoad,
                    stack_slot,
                    offset: _,
                } => {
                    stack_slot_usage_map.entry(OrdStackSlot(stack_slot)).or_insert_with(StackSlotUsage::default).stack_load.insert(inst);
                }
                InstructionData::StackStore {
                    opcode: Opcode::StackStore,
                    arg: _,
                    stack_slot,
                    offset: _,
                } => {
                    stack_slot_usage_map.entry(OrdStackSlot(stack_slot)).or_insert_with(StackSlotUsage::default).stack_store.insert(inst);
                }
                _ => {}
            }
        }
    }

    // FIXME Repeat following instructions until fixpoint.

    println!("{}:\nstack slot usage: {:?}", name, stack_slot_usage_map);

    remove_unused_stack_addr_and_stack_load(&mut ctx.func, &mut stack_slot_usage_map);

    println!("stack slot usage (after): {:?}", stack_slot_usage_map);

    for (stack_slot, users) in stack_slot_usage_map.iter_mut() {
        if users.stack_addr.is_empty().not() {
            // Stack addr leaked; there may be unknown loads and stores.
            // FIXME use stacked borrows to optimize
            continue;
        }

        for load in users.stack_load.clone().drain() {
            let load_ebb = ctx.func.layout.inst_ebb(load).unwrap();
            let loaded_value = ctx.func.dfg.inst_results(load)[0];
            let loaded_type = ctx.func.dfg.value_type(loaded_value);

            let potential_stores = users.stack_store.iter().cloned().filter(|&store| {
                // Check if the store modified some memory accessed by the load.

                match spatial_overlap(&ctx.func, load, store) {
                    SpatialOverlap::No => false, // Can never be the source of the loaded value.
                    SpatialOverlap::Partial | SpatialOverlap::Full => true,
                }
            }).filter(|&store| {
                match temporal_order(&*ctx, load, store) {
                    TemporalOrder::NeverBefore => false, // Can never be the source of the loaded value.
                    TemporalOrder::MaybeBefore | TemporalOrder::DefinitivelyBefore => true,
                }
            }).collect::<Vec<Inst>>();
            for &store in &potential_stores {
                println!(
                    "Potential store -> load forwarding {} -> {} ({:?}, {:?})",
                    ctx.func.dfg.display_inst(store, None),
                    ctx.func.dfg.display_inst(load, None),
                    spatial_overlap(&ctx.func, store, load),
                    temporal_order(&*ctx, store, load),
                );
            }

            match *potential_stores {
                [] => println!("[{}] [BUG?] Reading uninitialized memory", name),
                [store] if spatial_overlap(&ctx.func, store, load) == SpatialOverlap::Full && temporal_order(&ctx, store, load) == TemporalOrder::DefinitivelyBefore => {
                    let store_ebb = ctx.func.layout.inst_ebb(store).unwrap();
                    let stored_value = ctx.func.dfg.inst_args(store)[0];
                    let stored_type = ctx.func.dfg.value_type(stored_value);
                    if stored_type == loaded_type && store_ebb == load_ebb {
                        println!("Store to load forward {} -> {}", store, load);
                        ctx.func.dfg.detach_results(load);
                        ctx.func.dfg.replace(load).nop();
                        ctx.func.dfg.change_to_alias(loaded_value, stored_value);
                        users.stack_load.remove(&load);
                    }
                }
                _ => {} // FIXME implement this
            }
        }

        if users.stack_load.is_empty() {
            // Never loaded; can safely remove all stores and the stack slot.
            // FIXME also remove stores when there is always a next store before a load.
            for user in users.stack_store.drain() {
                println!("[{}] Remove dead stack store {} of {}", name, user, stack_slot.0);
                ctx.func.dfg.replace(user).nop();
            }
        }

        if users.stack_store.is_empty() && users.stack_load.is_empty() {
            // FIXME make stack_slot zero sized.
        }
    }

    println!();
}

fn combine_stack_addr_with_load_store(func: &mut Function) {
    // Turn load and store into stack_load and stack_store when possible.
    let mut cursor = FuncCursor::new(func);
    while let Some(_ebb) = cursor.next_ebb() {
        while let Some(inst) = cursor.next_inst() {
            match cursor.func.dfg[inst] {
                InstructionData::Load { opcode: Opcode::Load, arg: addr, flags: _, offset } => {
                    if cursor.func.dfg.ctrl_typevar(inst) == types::I128 || cursor.func.dfg.ctrl_typevar(inst).is_vector() {
                        continue; // WORKAROUD: stack_load.i128 not yet implemented
                    }
                    if let Some((stack_slot, stack_addr_offset)) = try_get_stack_slot_and_offset_for_addr(cursor.func, addr) {
                        if let Some(combined_offset) = offset.try_add_i64(stack_addr_offset.into()) {
                            let ty = cursor.func.dfg.ctrl_typevar(inst);
                            cursor.func.dfg.replace(inst).stack_load(ty, stack_slot, combined_offset);
                        }
                    }
                }
                InstructionData::Store { opcode: Opcode::Store, args: [value, addr], flags: _, offset } => {
                    if cursor.func.dfg.ctrl_typevar(inst) == types::I128 || cursor.func.dfg.ctrl_typevar(inst).is_vector() {
                        continue; // WORKAROUND: stack_store.i128 not yet implemented
                    }
                    if let Some((stack_slot, stack_addr_offset)) = try_get_stack_slot_and_offset_for_addr(cursor.func, addr) {
                        if let Some(combined_offset) = offset.try_add_i64(stack_addr_offset.into()) {
                            cursor.func.dfg.replace(inst).stack_store(value, stack_slot, combined_offset);
                        }
                    }
                }
                _ => {}
            }
        }
    }
}

fn remove_unused_stack_addr_and_stack_load(func: &mut Function, stack_slot_usage_map: &mut BTreeMap<OrdStackSlot, StackSlotUsage>) {
    // FIXME incrementally rebuild on each call?
    let mut stack_addr_load_insts_users = HashMap::<Inst, HashSet<Inst>>::new();

    let mut cursor = FuncCursor::new(func);
    while let Some(_ebb) = cursor.next_ebb() {
        while let Some(inst) = cursor.next_inst() {
            for &arg in cursor.func.dfg.inst_args(inst) {
                if let ValueDef::Result(arg_origin, 0) = cursor.func.dfg.value_def(arg) {
                    match cursor.func.dfg[arg_origin].opcode() {
                        Opcode::StackAddr | Opcode::StackLoad => {
                            stack_addr_load_insts_users.entry(arg_origin).or_insert_with(HashSet::new).insert(inst);
                        }
                        _ => {}
                    }
                }
            }
        }
    }

    println!("stack_addr/stack_load users: {:?}", stack_addr_load_insts_users);

    for inst in stack_addr_load_insts_users.keys() {
        let mut is_recorded_stack_addr_or_stack_load = false;
        for stack_slot_users in stack_slot_usage_map.values() {
            is_recorded_stack_addr_or_stack_load |= stack_slot_users.stack_addr.contains(inst) || stack_slot_users.stack_load.contains(inst);
        }
        assert!(is_recorded_stack_addr_or_stack_load);
    }

    // Replace all unused stack_addr and stack_load instructions with nop.
    for stack_slot_users in stack_slot_usage_map.values_mut() {
        // FIXME remove clone
        for &inst in stack_slot_users.stack_addr.clone().iter() {
            if stack_addr_load_insts_users.get(&inst).map(|users| users.is_empty()).unwrap_or(true) {
                println!("Removing unused stack_addr {}", inst);
                func.dfg.detach_results(inst);
                func.dfg.replace(inst).nop();
                stack_slot_users.stack_addr.remove(&inst);
            }
        }

        for &inst in stack_slot_users.stack_load.clone().iter() {
            if stack_addr_load_insts_users.get(&inst).map(|users| users.is_empty()).unwrap_or(true) {
                println!("Removing unused stack_addr {}", inst);
                func.dfg.detach_results(inst);
                func.dfg.replace(inst).nop();
                stack_slot_users.stack_load.remove(&inst);
            }
        }
    }
}

fn try_get_stack_slot_and_offset_for_addr(func: &Function, addr: Value) -> Option<(StackSlot, Offset32)> {
    if let ValueDef::Result(addr_inst, 0) = func.dfg.value_def(addr) {
        if let InstructionData::StackLoad {
            opcode: Opcode::StackAddr,
            stack_slot,
            offset,
        } = func.dfg[addr_inst] {
            return Some((stack_slot, offset));
        }
    }
    None
}

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
enum SpatialOverlap {
    No,
    Partial,
    Full,
}

fn spatial_overlap(func: &Function, src: Inst, dest: Inst) -> SpatialOverlap {
    fn inst_info(func: &Function, inst: Inst) -> (StackSlot, Offset32, u32) {
        match func.dfg[inst] {
            InstructionData::StackLoad {
                opcode: Opcode::StackAddr,
                stack_slot,
                offset,
            }
            | InstructionData::StackLoad {
                opcode: Opcode::StackLoad,
                stack_slot,
                offset,
            }
            | InstructionData::StackStore {
                opcode: Opcode::StackStore,
                stack_slot,
                offset,
                arg: _,
            } => (stack_slot, offset, func.dfg.ctrl_typevar(inst).bytes()),
            _ => unreachable!("{:?}", func.dfg[inst]),
        }
    }

    debug_assert_ne!(src, dest);

    let (src_ss, src_offset, src_size) = inst_info(func, src);
    let (dest_ss, dest_offset, dest_size) = inst_info(func, dest);

    if src_ss != dest_ss {
        return SpatialOverlap::No;
    }

    if src_offset == dest_offset && src_size == dest_size {
        return SpatialOverlap::Full;
    }

    let src_end: i64 = src_offset.try_add_i64(i64::from(src_size)).unwrap().into();
    let dest_end: i64 = dest_offset.try_add_i64(i64::from(dest_size)).unwrap().into();
    if src_end <= dest_offset.into() || dest_end <= src_offset.into() {
        return SpatialOverlap::No;
    }

    SpatialOverlap::Partial
}

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
enum TemporalOrder {
    /// `src` will never be executed before `dest`.
    NeverBefore,

    /// `src` may be executed before `dest`.
    MaybeBefore,

    /// `src` will always be executed before `dest`.
    /// There may still be other instructions in between.
    DefinitivelyBefore,
}

fn temporal_order(ctx: &Context, src: Inst, dest: Inst) -> TemporalOrder {
    debug_assert_ne!(src, dest);

    let src_ebb = ctx.func.layout.inst_ebb(src).unwrap();
    let dest_ebb = ctx.func.layout.inst_ebb(dest).unwrap();
    if src_ebb == dest_ebb {
        use std::cmp::Ordering::*;
        match ctx.func.layout.cmp(src, dest) {
            Less => TemporalOrder::DefinitivelyBefore,
            Equal => unreachable!(),
            Greater => TemporalOrder::MaybeBefore, // FIXME use dominator to check for loops
        }
    } else {
        // FIXME O(stack_load count * ebb count)
        // FIXME reuse memory allocations
        // FIXME return DefinitivelyBefore is src dominates dest
        let mut visited = EntitySet::new();
        let mut todo = EntitySet::new();
        todo.insert(dest_ebb);
        while let Some(ebb) = todo.pop() {
            if visited.contains(ebb) {
                continue;
            }
            visited.insert(ebb);
            if ebb == src_ebb {
                return TemporalOrder::MaybeBefore;
            }
            for bb in ctx.cfg.pred_iter(ebb) {
                todo.insert(bb.ebb);
            }
        }
        TemporalOrder::NeverBefore
    }
}
<|endoftext|>


<commit_before>use chrono::prelude::{Local, DateTime};

use web::config::Config;

use web::data::{UniquenameGeneMap, GeneDetails};

use pombase_rc_string::RcString;

pub type RNAcentralGeneSynonym = RcString;

#[derive(Serialize, Debug)]
pub struct RNAcentralGene {
#[serde(rename = "geneId")]
    pub gene_id: RcString,
#[serde(skip_serializing_if="Option::is_none")]
    pub symbol: Option<RcString>,
#[serde(skip_serializing_if="Option::is_none")]
    pub name: Option<RcString>,
    pub url: RcString,
    pub synonyms: Vec<RNAcentralGeneSynonym>,
}

#[derive(Serialize, Debug)]
pub struct RNAcentralGenomeLocation {
    pub exons: Vec<RNAcentralGenomeLocationExon>,
}

#[derive(Serialize, Debug)]
pub struct RNAcentralGenomeLocationExon {
    pub chromosome: RcString,
#[serde(rename = "startPosition")]
    pub start_position: u32,
#[serde(rename = "endPosition")]
    pub end_position: u32,
    pub strand: RcString,
}

#[derive(Serialize, Debug)]
pub struct RNAcentralNcRNA {
#[serde(rename = "primaryId")]
    pub primary_id: RcString,
#[serde(rename = "taxonId")]
    pub taxon_id: RcString,
#[serde(skip_serializing_if="Option::is_none")]
    pub symbol: Option<RcString>,
#[serde(rename = "symbolSynonyms")]
    pub symbol_synonyms: Vec<RcString>,
#[serde(rename = "soTermId")]
    pub so_term_id: RcString,
    pub sequence: Option<RcString>,
    pub url: RcString,
    pub gene: RNAcentralGene,
}

#[derive(Serialize, Debug)]
pub struct RNAcentralMetadata {
#[serde(rename = "dateProduced")]
    pub date_produced: RcString,
#[serde(rename = "dataProvider")]
    pub data_provider: RcString,
#[serde(rename = "schemaVersion")]
    pub schema_version: RcString,
    pub publications: Vec<RcString>,
}

#[derive(Serialize, Debug)]
pub struct RNAcentral {
    data: Vec<RNAcentralNcRNA>,
#[serde(rename = "metaData")]
    metadata: RNAcentralMetadata,
}

fn gene_synonyms(gene_details: &GeneDetails) -> Vec<RcString> {
    gene_details.synonyms.iter().map(|syn| syn.name.clone()).collect::<Vec<_>>()
}

fn db_uniquename(config: &Config, gene_details: &GeneDetails) -> RcString {
    RcString::from(&format!("{}:{}", &config.database_name, gene_details.uniquename.clone()))
}

fn make_url(config: &Config, gene_details: &GeneDetails) -> RcString {
    RcString::from(&format!("{}/gene/{}", &config.base_url, &gene_details.uniquename))
}

fn make_gene_struct(config: &Config, gene_details: &GeneDetails) -> RNAcentralGene {
    RNAcentralGene {
        gene_id: db_uniquename(config, gene_details),
        symbol: gene_details.name.clone(),
        name: gene_details.product.clone(),
        url: make_url(config, gene_details),
        synonyms: gene_synonyms(gene_details),
    }
}

fn make_data(config: &Config, genes: &UniquenameGeneMap) -> Vec<RNAcentralNcRNA> {
    genes.values()
        .filter(|gene_details| {
            if let Some(ref rnacentral_config) = config.file_exports.rnacentral {
                rnacentral_config.export_so_ids.contains(&gene_details.transcript_so_termid)
            } else {
                panic!("no configuration for exporting RNAcentral data");
            }
        })
        .map(|gene_details: &GeneDetails| {
            let rnacentral_gene = make_gene_struct(config, &gene_details);
            let primary_id = db_uniquename(config, gene_details);
            let symbol_synonyms = gene_synonyms(gene_details);
            RNAcentralNcRNA {
                primary_id,
                taxon_id: RcString::from(&format!("NCBITaxon:{}", gene_details.taxonid)),
                symbol: gene_details.name.clone(),
                symbol_synonyms,
                so_term_id: gene_details.transcript_so_termid.clone(),
                sequence: gene_details.spliced_transcript_sequence()
                    .map(|s| RcString::from(&s.to_uppercase())),
                url: make_url(config, gene_details),
                gene: rnacentral_gene,
            }
        }).collect()
}

pub fn make_rnacentral_struct(config: &Config, genes: &UniquenameGeneMap) -> RNAcentral {
    let local: DateTime<Local> = Local::now();

    let database_name = config.database_name.clone();
    let data = make_data(&config, genes);

    RNAcentral {
        data,
        metadata: RNAcentralMetadata {
            date_produced: RcString::from(&local.to_rfc3339()),
            data_provider: database_name.clone(),
            schema_version: RcString::from("0.3.0"),
            publications: vec![config.database_citation.clone()],
        }
    }
}
<commit_msg>Add publications to the RNAcentral output<commit_after>use std::collections::HashSet;

use chrono::prelude::{Local, DateTime};

use web::config::Config;

use web::data::{UniquenameGeneMap, GeneDetails};

use pombase_rc_string::RcString;

pub type RNAcentralGeneSynonym = RcString;

#[derive(Serialize, Debug)]
pub struct RNAcentralGene {
#[serde(rename = "geneId")]
    pub gene_id: RcString,
#[serde(skip_serializing_if="Option::is_none")]
    pub symbol: Option<RcString>,
#[serde(skip_serializing_if="Option::is_none")]
    pub name: Option<RcString>,
    pub url: RcString,
    pub synonyms: Vec<RNAcentralGeneSynonym>,
}

#[derive(Serialize, Debug)]
pub struct RNAcentralGenomeLocation {
    pub exons: Vec<RNAcentralGenomeLocationExon>,
}

#[derive(Serialize, Debug)]
pub struct RNAcentralGenomeLocationExon {
    pub chromosome: RcString,
#[serde(rename = "startPosition")]
    pub start_position: u32,
#[serde(rename = "endPosition")]
    pub end_position: u32,
    pub strand: RcString,
}

#[derive(Serialize, Debug)]
pub struct RNAcentralNcRNA {
#[serde(rename = "primaryId")]
    pub primary_id: RcString,
#[serde(rename = "taxonId")]
    pub taxon_id: RcString,
#[serde(skip_serializing_if="Option::is_none")]
    pub symbol: Option<RcString>,
#[serde(rename = "symbolSynonyms")]
    pub symbol_synonyms: Vec<RcString>,
#[serde(rename = "soTermId")]
    pub so_term_id: RcString,
    pub sequence: Option<RcString>,
    pub url: RcString,
    pub gene: RNAcentralGene,
#[serde(skip_serializing_if="HashSet::is_empty", default)]
    pub publications: HashSet<RcString>,
}

#[derive(Serialize, Debug)]
pub struct RNAcentralMetadata {
#[serde(rename = "dateProduced")]
    pub date_produced: RcString,
#[serde(rename = "dataProvider")]
    pub data_provider: RcString,
#[serde(rename = "schemaVersion")]
    pub schema_version: RcString,
    pub publications: Vec<RcString>,
}

#[derive(Serialize, Debug)]
pub struct RNAcentral {
    data: Vec<RNAcentralNcRNA>,
#[serde(rename = "metaData")]
    metadata: RNAcentralMetadata,
}

fn gene_synonyms(gene_details: &GeneDetails) -> Vec<RcString> {
    gene_details.synonyms.iter().map(|syn| syn.name.clone()).collect::<Vec<_>>()
}

fn db_uniquename(config: &Config, gene_details: &GeneDetails) -> RcString {
    RcString::from(&format!("{}:{}", &config.database_name, gene_details.uniquename.clone()))
}

fn make_url(config: &Config, gene_details: &GeneDetails) -> RcString {
    RcString::from(&format!("{}/gene/{}", &config.base_url, &gene_details.uniquename))
}

fn make_gene_struct(config: &Config, gene_details: &GeneDetails) -> RNAcentralGene {
    RNAcentralGene {
        gene_id: db_uniquename(config, gene_details),
        symbol: gene_details.name.clone(),
        name: gene_details.product.clone(),
        url: make_url(config, gene_details),
        synonyms: gene_synonyms(gene_details),
    }
}

fn make_data(config: &Config, genes: &UniquenameGeneMap) -> Vec<RNAcentralNcRNA> {
    genes.values()
        .filter(|gene_details| {
            if let Some(ref rnacentral_config) = config.file_exports.rnacentral {
                rnacentral_config.export_so_ids.contains(&gene_details.transcript_so_termid)
            } else {
                panic!("no configuration for exporting RNAcentral data");
            }
        })
        .map(|gene_details: &GeneDetails| {
            let rnacentral_gene = make_gene_struct(config, &gene_details);
            let primary_id = db_uniquename(config, gene_details);
            let symbol_synonyms = gene_synonyms(gene_details);
            RNAcentralNcRNA {
                primary_id,
                taxon_id: RcString::from(&format!("NCBITaxon:{}", gene_details.taxonid)),
                symbol: gene_details.name.clone(),
                symbol_synonyms,
                so_term_id: gene_details.transcript_so_termid.clone(),
                sequence: gene_details.spliced_transcript_sequence()
                    .map(|s| RcString::from(&s.to_uppercase())),
                url: make_url(config, gene_details),
                gene: rnacentral_gene,
                publications: gene_details.feature_publications.clone(),
            }
        }).collect()
}

pub fn make_rnacentral_struct(config: &Config, genes: &UniquenameGeneMap) -> RNAcentral {
    let local: DateTime<Local> = Local::now();

    let database_name = config.database_name.clone();
    let data = make_data(&config, genes);

    RNAcentral {
        data,
        metadata: RNAcentralMetadata {
            date_produced: RcString::from(&local.to_rfc3339()),
            data_provider: database_name.clone(),
            schema_version: RcString::from("0.3.0"),
            publications: vec![config.database_citation.clone()],
        }
    }
}
<|endoftext|>


<commit_before>// This module implements configuration related stuff.
// Copyright (c) 2014 by Shipeng Feng.
// Licensed under the BSD License, see LICENSE for more details.

use serialize::json::JsonObject;

pub trait ConfigTrait {
    fn from_jsonfile(&mut self, filepath: Path) -> Path {
        filepath
    }
}

/// Currently we are using `JsonObject` as config type.  Works like
/// a JSON Object but provides ways to fill it from JSON files:
///
/// ```rust,no_run
/// app.config.from_jsonfile("yourconfig.json")
/// ```
///
/// You can also load configurations from an environment variable
/// pointing to a file:
///
/// ```rust,no_run
/// app.config.from_envvar("YOURAPPLICATION_SETTINGS")
/// ```
///
/// In this case, you have to set this environment variable to the file
/// you want to use.  On Linux and OS X you can use the export statement:
///
/// ```bash
/// export YOURAPPLICATION_SETTINGS="/path/to/config/file"
/// ```
///
pub type Config = JsonObject;

impl ConfigTrait for Config {
}
<commit_msg>Ignore doc test<commit_after>// This module implements configuration related stuff.
// Copyright (c) 2014 by Shipeng Feng.
// Licensed under the BSD License, see LICENSE for more details.

use serialize::json::JsonObject;

pub trait ConfigTrait {
    fn from_jsonfile(&mut self, filepath: Path) -> Path {
        filepath
    }
}

/// Currently we are using `JsonObject` as config type.  Works like
/// a JSON Object but provides ways to fill it from JSON files:
///
/// ```ignore
/// app.config.from_jsonfile("yourconfig.json")
/// ```
///
/// You can also load configurations from an environment variable
/// pointing to a file:
///
/// ```ignore
/// app.config.from_envvar("YOURAPPLICATION_SETTINGS")
/// ```
///
/// In this case, you have to set this environment variable to the file
/// you want to use.  On Linux and OS X you can use the export statement:
///
/// ```bash
/// export YOURAPPLICATION_SETTINGS="/path/to/config/file"
/// ```
///
pub type Config = JsonObject;

impl ConfigTrait for Config {
}
<|endoftext|>


<commit_before>use std::fmt;
use std::sync::{Arc, Mutex};

#[derive(Clone)]
pub(crate) struct ThreadsafeWrapper<T>
where
    T: fmt::Debug + Default + Clone,
{
    data: Arc<Mutex<T>>,
}

impl<T> Default for ThreadsafeWrapper<T>
where
    T: fmt::Debug + Default + Clone,
{
    fn default() -> Self {
        ThreadsafeWrapper {
            data: Arc::new(Mutex::new(T::default())),
        }
    }
}

impl<T> fmt::Debug for ThreadsafeWrapper<T>
where
    T: fmt::Debug + Default + Clone,
{
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        let data = self.data.lock();
        data.fmt(f)
    }
}

impl<T> ThreadsafeWrapper<T>
where
    T: fmt::Debug + Default + Clone,
{
    pub(crate) fn new(elt: T) -> ThreadsafeWrapper<T> {
        ThreadsafeWrapper {
            data: Arc::new(Mutex::new(elt)),
        }
    }

    #[inline]
    /// Wraps retrieving a bucket's data, calls a function to make a
    /// decision and return a new state, and then tries to set the
    /// state on the bucket.
    ///
    /// This function can loop and call the decision closure again if
    /// the bucket state couldn't be set.
    ///
    /// # Panics
    /// Panics if an error occurs in acquiring any locks.
    pub(crate) fn measure_and_replace<F, E>(&mut self, f: F) -> Result<(), E>
    where
        F: Fn(&T) -> (Result<(), E>, Option<T>),
    {
        let mut data = self.data.lock().unwrap();
        let (decision, new_data) = f(&*data);
        if let Some(new_data) = new_data {
            *data = new_data;
        }
        decision
    }

    /// Retrieves and returns a snapshot of the bucket state. This
    /// isn't thread safe, but can be used to restore an old copy of
    /// the bucket if necessary.
    ///
    /// # Thread safety
    /// This function operates threadsafely, but you're literally
    /// taking a copy of data that will change. Relying on the data
    /// that is returned is *not* threadsafe.
    ///
    /// # Panics
    /// Panics if an error occurs in acquiring any locks.
    pub(crate) fn snapshot(&self) -> T {
        let data = self.data.lock().unwrap();
        data.clone()
    }
}
<commit_msg>Add some more crate-level docs<commit_after>use std::fmt;
use std::sync::{Arc, Mutex};

#[derive(Clone)]
/// Wraps the atomic operations on a Decider's state in a threadsafe
/// fashion.
pub(crate) struct ThreadsafeWrapper<T>
where
    T: fmt::Debug + Default + Clone,
{
    data: Arc<Mutex<T>>,
}

impl<T> Default for ThreadsafeWrapper<T>
where
    T: fmt::Debug + Default + Clone,
{
    fn default() -> Self {
        ThreadsafeWrapper {
            data: Arc::new(Mutex::new(T::default())),
        }
    }
}

impl<T> fmt::Debug for ThreadsafeWrapper<T>
where
    T: fmt::Debug + Default + Clone,
{
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        let data = self.data.lock();
        data.fmt(f)
    }
}

impl<T> ThreadsafeWrapper<T>
where
    T: fmt::Debug + Default + Clone,
{
    pub(crate) fn new(elt: T) -> ThreadsafeWrapper<T> {
        ThreadsafeWrapper {
            data: Arc::new(Mutex::new(elt)),
        }
    }

    #[inline]
    /// Wraps retrieving a bucket's data, calls a function to make a
    /// decision and return a new state, and then tries to set the
    /// state on the bucket.
    ///
    /// This function can loop and call the decision closure again if
    /// the bucket state couldn't be set.
    ///
    /// # Panics
    /// Panics if an error occurs in acquiring any locks.
    pub(crate) fn measure_and_replace<F, E>(&mut self, f: F) -> Result<(), E>
    where
        F: Fn(&T) -> (Result<(), E>, Option<T>),
    {
        let mut data = self.data.lock().unwrap();
        let (decision, new_data) = f(&*data);
        if let Some(new_data) = new_data {
            *data = new_data;
        }
        decision
    }

    /// Retrieves and returns a snapshot of the bucket state. This
    /// isn't thread safe, but can be used to restore an old copy of
    /// the bucket if necessary.
    ///
    /// # Thread safety
    /// This function operates threadsafely, but you're literally
    /// taking a copy of data that will change. Relying on the data
    /// that is returned is *not* threadsafe.
    ///
    /// # Panics
    /// Panics if an error occurs in acquiring any locks.
    pub(crate) fn snapshot(&self) -> T {
        let data = self.data.lock().unwrap();
        data.clone()
    }
}
<|endoftext|>


<commit_before>// Copyright 2015 Nathan Sizemore <nathanrsizemore@gmail.com>
//
// This Source Code Form is subject to the
// terms of the Mozilla Public License, v.
// 2.0. If a copy of the MPL was not
// distributed with this file, You can
// obtain one at
// http://mozilla.org/MPL/2.0/.
//
// This Source Code Form is "Incompatible
// With Secondary Licenses", as defined by
// the Mozilla Public License, v. 2.0.

use std::thread;
use std::thread::JoinHandle;
use std::net::TcpStream;
use std::ops::DerefMut;
use std::os::unix::io::RawFd;
use std::collections::LinkedList;
use std::sync::{Arc, Mutex};
use std::sync::mpsc::{
    channel,
    Sender,
    Receiver,
    TryRecvError
};

use super::types::*;
use super::epoll;
use super::epoll::util::*;
use super::epoll::EpollEvent;
use super::socket::Socket;
use super::simple_stream::nbetstream::NbetStream;

#[allow(dead_code)]
pub struct EventLoop {
    /// Handle to event loop process
    prox: JoinHandle<()>,
    /// Sender<T> to proc
    prox_tx: Sender<TcpStream>
}

impl EventLoop {

    /// Returns a new EventLoop
    pub fn new(server_tx: Sender<EventTuple>) -> EventLoop {
        let (tx, rx): (Sender<TcpStream>, Receiver<TcpStream>) = channel();
        let prox = thread::Builder::new()
            .name("EventLoop".to_string())
            .spawn(move || {
                EventLoop::start(rx, server_tx);
            }).unwrap();

        EventLoop {
            prox: prox,
            prox_tx: tx
        }
    }

    /// Returns a clone of this EventLoop's Sender<TcpStream> channel
    pub fn sender(&self) -> Sender<TcpStream> {
        self.prox_tx.clone()
    }

    /// Main event loop
    fn start(rx: Receiver<TcpStream>, uspace_tx: Sender<EventTuple>) {
        // Master socket list
        let sockets = Arc::new(Mutex::new(LinkedList::<Socket>::new()));

        // Our error handler for this thread and it's children
        let (err_tx, err_rx): (Sender<()>, Receiver<()>) = channel();

        // Create an epoll instance
        let result = epoll::create1(0);
        if result.is_err() {
            let err = result.unwrap_err();
            panic!("Unable to create epoll instance: {}", err)
        }
        let epoll_instance = result.unwrap();

        // Start the epoll thread
        let c_sockets = sockets.clone();
        let c_epoll_instance = epoll_instance.clone();
        let epoll_prox = thread::Builder::new()
            .name("EpollLoop".to_string())
            .spawn(move || {
                EventLoop::epoll_loop(uspace_tx, c_sockets, c_epoll_instance, err_rx);
            }).unwrap();

        for new_stream in rx.iter() {
            match NbetStream::new(new_stream) {
                Ok(s_stream) => {
                    // Add to master list
                    let socket = Socket::new(s_stream);

                    // TODO - Replace with recoverable version once into_inner() is stable
                    // Unfortunately, this is the only stable way to use mutexes at the moment
                    // Hopefully recovering from poisoning will be in 1.2
                    // let mut s_guard = match sockets.lock() {
                    //     Ok(guard) => guard,
                    //     Err(poisoned) => poisoned.into_inner()
                    // };
                    let mut s_guard = sockets.lock().unwrap();
                    let s_list = s_guard.deref_mut();
                    let s_fd = socket.raw_fd();
                    s_list.push_back(socket);

                    // Add to epoll
                    let event = Box::new(EpollEvent {
                        data: s_fd as u64,
                        events: (event_type::EPOLLIN | event_type::EPOLLET | event_type::EPOLLRDHUP)
                    });
                    match epoll::ctl(epoll_instance, ctl_op::ADD, s_fd, event) {
                        Ok(()) => println!("New socket added to epoll list"),
                        Err(e) => println!("Epoll CtrlError during add: {}", e)
                    };
                }
                Err(e) => {
                    // TODO - write error to log file
                    println!("Error: {}", e);
                }
            };
        }

        // If we get here, shit is real bad. It means we have lost our
        // channel to the outside world.
        // Kill off event loop, so unwinding can begin
        println!("Rust.EventLoop.start - thread finished");
        let _ = err_tx.send(());
    }

    /// Calls epoll_wait forever waiting for events to be fired up from kernel
    fn epoll_loop(uspace_tx: Sender<EventTuple>,
                  sockets: SocketList,
                  epoll_instance: RawFd,
                  err_rx: Receiver<()>) {

        // This is the maximum number of events we want to be notified of at one time
        let mut events = Vec::<EpollEvent>::with_capacity(100);
        unsafe { events.set_len(100); }

        loop {
            // Wait for epoll events
            println!("epoll_wait...");
            match epoll::wait(epoll_instance, &mut events[..], -1) {
                Ok(num_events) => {
                    println!("{} epoll event(s) received", num_events);
                    for x in 0..num_events {
                        let tx_clone = uspace_tx.clone();
                        let s_clone = sockets.clone();
                        let epfd_clone = epoll_instance.clone();

                        EventLoop::handle_epoll_event(epfd_clone,
                            &events[x as usize], s_clone, tx_clone);
                    }
                }
                Err(e) => {
                    panic!("Error on epoll::wait(): {}", e)
                }
            }

            // If we receive anything on this end or lose communication, we need to halt,
            // because we have no where to send up events, our "user space" channel is
            // gone
            match err_rx.try_recv() {
                Ok(_) => {
                    println!("Error, terminating epoll_loop");
                    break;
                }
                Err(e) => {
                    match e {
                        TryRecvError::Empty => {}
                        _ => {
                            println!("Error, terminating epoll_loo");
                            break;
                        }
                    }
                }
            };
        }
        println!("epoll_loop - thread finished");
    }

    /// Processes a read on the socket that is ready for reading
    ///
    /// An epoll event is passed to this function, with the fd that is ready to be read
    /// A traversal through the list of sockets is then performed in order to locate the
    /// NbetStream instance that is wrapping that file descriptor.
    /// After a read is performed on that socket, it's data will be passed back to the
    /// "user-space" section of the Server to process the payload however it needs to.
    ///
    /// TODO - Create a faster way to gain a safe reference to the NbetStream wrapping
    /// the fd instead of an O(n) lookup for every event. Currently lookup time is
    /// O(n) * MAX_EPOLL_EVENTS, where MAX_EPOLL_EVENTS is user defined in this function's
    /// caller.
    fn handle_epoll_event(epoll_instance: RawFd,
                          event: &EpollEvent,
                          sockets: SocketList,
                          uspace_tx: Sender<EventTuple>) {

        println!("handle_epoll_event");

        // List of socket id's that produced an Err on read attempt
        // Cannot do it within the match block because the list will need
        // access to the mutex, while this scope has the mutex, which will result
        // in a spinlock that will spin forever. And, that's a trip to bummer town
        let mut errd_socket_ids = Vec::<u32>::new();

        // The fd we are traversing the list for
        let fd = event.data;

        // We need a handle before the list gets locked and used
        let s_list_clone = sockets.clone();

        // TODO - Replace with recoverable version once into_inner() is stable
        // Unfortunately, this is the only stable way to use mutexes at the moment
        // Hopefully recovering from poisoning will be in 1.2
        // let mut s_guard = match sockets.lock() {
        //     Ok(guard) => guard,
        //     Err(poisoned) => poisoned.into_inner()
        // };
        let mut s_guard = sockets.lock().unwrap();
        let s_list = s_guard.deref_mut();
        for socket in s_list.iter_mut() {
            if socket.raw_fd() as u64 == fd {
                // We've found the socket for the event passed from epoll
                // Lets attempt to read from it
                match socket.read() {
                    Ok(()) => {
                        println!("Rust.EventLoop.handle_epoll_event.socket.read.Ok()");
                        for msg in socket.buffer().iter() {
                            let msg_for_debug = msg.clone();
                            match String::from_utf8(msg_for_debug) {
                                Ok(msg_str) => {
                                    println!("Rust.EventLoop.handle_epoll_event.socket.read.ok.msg_as_string: {}", msg_str);
                                }
                                Err(e) => {
                                    println!("Rust.EventLoop.handle_epoll_event.socket.read.ok.msg_as_string.Err: {}", e);
                                }
                            };

                            let list_handle = s_list_clone.clone();
                            let socket_clone = socket.clone();
                            let msg_clone = msg.clone();
                            let _ = uspace_tx.send((list_handle, socket_clone, msg_clone));
                        }
                    }
                    Err(e) => {
                        println!("Rust.EventLoop.handle_epoll_event.socket.read.Err(e): {}", e);
                        // We really don't care what happened, it all results in the same
                        // outcome - remove the socket from system...
                        errd_socket_ids.push(socket.id());
                        let s_clone = socket.clone();
                        let epfd_clone = epoll_instance.clone();
                        EventLoop::remove_socket_from_epoll(s_clone, epfd_clone);
                    }
                }
            }
        }

        // Remove any errd sockets from the master list of sockets
        EventLoop::remove_socket_from_list(errd_socket_ids, &mut s_list);
    }

    /// Removes socket from the epoll watch list
    fn remove_socket_from_epoll(socket: Socket, epoll_instance: RawFd) {
        println!("remove_socket_from_epoll");

        // In kernel versions before 2.6.9, the EPOLL_CTL_DEL operation required
        // a non-null pointer in event, even though this argument is ignored.
        // Since Linux 2.6.9, event can be specified as NULL when using
        // EPOLL_CTL_DEL.  Applications that need to be portable to kernels
        // before 2.6.9 should specify a non-null pointer in event.
        let event = Box::new(EpollEvent {
            data: 0 as u64,
            events: 0 as u32
        });
        let s_fd = socket.raw_fd();

        // Depending on how fd are duplicated with .clone(), this may fail
        // If the failure case is CtlError::ENOENT, we do not care, because
        // epoll will clean the up the descriptor after they are all dropped from
        // program memory
        match epoll::ctl(epoll_instance, ctl_op::DEL, s_fd, event) {
            Ok(()) => println!("Socket removed from epoll watch list"),
            Err(e) => {
                match e {
                    CtlError::ENOENT => {
                        println!("Fd not found in epoll, will remove when fd is syscall closed");
                    }
                    _ => println!("Epoll CtrlError during del: {}", e)
                }
            }
        };
    }

    /// Removes socket from master list
    fn remove_socket_from_list(socket_ids: Vec<u32>, sockets: &mut LinkedList<Socket>) {
        println!("remove_socket_from_list");

        // TODO - Replace with recoverable version once into_inner() is stable
        // Unfortunately, this is the only stable way to use mutexes at the moment
        // Hopefully recovering from poisoning will be in 1.2
        // let mut s_guard = match sockets.lock() {
        //     Ok(guard) => guard,
        //     Err(poisoned) => poisoned.into_inner()
        // };
        // let mut s_guard = sockets.lock().unwrap();
        // let s_list = s_guard.deref_mut();

        for socket_id in socket_ids.iter() {
            let mut socket_found = false;
            let mut index: usize = 1;
            for socket in sockets.iter() {
                if socket.id() == *socket_id {
                    socket_found = true;
                    break;
                }
                index += 1;
            }

            if !socket_found {
                println!("Socket not found in list...?");
                return;
            }

            if index == 1 {
                s_list.pop_front();
            } else {
                let mut split = s_list.split_off(index - 1);
                split.pop_front();
                s_list.append(&mut split);
            }

            println!("Socket removed sucessfully");
        }
    }
}
<commit_msg>Debugging<commit_after>// Copyright 2015 Nathan Sizemore <nathanrsizemore@gmail.com>
//
// This Source Code Form is subject to the
// terms of the Mozilla Public License, v.
// 2.0. If a copy of the MPL was not
// distributed with this file, You can
// obtain one at
// http://mozilla.org/MPL/2.0/.
//
// This Source Code Form is "Incompatible
// With Secondary Licenses", as defined by
// the Mozilla Public License, v. 2.0.

use std::thread;
use std::thread::JoinHandle;
use std::net::TcpStream;
use std::ops::DerefMut;
use std::os::unix::io::RawFd;
use std::collections::LinkedList;
use std::sync::{Arc, Mutex};
use std::sync::mpsc::{
    channel,
    Sender,
    Receiver,
    TryRecvError
};

use super::types::*;
use super::epoll;
use super::epoll::util::*;
use super::epoll::EpollEvent;
use super::socket::Socket;
use super::simple_stream::nbetstream::NbetStream;

#[allow(dead_code)]
pub struct EventLoop {
    /// Handle to event loop process
    prox: JoinHandle<()>,
    /// Sender<T> to proc
    prox_tx: Sender<TcpStream>
}

impl EventLoop {

    /// Returns a new EventLoop
    pub fn new(server_tx: Sender<EventTuple>) -> EventLoop {
        let (tx, rx): (Sender<TcpStream>, Receiver<TcpStream>) = channel();
        let prox = thread::Builder::new()
            .name("EventLoop".to_string())
            .spawn(move || {
                EventLoop::start(rx, server_tx);
            }).unwrap();

        EventLoop {
            prox: prox,
            prox_tx: tx
        }
    }

    /// Returns a clone of this EventLoop's Sender<TcpStream> channel
    pub fn sender(&self) -> Sender<TcpStream> {
        self.prox_tx.clone()
    }

    /// Main event loop
    fn start(rx: Receiver<TcpStream>, uspace_tx: Sender<EventTuple>) {
        // Master socket list
        let sockets = Arc::new(Mutex::new(LinkedList::<Socket>::new()));

        // Our error handler for this thread and it's children
        let (err_tx, err_rx): (Sender<()>, Receiver<()>) = channel();

        // Create an epoll instance
        let result = epoll::create1(0);
        if result.is_err() {
            let err = result.unwrap_err();
            panic!("Unable to create epoll instance: {}", err)
        }
        let epoll_instance = result.unwrap();

        // Start the epoll thread
        let c_sockets = sockets.clone();
        let c_epoll_instance = epoll_instance.clone();
        let epoll_prox = thread::Builder::new()
            .name("EpollLoop".to_string())
            .spawn(move || {
                EventLoop::epoll_loop(uspace_tx, c_sockets, c_epoll_instance, err_rx);
            }).unwrap();

        for new_stream in rx.iter() {
            match NbetStream::new(new_stream) {
                Ok(s_stream) => {
                    // Add to master list
                    let socket = Socket::new(s_stream);

                    // TODO - Replace with recoverable version once into_inner() is stable
                    // Unfortunately, this is the only stable way to use mutexes at the moment
                    // Hopefully recovering from poisoning will be in 1.2
                    // let mut s_guard = match sockets.lock() {
                    //     Ok(guard) => guard,
                    //     Err(poisoned) => poisoned.into_inner()
                    // };
                    let mut s_guard = sockets.lock().unwrap();
                    let s_list = s_guard.deref_mut();
                    let s_fd = socket.raw_fd();
                    s_list.push_back(socket);

                    // Add to epoll
                    let event = Box::new(EpollEvent {
                        data: s_fd as u64,
                        events: (event_type::EPOLLIN | event_type::EPOLLET | event_type::EPOLLRDHUP)
                    });
                    match epoll::ctl(epoll_instance, ctl_op::ADD, s_fd, event) {
                        Ok(()) => println!("New socket added to epoll list"),
                        Err(e) => println!("Epoll CtrlError during add: {}", e)
                    };
                }
                Err(e) => {
                    // TODO - write error to log file
                    println!("Error: {}", e);
                }
            };
        }

        // If we get here, shit is real bad. It means we have lost our
        // channel to the outside world.
        // Kill off event loop, so unwinding can begin
        println!("Rust.EventLoop.start - thread finished");
        let _ = err_tx.send(());
    }

    /// Calls epoll_wait forever waiting for events to be fired up from kernel
    fn epoll_loop(uspace_tx: Sender<EventTuple>,
                  sockets: SocketList,
                  epoll_instance: RawFd,
                  err_rx: Receiver<()>) {

        // This is the maximum number of events we want to be notified of at one time
        let mut events = Vec::<EpollEvent>::with_capacity(100);
        unsafe { events.set_len(100); }

        loop {
            // Wait for epoll events
            println!("epoll_wait...");
            match epoll::wait(epoll_instance, &mut events[..], -1) {
                Ok(num_events) => {
                    println!("{} epoll event(s) received", num_events);
                    for x in 0..num_events {
                        let tx_clone = uspace_tx.clone();
                        let s_clone = sockets.clone();
                        let epfd_clone = epoll_instance.clone();

                        EventLoop::handle_epoll_event(epfd_clone,
                            &events[x as usize], s_clone, tx_clone);
                    }
                }
                Err(e) => {
                    panic!("Error on epoll::wait(): {}", e)
                }
            }

            // If we receive anything on this end or lose communication, we need to halt,
            // because we have no where to send up events, our "user space" channel is
            // gone
            match err_rx.try_recv() {
                Ok(_) => {
                    println!("Error, terminating epoll_loop");
                    break;
                }
                Err(e) => {
                    match e {
                        TryRecvError::Empty => {}
                        _ => {
                            println!("Error, terminating epoll_loo");
                            break;
                        }
                    }
                }
            };
        }
        println!("epoll_loop - thread finished");
    }

    /// Processes a read on the socket that is ready for reading
    ///
    /// An epoll event is passed to this function, with the fd that is ready to be read
    /// A traversal through the list of sockets is then performed in order to locate the
    /// NbetStream instance that is wrapping that file descriptor.
    /// After a read is performed on that socket, it's data will be passed back to the
    /// "user-space" section of the Server to process the payload however it needs to.
    ///
    /// TODO - Create a faster way to gain a safe reference to the NbetStream wrapping
    /// the fd instead of an O(n) lookup for every event. Currently lookup time is
    /// O(n) * MAX_EPOLL_EVENTS, where MAX_EPOLL_EVENTS is user defined in this function's
    /// caller.
    fn handle_epoll_event(epoll_instance: RawFd,
                          event: &EpollEvent,
                          sockets: SocketList,
                          uspace_tx: Sender<EventTuple>) {

        println!("handle_epoll_event");

        // List of socket id's that produced an Err on read attempt
        // Cannot do it within the match block because the list will need
        // access to the mutex, while this scope has the mutex, which will result
        // in a spinlock that will spin forever. And, that's a trip to bummer town
        let mut errd_socket_ids = Vec::<u32>::new();

        // The fd we are traversing the list for
        let fd = event.data;

        // We need a handle before the list gets locked and used
        let s_list_clone = sockets.clone();

        // TODO - Replace with recoverable version once into_inner() is stable
        // Unfortunately, this is the only stable way to use mutexes at the moment
        // Hopefully recovering from poisoning will be in 1.2
        // let mut s_guard = match sockets.lock() {
        //     Ok(guard) => guard,
        //     Err(poisoned) => poisoned.into_inner()
        // };
        let mut s_guard = sockets.lock().unwrap();
        let s_list = s_guard.deref_mut();
        for socket in s_list.iter_mut() {
            if socket.raw_fd() as u64 == fd {
                // We've found the socket for the event passed from epoll
                // Lets attempt to read from it
                match socket.read() {
                    Ok(()) => {
                        println!("Rust.EventLoop.handle_epoll_event.socket.read.Ok()");
                        for msg in socket.buffer().iter() {
                            let msg_for_debug = msg.clone();
                            match String::from_utf8(msg_for_debug) {
                                Ok(msg_str) => {
                                    println!("Rust.EventLoop.handle_epoll_event.socket.read.ok.msg_as_string: {}", msg_str);
                                }
                                Err(e) => {
                                    println!("Rust.EventLoop.handle_epoll_event.socket.read.ok.msg_as_string.Err: {}", e);
                                }
                            };

                            let list_handle = s_list_clone.clone();
                            let socket_clone = socket.clone();
                            let msg_clone = msg.clone();
                            let _ = uspace_tx.send((list_handle, socket_clone, msg_clone));
                        }
                    }
                    Err(e) => {
                        println!("Rust.EventLoop.handle_epoll_event.socket.read.Err(e): {}", e);
                        // We really don't care what happened, it all results in the same
                        // outcome - remove the socket from system...
                        errd_socket_ids.push(socket.id());
                        let s_clone = socket.clone();
                        let epfd_clone = epoll_instance.clone();
                        EventLoop::remove_socket_from_epoll(s_clone, epfd_clone);
                    }
                }
            }
        }

        // Remove any errd sockets from the master list of sockets
        EventLoop::remove_socket_from_list(errd_socket_ids, &mut s_list);
    }

    /// Removes socket from the epoll watch list
    fn remove_socket_from_epoll(socket: Socket, epoll_instance: RawFd) {
        println!("remove_socket_from_epoll");

        // In kernel versions before 2.6.9, the EPOLL_CTL_DEL operation required
        // a non-null pointer in event, even though this argument is ignored.
        // Since Linux 2.6.9, event can be specified as NULL when using
        // EPOLL_CTL_DEL.  Applications that need to be portable to kernels
        // before 2.6.9 should specify a non-null pointer in event.
        let event = Box::new(EpollEvent {
            data: 0 as u64,
            events: 0 as u32
        });
        let s_fd = socket.raw_fd();

        // Depending on how fd are duplicated with .clone(), this may fail
        // If the failure case is CtlError::ENOENT, we do not care, because
        // epoll will clean the up the descriptor after they are all dropped from
        // program memory
        match epoll::ctl(epoll_instance, ctl_op::DEL, s_fd, event) {
            Ok(()) => println!("Socket removed from epoll watch list"),
            Err(e) => {
                match e {
                    CtlError::ENOENT => {
                        println!("Fd not found in epoll, will remove when fd is syscall closed");
                    }
                    _ => println!("Epoll CtrlError during del: {}", e)
                }
            }
        };
    }

    /// Removes socket from master list
    fn remove_socket_from_list(socket_ids: Vec<u32>, sockets: &mut LinkedList<Socket>) {
        println!("remove_socket_from_list");

        // TODO - Replace with recoverable version once into_inner() is stable
        // Unfortunately, this is the only stable way to use mutexes at the moment
        // Hopefully recovering from poisoning will be in 1.2
        // let mut s_guard = match sockets.lock() {
        //     Ok(guard) => guard,
        //     Err(poisoned) => poisoned.into_inner()
        // };
        // let mut s_guard = sockets.lock().unwrap();
        // let s_list = s_guard.deref_mut();

        for socket_id in socket_ids.iter() {
            let mut socket_found = false;
            let mut index: usize = 1;
            for socket in sockets.iter() {
                if socket.id() == *socket_id {
                    socket_found = true;
                    break;
                }
                index += 1;
            }

            if !socket_found {
                println!("Socket not found in list...?");
                return;
            }

            if index == 1 {
                sockets.pop_front();
            } else {
                let mut split = sockets.split_off(index - 1);
                split.pop_front();
                sockets.append(&mut split);
            }

            println!("Socket removed sucessfully");
        }
    }
}
<|endoftext|>


<commit_before>use memory::Memory;
use std::vec::Vec;
use std::sync::atomic::{AtomicIsize, Ordering};

/// Maps multiple `Memory`s into a single address space.
///
/// The `MappedMemory` allows to unify multiple `Memory`s in one address space.
/// A `Memory` is mounted at a certain address and is from now on responsible
/// for all reads and writes starting at that address (the “mount point”) and
/// ending where the mounted memory ends. The read and write calls for the
/// responsible `Memory` will be given an address _relative_ to e mount point.
///
/// ```text
///                   Unmapped
///                <-------->
///       mem_a                   mem_b
/// |--------------|            |------|
///                            NullMemory
///                         |--------------
/// |------------- mapped_mem ----------->
/// |              |            |      |
/// 0            0x100        0x200  0x280
/// ```
///
/// For example: `mapped_mem.get(0x208)` would yield the same value as
/// `mem_b.get(0x008)`.
///
/// # Examples
///
/// ```
/// use osciemu::memory::{Memory, SliceMemory, MappedMemory};
///
/// let mut mm = MappedMemory::new();
/// let m1 = mm.mount(0, Box::new(SliceMemory::from_slice(Box::new([1]))));
/// let m2 = mm.mount(2, Box::new(SliceMemory::from_slice(Box::new([2, 2]))));
/// // Now mm =~ [1, _, 2, 2]
/// assert_eq!(mm.get(0), 1);
/// assert_eq!(mm.get(3), 2);
/// ```
///
/// A call to `mount` returns a `MemoryToken`. These tokens can be used to
/// borrow a `Memory` even if it has been mounted. This works similar to a
/// `std::cell::RefCell`.
///
/// ```
/// use osciemu::memory::{Memory, NullMemory, SliceMemory, MappedMemory};
/// use osciemu::memory::mappedmemory::MemoryToken;
///
/// let mut mm = MappedMemory::new();
/// let m1 = mm.mount(0, Box::new(NullMemory::new()));
/// let m2 = mm.mount(0, Box::new(SliceMemory::from_slice(Box::new([1, 2, 3, 4]))));
/// mm.set(0, 99);
/// assert_eq!(mm.get(0), 99);
/// assert_eq!(mm.borrow(&m2).get(0), 99);
/// ```
///
/// The `MemoryToken`s are also used to disable a mounted `Memory`. A disabled
/// memory is effectively unmounted, but remains in the ownership of the
/// `MappedMemory` and can be enabled using `enable_mount()`.
///
/// ```
/// # use osciemu::memory::{Memory, NullMemory, SliceMemory, MappedMemory};
/// # use osciemu::memory::mappedmemory::MemoryToken;
/// # let mut mm = MappedMemory::new();
/// # let m1 = mm.mount(0, Box::new(NullMemory::new()));
/// # let m2 = mm.mount(0, Box::new(SliceMemory::from_slice(Box::new([1, 2, 3, 4]))));
/// # mm.set(0, 99);
/// // ...
/// mm.disable_mount(&m2);
/// assert_eq!(mm.get(0), 0);
/// assert_eq!(mm.borrow(&m2).get(0), 99);
/// ```
///
/// To move a memory out of the `MappedMemory`, a memory can be unmounted:
///
/// ```
/// # use osciemu::memory::{Memory, NullMemory, SliceMemory, MappedMemory};
/// # use osciemu::memory::mappedmemory::MemoryToken;
/// # let mut mm = MappedMemory::new();
/// # let m1 = mm.mount(0, Box::new(NullMemory::new()));
/// # let m2 = mm.mount(0, Box::new(SliceMemory::from_slice(Box::new([1, 2, 3, 4]))));
/// # mm.set(0, 99);
/// // ...
/// let m2 = mm.unmount(m2);
/// assert_eq!(mm.get(0), 0);
/// assert_eq!(m2.get(0), 99);
/// ```
///
/// # Panics
/// `MappedMemory` panics when an unmapped address is read or written.
pub struct MappedMemory {
    memories: Vec<Entry>,
}

static id_counter: AtomicIsize = AtomicIsize::new(0);

struct Entry {
    id: isize,
    start_address: usize,
    size: usize,
    active: bool,
    memory: Box<Memory>,
}

impl Entry {
    fn contains(&self, addr: usize) -> bool {
        self.start_address <= addr && self.start_address + self.size > addr
    }
}

#[derive(Clone)]
pub struct MemoryToken {
    id: isize,
}

impl MappedMemory {
    pub fn new() -> MappedMemory {
        MappedMemory {
            memories: Vec::new(),
        }
    }

    /// Mounts a `Memory` at the given address.
    pub fn mount(&mut self, start_address: usize, memory: Box<Memory>) -> MemoryToken {
        let size = memory.size();
        let id = id_counter.fetch_add(1, Ordering::Relaxed);
        let new_entry = Entry {
            id,
            start_address,
            size,
            memory,
            active: true
        };
        self.memories.push(new_entry);
        MemoryToken{id}
    }

    /// Unmounts a memory.
    pub fn unmount(&mut self, token: MemoryToken) -> Box<Memory> {
        let idx = self.memories
            .iter()
            .enumerate()
            .find(|&(_idx, ref entry)| entry.id == token.id)
            .map(|(idx, ref _entry)| idx)
            .unwrap();

        self.memories.remove(idx).memory
    }

    /// Disables a memory. This is the same as unmounting without moving
    /// ownership
    pub fn disable_mount(&mut self, token: &MemoryToken) {
        let entry = self.entry_for_token_mut(token);
        entry.active = false;
    }

    // Enables a memory. The mount point remains unchanged.
    pub fn enable_mount(&mut self, token: &MemoryToken) {
        let entry = self.entry_for_token_mut(token);
        entry.active = true;
    }

    /// Borrows a memory.
    pub fn borrow(&self, token: &MemoryToken) -> &Box<Memory> {
        &self.entry_for_token(token).memory
    }

    /// Mutable borrows a memory.
    pub fn borrow_mut(&mut self, token: &MemoryToken) -> &mut Box<Memory> {
        &mut self.entry_for_token_mut(token).memory
    }

    fn entry_for_token(&self, token: &MemoryToken) -> &Entry {
        self.memories
            .iter()
            .find(|ref entry| entry.id == token.id)
            .unwrap()
    }

    fn entry_for_token_mut(&mut self, token: &MemoryToken) -> &mut Entry {
        self.memories
            .iter_mut()
            .find(|ref mut entry| entry.id == token.id)
            .unwrap()
    }

    fn active_entry_at_addr(&self, addr: usize) -> Option<&Entry> {
        self.memories
            .iter()
            .rev()
            .filter(|entry| entry.active)
            .find(|entry| entry.contains(addr))
    }

    fn active_entry_at_addr_mut(&mut self, addr: usize) -> Option<&mut Entry> {
        self.memories
            .iter_mut()
            .rev()
            .filter(|entry| entry.active)
            .find(|entry| entry.contains(addr))
    }
}

impl Memory for MappedMemory {
    fn get(&self, addr: usize) -> u32 {
        self.active_entry_at_addr(addr)
            .map(|entry| entry.memory.get(addr - entry.start_address))
            .expect("Out of bounds")
    }

    fn set(&mut self, addr: usize, value: u32) {
        self.active_entry_at_addr_mut(addr)
            .map(|entry| entry.memory.set(addr - entry.start_address, value))
            .expect("Out of bounds")
    }

    fn size(&self) -> usize {
        self.memories
            .iter()
            .filter(|entry| entry.active)
            .map(|entry| entry.start_address + entry.size)
            .max()
            .unwrap_or(0)
    }
}

#[cfg(test)]
mod tests {
    use memory::{Memory, SliceMemory, NullMemory};

    #[test]
    fn overlapping_mounts() {
        let mut mm = super::MappedMemory::new();
        let m1 = mm.mount(0, Box::new(NullMemory::new()));
        let m2 = mm.mount(1, Box::new(SliceMemory::from_slice(Box::new([2, 2]))));
        let m3 = mm.mount(2, Box::new(SliceMemory::from_slice(Box::new([3]))));
        assert_eq!(mm.get(0), 0);
        assert_eq!(mm.get(1), 2);
        assert_eq!(mm.get(2), 3);
        assert_eq!(mm.get(3), 0);
    }

    #[test]
    fn get_and_set() {
        let mut mm = super::MappedMemory::new();
        let m1 = mm.mount(0, Box::new(SliceMemory::from_slice(Box::new([1]))));
        let m2 = mm.mount(2, Box::new(SliceMemory::from_slice(Box::new([2, 2]))));

        assert_eq!(mm.get(0), 1);
        assert_eq!(mm.get(2), 2);

        mm.set(0, 3);
        assert_eq!(mm.get(0), 3);

        mm.set(3, 0);
        assert_eq!(mm.get(3), 0);
        assert_eq!(mm.borrow(&m1).get(0), 3);
        assert_eq!(mm.borrow(&m2).get(1), 0);
    }

    #[test]
    fn size() {
        let mut mm = super::MappedMemory::new();
        let m1 = Box::new(SliceMemory::from_slice(Box::new([1])));
        let m2 = Box::new(SliceMemory::from_slice(Box::new([2, 2])));
        assert_eq!(mm.size(), 0);

        mm.mount(0, m1);
        assert_eq!(mm.size(), 1);

        mm.mount(2, m2);
        assert_eq!(mm.size(), 4);
    }

    #[test]
    fn size_with_overlap() {
        let mut mm = super::MappedMemory::new();
        let m1 = Box::new(SliceMemory::from_slice(Box::new([1, 1, 1, 1, 1])));
        let m2 = Box::new(SliceMemory::from_slice(Box::new([2, 2])));
        let m3 = Box::new(SliceMemory::from_slice(Box::new([3, 3, 3])));

        mm.mount(0, m1);
        mm.mount(2, m2);
        assert_eq!(mm.size(), 5);

        mm.mount(6, m3);
        assert_eq!(mm.size(), 9);
    }

    #[test]
    fn disable_mount() {
        let mut mm = super::MappedMemory::new();
        let m1 = mm.mount(0, Box::new(SliceMemory::from_slice(Box::new([1, 1, 1, 1, 1]))));
        let m2 = mm.mount(0, Box::new(SliceMemory::from_slice(Box::new([2, 2, 2, 2, 2]))));
        let m3 = mm.mount(0, Box::new(SliceMemory::from_slice(Box::new([3, 3, 3, 3, 3]))));

        for i in 0..5 {
            assert_eq!(mm.get(i), 3);
        }
        mm.disable_mount(&m3);
        for i in 0..5 {
            assert_eq!(mm.get(i), 2);
        }
        mm.disable_mount(&m1);
        for i in 0..5 {
            assert_eq!(mm.get(i), 2);
        }
    }

    #[test]
    fn enable_mount() {
        let mut mm = super::MappedMemory::new();
        let m1 = mm.mount(0, Box::new(SliceMemory::from_slice(Box::new([1, 1, 1, 1, 1]))));
        let m2 = mm.mount(0, Box::new(SliceMemory::from_slice(Box::new([2, 2, 2, 2, 2]))));

        for i in 0..5 {
            assert_eq!(mm.get(i), 2);
        }
        mm.disable_mount(&m2);
        for i in 0..5 {
            assert_eq!(mm.get(i), 1);
        }
        mm.enable_mount(&m2);
        for i in 0..5 {
            assert_eq!(mm.get(i), 2);
        }
    }

    #[test]
    fn unmount() {
        let mut mm = super::MappedMemory::new();
        let m1 = mm.mount(0, Box::new(SliceMemory::from_slice(Box::new([1, 1, 1, 1, 1]))));
        let m2 = mm.mount(0, Box::new(SliceMemory::from_slice(Box::new([2, 2, 2, 2, 2]))));

        for i in 0..5 {
            assert_eq!(mm.get(i), 2);
        }
        let m2 = mm.unmount(m2);
        for i in 0..5 {
            assert_eq!(mm.get(i), 1);
            assert_eq!(m2.get(i), 2);
        }
    }

    #[test]
    #[should_panic]
    fn read_disabled() {
        let mut mm = super::MappedMemory::new();
        let m1 = mm.mount(0, Box::new(SliceMemory::from_slice(Box::new([1]))));
        let m2 = mm.mount(1, Box::new(SliceMemory::from_slice(Box::new([2]))));
        let m3 = mm.mount(2, Box::new(SliceMemory::from_slice(Box::new([3]))));
        mm.disable_mount(&m2);
        mm.get(1);
    }
}
<commit_msg>feat(mappedmemory): Add is_mounted_memory<commit_after>use memory::Memory;
use std::vec::Vec;
use std::sync::atomic::{AtomicIsize, Ordering};

/// Maps multiple `Memory`s into a single address space.
///
/// The `MappedMemory` allows to unify multiple `Memory`s in one address space.
/// A `Memory` is mounted at a certain address and is from now on responsible
/// for all reads and writes starting at that address (the “mount point”) and
/// ending where the mounted memory ends. The read and write calls for the
/// responsible `Memory` will be given an address _relative_ to e mount point.
///
/// ```text
///                   Unmapped
///                <-------->
///       mem_a                   mem_b
/// |--------------|            |------|
///                            NullMemory
///                         |--------------
/// |------------- mapped_mem ----------->
/// |              |            |      |
/// 0            0x100        0x200  0x280
/// ```
///
/// For example: `mapped_mem.get(0x208)` would yield the same value as
/// `mem_b.get(0x008)`.
///
/// # Examples
///
/// ```
/// use osciemu::memory::{Memory, SliceMemory, MappedMemory};
///
/// let mut mm = MappedMemory::new();
/// let m1 = mm.mount(0, Box::new(SliceMemory::from_slice(Box::new([1]))));
/// let m2 = mm.mount(2, Box::new(SliceMemory::from_slice(Box::new([2, 2]))));
/// // Now mm =~ [1, _, 2, 2]
/// assert_eq!(mm.get(0), 1);
/// assert_eq!(mm.get(3), 2);
/// ```
///
/// A call to `mount` returns a `MemoryToken`. These tokens can be used to
/// borrow a `Memory` even if it has been mounted. This works similar to a
/// `std::cell::RefCell`.
///
/// ```
/// use osciemu::memory::{Memory, NullMemory, SliceMemory, MappedMemory};
/// use osciemu::memory::mappedmemory::MemoryToken;
///
/// let mut mm = MappedMemory::new();
/// let m1 = mm.mount(0, Box::new(NullMemory::new()));
/// let m2 = mm.mount(0, Box::new(SliceMemory::from_slice(Box::new([1, 2, 3, 4]))));
/// mm.set(0, 99);
/// assert_eq!(mm.get(0), 99);
/// assert_eq!(mm.borrow(&m2).get(0), 99);
/// ```
///
/// The `MemoryToken`s are also used to disable a mounted `Memory`. A disabled
/// memory is effectively unmounted, but remains in the ownership of the
/// `MappedMemory` and can be enabled using `enable_mount()`.
///
/// ```
/// # use osciemu::memory::{Memory, NullMemory, SliceMemory, MappedMemory};
/// # use osciemu::memory::mappedmemory::MemoryToken;
/// # let mut mm = MappedMemory::new();
/// # let m1 = mm.mount(0, Box::new(NullMemory::new()));
/// # let m2 = mm.mount(0, Box::new(SliceMemory::from_slice(Box::new([1, 2, 3, 4]))));
/// # mm.set(0, 99);
/// // ...
/// mm.disable_mount(&m2);
/// assert_eq!(mm.get(0), 0);
/// assert_eq!(mm.borrow(&m2).get(0), 99);
/// ```
///
/// To move a memory out of the `MappedMemory`, a memory can be unmounted:
///
/// ```
/// # use osciemu::memory::{Memory, NullMemory, SliceMemory, MappedMemory};
/// # use osciemu::memory::mappedmemory::MemoryToken;
/// # let mut mm = MappedMemory::new();
/// # let m1 = mm.mount(0, Box::new(NullMemory::new()));
/// # let m2 = mm.mount(0, Box::new(SliceMemory::from_slice(Box::new([1, 2, 3, 4]))));
/// # mm.set(0, 99);
/// // ...
/// let m2 = mm.unmount(m2);
/// assert_eq!(mm.get(0), 0);
/// assert_eq!(m2.get(0), 99);
/// ```
///
/// # Panics
/// `MappedMemory` panics when an unmapped address is read or written.
pub struct MappedMemory {
    memories: Vec<Entry>,
}

static id_counter: AtomicIsize = AtomicIsize::new(0);

struct Entry {
    id: isize,
    start_address: usize,
    size: usize,
    enabled: bool,
    memory: Box<Memory>,
}

impl Entry {
    fn contains(&self, addr: usize) -> bool {
        self.start_address <= addr && self.start_address + self.size > addr
    }
}

#[derive(Clone)]
pub struct MemoryToken {
    id: isize,
}

impl MappedMemory {
    pub fn new() -> MappedMemory {
        MappedMemory {
            memories: Vec::new(),
        }
    }

    /// Mounts a `Memory` at the given address.
    pub fn mount(&mut self, start_address: usize, memory: Box<Memory>) -> MemoryToken {
        let size = memory.size();
        let id = id_counter.fetch_add(1, Ordering::Relaxed);
        let new_entry = Entry {
            id,
            start_address,
            size,
            memory,
            enabled: true
        };
        self.memories.push(new_entry);
        MemoryToken{id}
    }

    /// Unmounts a memory.
    pub fn unmount(&mut self, token: MemoryToken) -> Box<Memory> {
        let idx = self.memories
            .iter()
            .enumerate()
            .find(|&(_idx, ref entry)| entry.id == token.id)
            .map(|(idx, ref _entry)| idx)
            .unwrap();

        self.memories.remove(idx).memory
    }

    pub fn is_enabled_mount(&self, token: &MemoryToken) -> bool {
        self.entry_for_token(token).enabled
    }

    /// Disables a memory. This is the same as unmounting without moving
    /// ownership
    pub fn disable_mount(&mut self, token: &MemoryToken) {
        let entry = self.entry_for_token_mut(token);
        entry.enabled = false;
    }

    // Enables a memory. The mount point remains unchanged.
    pub fn enable_mount(&mut self, token: &MemoryToken) {
        let entry = self.entry_for_token_mut(token);
        entry.enabled = true;
    }

    /// Borrows a memory.
    pub fn borrow(&self, token: &MemoryToken) -> &Box<Memory> {
        &self.entry_for_token(token).memory
    }

    /// Mutable borrows a memory.
    pub fn borrow_mut(&mut self, token: &MemoryToken) -> &mut Box<Memory> {
        &mut self.entry_for_token_mut(token).memory
    }

    fn entry_for_token(&self, token: &MemoryToken) -> &Entry {
        self.memories
            .iter()
            .find(|ref entry| entry.id == token.id)
            .unwrap()
    }

    fn entry_for_token_mut(&mut self, token: &MemoryToken) -> &mut Entry {
        self.memories
            .iter_mut()
            .find(|ref mut entry| entry.id == token.id)
            .unwrap()
    }

    fn enabled_entry_at_addr(&self, addr: usize) -> Option<&Entry> {
        self.memories
            .iter()
            .rev()
            .filter(|entry| entry.enabled)
            .find(|entry| entry.contains(addr))
    }

    fn enabled_entry_at_addr_mut(&mut self, addr: usize) -> Option<&mut Entry> {
        self.memories
            .iter_mut()
            .rev()
            .filter(|entry| entry.enabled)
            .find(|entry| entry.contains(addr))
    }
}

impl Memory for MappedMemory {
    fn get(&self, addr: usize) -> u32 {
        self.enabled_entry_at_addr(addr)
            .map(|entry| entry.memory.get(addr - entry.start_address))
            .expect("Out of bounds")
    }

    fn set(&mut self, addr: usize, value: u32) {
        self.enabled_entry_at_addr_mut(addr)
            .map(|entry| entry.memory.set(addr - entry.start_address, value))
            .expect("Out of bounds")
    }

    fn size(&self) -> usize {
        self.memories
            .iter()
            .filter(|entry| entry.enabled)
            .map(|entry| entry.start_address + entry.size)
            .max()
            .unwrap_or(0)
    }
}

#[cfg(test)]
mod tests {
    use memory::{Memory, SliceMemory, NullMemory};

    #[test]
    fn overlapping_mounts() {
        let mut mm = super::MappedMemory::new();
        let m1 = mm.mount(0, Box::new(NullMemory::new()));
        let m2 = mm.mount(1, Box::new(SliceMemory::from_slice(Box::new([2, 2]))));
        let m3 = mm.mount(2, Box::new(SliceMemory::from_slice(Box::new([3]))));
        assert_eq!(mm.get(0), 0);
        assert_eq!(mm.get(1), 2);
        assert_eq!(mm.get(2), 3);
        assert_eq!(mm.get(3), 0);
    }

    #[test]
    fn get_and_set() {
        let mut mm = super::MappedMemory::new();
        let m1 = mm.mount(0, Box::new(SliceMemory::from_slice(Box::new([1]))));
        let m2 = mm.mount(2, Box::new(SliceMemory::from_slice(Box::new([2, 2]))));

        assert_eq!(mm.get(0), 1);
        assert_eq!(mm.get(2), 2);

        mm.set(0, 3);
        assert_eq!(mm.get(0), 3);

        mm.set(3, 0);
        assert_eq!(mm.get(3), 0);
        assert_eq!(mm.borrow(&m1).get(0), 3);
        assert_eq!(mm.borrow(&m2).get(1), 0);
    }

    #[test]
    fn size() {
        let mut mm = super::MappedMemory::new();
        let m1 = Box::new(SliceMemory::from_slice(Box::new([1])));
        let m2 = Box::new(SliceMemory::from_slice(Box::new([2, 2])));
        assert_eq!(mm.size(), 0);

        mm.mount(0, m1);
        assert_eq!(mm.size(), 1);

        mm.mount(2, m2);
        assert_eq!(mm.size(), 4);
    }

    #[test]
    fn size_with_overlap() {
        let mut mm = super::MappedMemory::new();
        let m1 = Box::new(SliceMemory::from_slice(Box::new([1, 1, 1, 1, 1])));
        let m2 = Box::new(SliceMemory::from_slice(Box::new([2, 2])));
        let m3 = Box::new(SliceMemory::from_slice(Box::new([3, 3, 3])));

        mm.mount(0, m1);
        mm.mount(2, m2);
        assert_eq!(mm.size(), 5);

        mm.mount(6, m3);
        assert_eq!(mm.size(), 9);
    }

    #[test]
    fn disable_mount() {
        let mut mm = super::MappedMemory::new();
        let m1 = mm.mount(0, Box::new(SliceMemory::from_slice(Box::new([1, 1, 1, 1, 1]))));
        let m2 = mm.mount(0, Box::new(SliceMemory::from_slice(Box::new([2, 2, 2, 2, 2]))));
        let m3 = mm.mount(0, Box::new(SliceMemory::from_slice(Box::new([3, 3, 3, 3, 3]))));

        for i in 0..5 {
            assert_eq!(mm.get(i), 3);
        }
        mm.disable_mount(&m3);
        for i in 0..5 {
            assert_eq!(mm.get(i), 2);
        }
        mm.disable_mount(&m1);
        for i in 0..5 {
            assert_eq!(mm.get(i), 2);
        }
    }

    #[test]
    fn enable_mount() {
        let mut mm = super::MappedMemory::new();
        let m1 = mm.mount(0, Box::new(SliceMemory::from_slice(Box::new([1, 1, 1, 1, 1]))));
        let m2 = mm.mount(0, Box::new(SliceMemory::from_slice(Box::new([2, 2, 2, 2, 2]))));

        for i in 0..5 {
            assert_eq!(mm.get(i), 2);
        }
        mm.disable_mount(&m2);
        for i in 0..5 {
            assert_eq!(mm.get(i), 1);
        }
        mm.enable_mount(&m2);
        for i in 0..5 {
            assert_eq!(mm.get(i), 2);
        }
    }

    #[test]
    fn unmount() {
        let mut mm = super::MappedMemory::new();
        let m1 = mm.mount(0, Box::new(SliceMemory::from_slice(Box::new([1, 1, 1, 1, 1]))));
        let m2 = mm.mount(0, Box::new(SliceMemory::from_slice(Box::new([2, 2, 2, 2, 2]))));

        for i in 0..5 {
            assert_eq!(mm.get(i), 2);
        }
        let m2 = mm.unmount(m2);
        for i in 0..5 {
            assert_eq!(mm.get(i), 1);
            assert_eq!(m2.get(i), 2);
        }
    }

    #[test]
    fn is_enabled_mount() {
        let mut mm = super::MappedMemory::new();
        let m1 = mm.mount(0, Box::new(SliceMemory::from_slice(Box::new([1, 1, 1, 1, 1]))));
        let m2 = mm.mount(0, Box::new(SliceMemory::from_slice(Box::new([2, 2, 2, 2, 2]))));

        assert!(mm.is_enabled_mount(&m2));
        mm.disable_mount(&m2);
        assert!(!mm.is_enabled_mount(&m2));
        mm.enable_mount(&m2);
        assert!(mm.is_enabled_mount(&m2));
    }

    #[test]
    #[should_panic]
    fn read_disabled() {
        let mut mm = super::MappedMemory::new();
        let m1 = mm.mount(0, Box::new(SliceMemory::from_slice(Box::new([1]))));
        let m2 = mm.mount(1, Box::new(SliceMemory::from_slice(Box::new([2]))));
        let m3 = mm.mount(2, Box::new(SliceMemory::from_slice(Box::new([3]))));
        mm.disable_mount(&m2);
        mm.get(1);
    }
}
<|endoftext|>