<commit_before>pub extern crate xcb;

pub mod error;
mod run;

use std::thread;
use std::time::{ Duration, Instant };
use std::sync::{ Arc, RwLock };
use std::sync::mpsc::{ Sender, channel };
use std::collections::HashMap;
use xcb::{ ConnError, Connection, Xid };
use xcb::{ x, xfixes };
use error::Error;

pub const INCR_CHUNK_SIZE: usize = 4000;
const POLL_DURATION: u64 = 50;
type SetMap = Arc<RwLock<HashMap<x::Atom, (x::Atom, Vec<u8>)>>>;

#[derive(Clone, Debug)]
pub struct Atoms {
    pub primary: x::Atom,
    pub clipboard: x::Atom,
    pub property: x::Atom,
    pub targets: x::Atom,
    pub string: x::Atom,
    pub utf8_string: x::Atom,
    pub incr: x::Atom,
}

impl Atoms {
    fn intern_all(conn: &xcb::Connection) -> xcb::Result<Atoms> {
        let clipboard = conn.send_request(&x::InternAtom{
            only_if_exists: false,
            name: b"CLIPBOARD",
        });
        let property = conn.send_request(&x::InternAtom{
            only_if_exists: false,
            name: b"THIS_CLIPBOARD_OUT",
        });
        let targets = conn.send_request(&x::InternAtom{
            only_if_exists: false,
            name: b"TARGETS",
        });
        let utf8_string = conn.send_request(&x::InternAtom{
            only_if_exists: false,
            name: b"UTF8_STRING",
        });
        let incr = conn.send_request(&x::InternAtom{
            only_if_exists: false,
            name: b"INCR",
        });
        Ok(Atoms {
            primary: x::ATOM_PRIMARY,
            clipboard: conn.wait_for_reply(clipboard)?.atom(),
            property: conn.wait_for_reply(property)?.atom(),
            targets: conn.wait_for_reply(targets)?.atom(),
            string: x::ATOM_STRING,
            utf8_string: conn.wait_for_reply(utf8_string)?.atom(),
            incr: conn.wait_for_reply(incr)?.atom(),
        })
    }
}

/// X11 Clipboard
pub struct Clipboard {
    pub getter: Context,
    pub setter: Arc<Context>,
    setmap: SetMap,
    send: Sender<x::Atom>
}

pub struct Context {
    pub connection: Connection,
    pub screen: i32,
    pub window: x::Window,
    pub atoms: Atoms
}

#[inline]
fn get_atom(connection: &Connection, name: &str) -> Result<x::Atom, Error> {
    let cookie = connection.send_request(&x::InternAtom {
        only_if_exists: false,
        name: name.as_bytes()
    });
    let reply = connection.wait_for_reply(cookie)?;
    Ok(reply.atom())
}

impl Context {
    pub fn new(displayname: Option<&str>) -> Result<Self, Error> {
        let (connection, screen) = Connection::connect_with_extensions(displayname,  &[], &[])?;
        let window = connection.generate_id();

        {
            let screen = connection.get_setup().roots().nth(screen as usize)
                .ok_or(Error::XcbConn(ConnError::ClosedInvalidScreen))?;
            connection.send_and_check_request(&x::CreateWindow {
                depth: x::COPY_FROM_PARENT as u8,
                wid: window,
                parent: screen.root(),
                x: 0,
                y: 0,
                width: 1,
                height: 1,
                border_width: 0,
                class: x::WindowClass::InputOutput,
                visual: screen.root_visual(),
                value_list: &[
                    x::Cw::EventMask(x::EventMask::STRUCTURE_NOTIFY | x::EventMask::PROPERTY_CHANGE)
                ],
            })?;
        }

        let atoms = Atoms::intern_all(&connection)?;

        Ok(Context { connection, screen, window, atoms })
    }

    pub fn get_atom(&self, name: &str) -> Result<x::Atom, Error> {
        get_atom(&self.connection, name)
    }
}

impl Clipboard {
    /// Create Clipboard.
    pub fn new() -> Result<Self, Error> {
        let getter = Context::new(None)?;
        let setter = Arc::new(Context::new(None)?);
        let setter2 = Arc::clone(&setter);
        let setmap = Arc::new(RwLock::new(HashMap::new()));
        let setmap2 = Arc::clone(&setmap);

        let (sender, receiver) = channel();
        let max_length = setter.connection.get_maximum_request_length() as usize * 4;
        thread::spawn(move || run::run(&setter2, &setmap2, max_length, &receiver));

        Ok(Clipboard { getter, setter, setmap, send: sender })
    }

    fn process_event<T>(&self, buff: &mut Vec<u8>, selection: x::Atom, target: x::Atom, property: x::Atom, timeout: T, use_xfixes: bool)
        -> Result<(), Error>
        where T: Into<Option<Duration>>
    {
        let mut is_incr = false;
        let timeout = timeout.into();
        let start_time =
            if timeout.is_some() { Some(Instant::now()) }
            else { None };

        loop {
            if timeout.into_iter()
                .zip(start_time)
                .next()
                .map(|(timeout, time)| (Instant::now() - time) >= timeout)
                .unwrap_or(false)
            {
                return Err(Error::Timeout);
            }

            let event = match use_xfixes {
                true => self.getter.connection.wait_for_event()?,
                false => {
                    match self.getter.connection.poll_for_event()? {
                        Some(event) => event,
                        None => {
                            thread::park_timeout(Duration::from_millis(POLL_DURATION));
                            continue
                        }
                    }
                }
            };

            match event {
                xcb::Event::XFixes(xfixes::Event::SelectionNotify(event)) if use_xfixes => {
                    self.getter.connection.send_and_check_request(&x::ConvertSelection {
                        requestor: self.getter.window,
                        selection,
                        target,
                        property,
                        time: event.timestamp(),
                    })?;
                },
                xcb::Event::X(x::Event::SelectionNotify(event)) => {
                    if event.selection() != selection { continue };

                    // Note that setting the property argument to None indicates that the
                    // conversion requested could not be made.
                    if event.property().is_none() {
                        break;
                    }

                    let reply = self.getter.connection.wait_for_reply(
                        self.getter.connection.send_request(&x::GetProperty {
                            delete: false,
                            window: self.getter.window,
                            property: event.property(),
                            r#type: x::ATOM_NONE,
                            long_offset: buff.len() as u32,
                            long_length: ::std::u32::MAX,
                        })
                    )?;

                    if reply.r#type() == self.getter.atoms.incr {
                        if let Some(&size) = reply.value::<u32>().get(0) {
                            buff.reserve(size as usize);
                        }
                        self.getter.connection.send_and_check_request(&x::DeleteProperty {
                            window: self.getter.window,
                            property
                        })?;
                        is_incr = true;
                        continue
                    } else if reply.r#type() != target {
                        return Err(Error::UnexpectedType(reply.r#type()));
                    }

                    buff.extend_from_slice(reply.value());
                    break
                },
                xcb::Event::X(x::Event::PropertyNotify(event)) if is_incr => {
                    if event.state() != x::Property::NewValue { continue };

                    let cookie = self.getter.connection.send_request(&x::GetProperty {
                        delete: false,
                        window: self.getter.window,
                        property,
                        r#type: x::ATOM_NONE,
                        long_offset: 0,
                        long_length: 0,
                    });
                    let length = self.getter.connection.wait_for_reply(cookie)?.bytes_after();

                    let cookie = self.getter.connection.send_request(&x::GetProperty {
                        delete: true,
                        window: self.getter.window,
                        property,
                        r#type: x::ATOM_NONE,
                        long_offset: 0,
                        long_length: length,
                    });
                    let reply = self.getter.connection.wait_for_reply(cookie)?;
                    if reply.r#type() != target { continue };

                    let value = reply.value();

                    if value.len() != 0 {
                        buff.extend_from_slice(value);
                    } else {
                        break
                    }
                },
                _ => ()
            }
        }
        Ok(())
    }

    /// load value.
    pub fn load<T>(&self, selection: x::Atom, target: x::Atom, property: x::Atom, timeout: T)
        -> Result<Vec<u8>, Error>
        where T: Into<Option<Duration>>
    {
        let mut buff = Vec::new();
        let timeout = timeout.into();

        self.getter.connection.send_and_check_request(&x::ConvertSelection {
            requestor: self.getter.window,
            selection,
            target,
            property,
            time: x::CURRENT_TIME,
                // FIXME ^
                // Clients should not use CurrentTime for the time argument of a ConvertSelection request.
                // Instead, they should use the timestamp of the event that caused the request to be made.
        })?;

        self.process_event(&mut buff, selection, target, property, timeout, false)?;

        self.getter.connection.send_and_check_request(&x::DeleteProperty {
            window: self.getter.window,
            property,
        })?;

        Ok(buff)
    }

    /// wait for a new value and load it
    pub fn load_wait(&self, selection: x::Atom, target: x::Atom, property: x::Atom)
        -> Result<Vec<u8>, Error>
    {
        let mut buff = Vec::new();

        let screen = &self.getter.connection.get_setup().roots()
            .nth(self.getter.screen as usize)
            .ok_or(Error::XcbConn(ConnError::ClosedInvalidScreen))?;

        self.getter.connection.send_request(&xfixes::QueryVersion {
            client_major_version: 5,
            client_minor_version: 0,
        });
        // Clear selection sources...
        self.getter.connection.send_request(&xfixes::SelectSelectionInput {
            window: screen.root(),
            selection: self.getter.atoms.primary,
            event_mask: xfixes::SelectionEventMask::empty(),
        });
        self.getter.connection.send_request(&xfixes::SelectSelectionInput {
            window: screen.root(),
            selection: self.getter.atoms.clipboard,
            event_mask: xfixes::SelectionEventMask::empty(),
        });
        // ...and set the one requested now
        self.getter.connection.send_and_check_request(&xfixes::SelectSelectionInput {
            window: screen.root(),
            selection,
            event_mask: xfixes::SelectionEventMask::SET_SELECTION_OWNER |
                xfixes::SelectionEventMask::SELECTION_CLIENT_CLOSE |
                xfixes::SelectionEventMask::SELECTION_WINDOW_DESTROY,
        })?;

        self.process_event(&mut buff, selection, target, property, None, true)?;

        self.getter.connection.send_and_check_request(&x::DeleteProperty {
            window: self.getter.window,
            property,
        })?;

        Ok(buff)
    }

    /// store value.
    pub fn store<T: Into<Vec<u8>>>(&self, selection: x::Atom, target: x::Atom, value: T)
        -> Result<(), Error>
    {
        self.send.send(selection)?;
        self.setmap
            .write()
            .map_err(|_| Error::Lock)?
            .insert(selection, (target, value.into()));

        self.setter.connection.send_and_check_request(&x::SetSelectionOwner {
            owner: self.setter.window,
            selection,
            time: x::CURRENT_TIME,
        })?;

        let cookie = self.setter.connection.send_request(&x::GetSelectionOwner {
            selection
        });
        if self.setter.connection.wait_for_reply(cookie)
            .map(|reply| reply.owner() == self.setter.window)
            .unwrap_or(false) {
            Ok(())
        } else {
            Err(Error::Owner)
        }
    }
}
<commit_msg>Activate the XFixes extension<commit_after>pub extern crate xcb;

pub mod error;
mod run;

use std::thread;
use std::time::{ Duration, Instant };
use std::sync::{ Arc, RwLock };
use std::sync::mpsc::{ Sender, channel };
use std::collections::HashMap;
use xcb::{ ConnError, Connection, Extension, Xid };
use xcb::{ x, xfixes };
use error::Error;

pub const INCR_CHUNK_SIZE: usize = 4000;
const POLL_DURATION: u64 = 50;
type SetMap = Arc<RwLock<HashMap<x::Atom, (x::Atom, Vec<u8>)>>>;

#[derive(Clone, Debug)]
pub struct Atoms {
    pub primary: x::Atom,
    pub clipboard: x::Atom,
    pub property: x::Atom,
    pub targets: x::Atom,
    pub string: x::Atom,
    pub utf8_string: x::Atom,
    pub incr: x::Atom,
}

impl Atoms {
    fn intern_all(conn: &xcb::Connection) -> xcb::Result<Atoms> {
        let clipboard = conn.send_request(&x::InternAtom{
            only_if_exists: false,
            name: b"CLIPBOARD",
        });
        let property = conn.send_request(&x::InternAtom{
            only_if_exists: false,
            name: b"THIS_CLIPBOARD_OUT",
        });
        let targets = conn.send_request(&x::InternAtom{
            only_if_exists: false,
            name: b"TARGETS",
        });
        let utf8_string = conn.send_request(&x::InternAtom{
            only_if_exists: false,
            name: b"UTF8_STRING",
        });
        let incr = conn.send_request(&x::InternAtom{
            only_if_exists: false,
            name: b"INCR",
        });
        Ok(Atoms {
            primary: x::ATOM_PRIMARY,
            clipboard: conn.wait_for_reply(clipboard)?.atom(),
            property: conn.wait_for_reply(property)?.atom(),
            targets: conn.wait_for_reply(targets)?.atom(),
            string: x::ATOM_STRING,
            utf8_string: conn.wait_for_reply(utf8_string)?.atom(),
            incr: conn.wait_for_reply(incr)?.atom(),
        })
    }
}

/// X11 Clipboard
pub struct Clipboard {
    pub getter: Context,
    pub setter: Arc<Context>,
    setmap: SetMap,
    send: Sender<x::Atom>
}

pub struct Context {
    pub connection: Connection,
    pub screen: i32,
    pub window: x::Window,
    pub atoms: Atoms
}

#[inline]
fn get_atom(connection: &Connection, name: &str) -> Result<x::Atom, Error> {
    let cookie = connection.send_request(&x::InternAtom {
        only_if_exists: false,
        name: name.as_bytes()
    });
    let reply = connection.wait_for_reply(cookie)?;
    Ok(reply.atom())
}

impl Context {
    pub fn new(displayname: Option<&str>) -> Result<Self, Error> {
        let (connection, screen) = Connection::connect_with_extensions(displayname,  &[Extension::XFixes], &[])?;
        let window = connection.generate_id();

        {
            let screen = connection.get_setup().roots().nth(screen as usize)
                .ok_or(Error::XcbConn(ConnError::ClosedInvalidScreen))?;
            connection.send_and_check_request(&x::CreateWindow {
                depth: x::COPY_FROM_PARENT as u8,
                wid: window,
                parent: screen.root(),
                x: 0,
                y: 0,
                width: 1,
                height: 1,
                border_width: 0,
                class: x::WindowClass::InputOutput,
                visual: screen.root_visual(),
                value_list: &[
                    x::Cw::EventMask(x::EventMask::STRUCTURE_NOTIFY | x::EventMask::PROPERTY_CHANGE)
                ],
            })?;
        }

        let atoms = Atoms::intern_all(&connection)?;

        Ok(Context { connection, screen, window, atoms })
    }

    pub fn get_atom(&self, name: &str) -> Result<x::Atom, Error> {
        get_atom(&self.connection, name)
    }
}

impl Clipboard {
    /// Create Clipboard.
    pub fn new() -> Result<Self, Error> {
        let getter = Context::new(None)?;
        let setter = Arc::new(Context::new(None)?);
        let setter2 = Arc::clone(&setter);
        let setmap = Arc::new(RwLock::new(HashMap::new()));
        let setmap2 = Arc::clone(&setmap);

        let (sender, receiver) = channel();
        let max_length = setter.connection.get_maximum_request_length() as usize * 4;
        thread::spawn(move || run::run(&setter2, &setmap2, max_length, &receiver));

        Ok(Clipboard { getter, setter, setmap, send: sender })
    }

    fn process_event<T>(&self, buff: &mut Vec<u8>, selection: x::Atom, target: x::Atom, property: x::Atom, timeout: T, use_xfixes: bool)
        -> Result<(), Error>
        where T: Into<Option<Duration>>
    {
        let mut is_incr = false;
        let timeout = timeout.into();
        let start_time =
            if timeout.is_some() { Some(Instant::now()) }
            else { None };

        loop {
            if timeout.into_iter()
                .zip(start_time)
                .next()
                .map(|(timeout, time)| (Instant::now() - time) >= timeout)
                .unwrap_or(false)
            {
                return Err(Error::Timeout);
            }

            let event = match use_xfixes {
                true => self.getter.connection.wait_for_event()?,
                false => {
                    match self.getter.connection.poll_for_event()? {
                        Some(event) => event,
                        None => {
                            thread::park_timeout(Duration::from_millis(POLL_DURATION));
                            continue
                        }
                    }
                }
            };

            match event {
                xcb::Event::XFixes(xfixes::Event::SelectionNotify(event)) if use_xfixes => {
                    self.getter.connection.send_and_check_request(&x::ConvertSelection {
                        requestor: self.getter.window,
                        selection,
                        target,
                        property,
                        time: event.timestamp(),
                    })?;
                },
                xcb::Event::X(x::Event::SelectionNotify(event)) => {
                    if event.selection() != selection { continue };

                    // Note that setting the property argument to None indicates that the
                    // conversion requested could not be made.
                    if event.property().is_none() {
                        break;
                    }

                    let reply = self.getter.connection.wait_for_reply(
                        self.getter.connection.send_request(&x::GetProperty {
                            delete: false,
                            window: self.getter.window,
                            property: event.property(),
                            r#type: x::ATOM_NONE,
                            long_offset: buff.len() as u32,
                            long_length: ::std::u32::MAX,
                        })
                    )?;

                    if reply.r#type() == self.getter.atoms.incr {
                        if let Some(&size) = reply.value::<u32>().get(0) {
                            buff.reserve(size as usize);
                        }
                        self.getter.connection.send_and_check_request(&x::DeleteProperty {
                            window: self.getter.window,
                            property
                        })?;
                        is_incr = true;
                        continue
                    } else if reply.r#type() != target {
                        return Err(Error::UnexpectedType(reply.r#type()));
                    }

                    buff.extend_from_slice(reply.value());
                    break
                },
                xcb::Event::X(x::Event::PropertyNotify(event)) if is_incr => {
                    if event.state() != x::Property::NewValue { continue };

                    let cookie = self.getter.connection.send_request(&x::GetProperty {
                        delete: false,
                        window: self.getter.window,
                        property,
                        r#type: x::ATOM_NONE,
                        long_offset: 0,
                        long_length: 0,
                    });
                    let length = self.getter.connection.wait_for_reply(cookie)?.bytes_after();

                    let cookie = self.getter.connection.send_request(&x::GetProperty {
                        delete: true,
                        window: self.getter.window,
                        property,
                        r#type: x::ATOM_NONE,
                        long_offset: 0,
                        long_length: length,
                    });
                    let reply = self.getter.connection.wait_for_reply(cookie)?;
                    if reply.r#type() != target { continue };

                    let value = reply.value();

                    if value.len() != 0 {
                        buff.extend_from_slice(value);
                    } else {
                        break
                    }
                },
                _ => ()
            }
        }
        Ok(())
    }

    /// load value.
    pub fn load<T>(&self, selection: x::Atom, target: x::Atom, property: x::Atom, timeout: T)
        -> Result<Vec<u8>, Error>
        where T: Into<Option<Duration>>
    {
        let mut buff = Vec::new();
        let timeout = timeout.into();

        self.getter.connection.send_and_check_request(&x::ConvertSelection {
            requestor: self.getter.window,
            selection,
            target,
            property,
            time: x::CURRENT_TIME,
                // FIXME ^
                // Clients should not use CurrentTime for the time argument of a ConvertSelection request.
                // Instead, they should use the timestamp of the event that caused the request to be made.
        })?;

        self.process_event(&mut buff, selection, target, property, timeout, false)?;

        self.getter.connection.send_and_check_request(&x::DeleteProperty {
            window: self.getter.window,
            property,
        })?;

        Ok(buff)
    }

    /// wait for a new value and load it
    pub fn load_wait(&self, selection: x::Atom, target: x::Atom, property: x::Atom)
        -> Result<Vec<u8>, Error>
    {
        let mut buff = Vec::new();

        let screen = &self.getter.connection.get_setup().roots()
            .nth(self.getter.screen as usize)
            .ok_or(Error::XcbConn(ConnError::ClosedInvalidScreen))?;

        self.getter.connection.send_request(&xfixes::QueryVersion {
            client_major_version: 5,
            client_minor_version: 0,
        });
        // Clear selection sources...
        self.getter.connection.send_request(&xfixes::SelectSelectionInput {
            window: screen.root(),
            selection: self.getter.atoms.primary,
            event_mask: xfixes::SelectionEventMask::empty(),
        });
        self.getter.connection.send_request(&xfixes::SelectSelectionInput {
            window: screen.root(),
            selection: self.getter.atoms.clipboard,
            event_mask: xfixes::SelectionEventMask::empty(),
        });
        // ...and set the one requested now
        self.getter.connection.send_and_check_request(&xfixes::SelectSelectionInput {
            window: screen.root(),
            selection,
            event_mask: xfixes::SelectionEventMask::SET_SELECTION_OWNER |
                xfixes::SelectionEventMask::SELECTION_CLIENT_CLOSE |
                xfixes::SelectionEventMask::SELECTION_WINDOW_DESTROY,
        })?;

        self.process_event(&mut buff, selection, target, property, None, true)?;

        self.getter.connection.send_and_check_request(&x::DeleteProperty {
            window: self.getter.window,
            property,
        })?;

        Ok(buff)
    }

    /// store value.
    pub fn store<T: Into<Vec<u8>>>(&self, selection: x::Atom, target: x::Atom, value: T)
        -> Result<(), Error>
    {
        self.send.send(selection)?;
        self.setmap
            .write()
            .map_err(|_| Error::Lock)?
            .insert(selection, (target, value.into()));

        self.setter.connection.send_and_check_request(&x::SetSelectionOwner {
            owner: self.setter.window,
            selection,
            time: x::CURRENT_TIME,
        })?;

        let cookie = self.setter.connection.send_request(&x::GetSelectionOwner {
            selection
        });
        if self.setter.connection.wait_for_reply(cookie)
            .map(|reply| reply.owner() == self.setter.window)
            .unwrap_or(false) {
            Ok(())
        } else {
            Err(Error::Owner)
        }
    }
}
<|endoftext|>


<commit_before>use std::cmp;

// 2 billion junctions is good enough for now
pub type JunctionIndex = usize;
pub type Time = i64;
pub type Level = bool;

pub enum GateBehavior {
	And,
	Not,
	Or,
	Xor,
}

// In the future, at time "time", junction "junction" will be set to level "level".
#[derive (Clone, Copy)]
pub struct Signal {
	junction: JunctionIndex,
	level: Level,
	time: Time,
}

#[derive (Clone, Copy)]
pub struct Wire {
	input: JunctionIndex,
	output: JunctionIndex,
	delay: Time,
}

pub struct Gate {
	inputs: Vec <JunctionIndex>,
	output: JunctionIndex,
	behavior: GateBehavior,
}

pub struct World {
	wires: Vec <Wire>,
	gates: Vec <Gate>,

	junctions: Vec <Level>,
	time: Time,
	signals: Vec <Signal>,
}

pub enum WorldCreationErr {
	// Two elements point to the same junction, this is illegal
	FanIn,
}

impl Wire {
	fn new (input: JunctionIndex, output: JunctionIndex, delay: Time) -> Wire {
		Wire {
			input: input,
			output: output,
			delay: delay,
		}
	}
}

impl World {
	pub fn new (wire_tuples: Vec <(JunctionIndex, JunctionIndex, Time)>, gates: Vec <Gate>) -> Result <World, WorldCreationErr> {
		let wires: Vec <Wire> = wire_tuples.iter ().map (|tuple| Wire::new (tuple.0, tuple.1, tuple.2)).collect ();

		let max_junction_wires = wires.iter ().fold (0, |max, wire| cmp::max (max, cmp::max (wire.input, wire.output)));
		let max_junction_gates = gates.iter ().fold (0, |max, gate| {
			let max_input = gate.inputs.iter ().max ();

			let max_junction_gate = match max_input {
				Option::Some (junction) => cmp::max (*junction, gate.output),
				Option::None => gate.output,
			};

			cmp::max (max, max_junction_gate)
		});

		let max_junction = cmp::max (max_junction_gates, max_junction_wires);
		let junction_count = max_junction + 1;

		let mut junction_has_input = vec! [false; junction_count];

		for wire in wires.iter () {
			if junction_has_input [wire.output] {
				return Err (WorldCreationErr::FanIn)
			}

			junction_has_input [wire.output] = true;
		}

		for gate in gates.iter () {
			if junction_has_input [gate.output] {
				return Err (WorldCreationErr::FanIn);
			}

			junction_has_input [gate.output] = true;
		}

		Ok (World {
			time: 0,
			signals: vec![],
			junctions: vec![false; junction_count],
			wires: wires,
			gates: gates,
		})
	}

	pub fn new_half_adder () -> World {
		World::new (
		vec![
			(0, 1, 4),
			(0, 5, 8),
			(2, 3, 4),
			(6, 7, 3),
			(8, 4, 8),
			(8, 9, 4),
		],
		vec![
			Gate {
				inputs: vec![1, 4],
				output: 2,
				behavior: GateBehavior::And,
			},
			Gate {
				inputs: vec![5, 9],
				output: 6,
				behavior: GateBehavior::Xor,
			},
		]).ok ().expect ("Half adder circuit is invalid")
	}

	pub fn new_full_adder () -> World {
		World::new (
		vec![
			(0, 3, 1),
			(0, 5, 1),
			(1, 4, 1),
			(1, 6, 1),
			(2, 11, 1),
			(2, 9, 1),
			(7, 10, 1),
			(7, 8, 1),
			(12, 14, 1),
			(13, 15, 1),
		],
		vec![
			Gate {
				inputs: vec![3, 4],
				output: 7,
				behavior: GateBehavior::Xor,
			},
			Gate {
				inputs: vec![10, 11],
				output: 16,
				behavior: GateBehavior::Xor,
			},
			Gate {
				inputs: vec![8, 9],
				output: 12,
				behavior: GateBehavior::And,
			},
			Gate {
				inputs: vec![5, 6],
				output: 13,
				behavior: GateBehavior::And,
			},
			Gate {
				inputs: vec![14, 15],
				output: 17,
				behavior: GateBehavior::Or,
			},
		]).ok ().expect ("Full adder circuit is invalid")
	}

	pub fn is_settled (& self) -> bool {
		self.signals.len () == 0
	}

	fn sort_signals (&mut self) {
		// This step is just a safety since we should be insertion sorting the delays already
		self.signals.sort_by (|a, b| a.time.cmp (&b.time));
	}

	fn step_gates (&mut self) {
		let mut new_signals = Vec::<Signal>::new ();

		// TODO: Optimize to only touch gates whose inputs have changed
		for gate in self.gates.iter () {
			let inputs: Vec <bool> = gate.inputs.iter ().map (|i| self.junctions [*i]).collect ();

			let output = match gate.behavior {
				GateBehavior::And => inputs.iter ().fold (true, |sum, a| sum && *a),
				GateBehavior::Not => ! inputs [0],
				GateBehavior::Or => inputs.iter ().fold (false, |sum, a| sum || *a),
				GateBehavior::Xor => inputs.iter ().fold (false, |sum, a| sum ^ *a),
			};

			let destiny_level = self.get_junction_destiny (gate.output);

			if destiny_level != output {
				new_signals.push (Signal {
					junction: gate.output,
					time: self.time,
					level: output,
				});
			}
		};

		for signal in new_signals {
			self.signals.push (signal);
		}

		// TODO: Proper insertion sorting
		self.sort_signals ();
	}

	fn get_junction_destiny (& self, junction: JunctionIndex) -> Level {
		if self.signals.len () > 0 {
			// Find the last signal that was heading for that junction
			// Remember that junctions are not allowed to fan in
			for i in 0 .. self.signals.len () {
				let signals = self.signals [self.signals.len () - i - 1];

				if signals.junction == junction {
					return signals.level;
				}
			}
		}

		self.junctions [junction]
	}

	fn step_wires (&mut self) {
		for wire in self.wires.iter () {
			let input = self.junctions [wire.input];

			// Don't push redundant delays
			let destiny_level = self.get_junction_destiny (wire.output);

			if input != destiny_level {
				self.signals.push (Signal {
					junction: wire.output,
					time: self.time + wire.delay,
					level: input,
				});
			}
		};

		// TODO: Proper insertion sorting
		self.sort_signals ();
	}

	fn propagate_signals (&mut self) {
		let next_time = self.signals [0].time;

		for signal in self.signals.iter () {
			if signal.time == next_time {
				self.junctions [signal.junction] = signal.level;
			}
			else if signal.time > next_time {
				break;
			}
		}

		self.signals.retain (|signal| signal.time > next_time);

		self.time = next_time;
	}

	pub fn set_junction (&mut self, junction: JunctionIndex, level: Level) {
		self.signals.push (Signal {
			junction: junction,
			level: level,
			time: self.time,
		});
	}

	pub fn step (&mut self) {
		if self.is_settled () {
			return;
		}

		self.sort_signals ();

		self.propagate_signals ();

		self.step_gates ();
		self.step_wires ();
	}

	pub fn step_to_settled (&mut self) {
		while ! self.is_settled () {
			self.step ();
		}
	}
}

#[test]
pub fn test_fan_in () {
	let world_or_err = World::new (
		vec! [
		(0, 2, 1),
		(1, 2, 1),
		],
		vec! []
	);

	if let Err (WorldCreationErr::FanIn) = world_or_err {
		// Good
	}
	else {
		panic! ("World creation should have thrown a FanIn error");
	}
}

#[test]
pub fn test_half_adder () {
	let mut world = World::new_half_adder ();
	world.step_to_settled ();

	let assert_outputs = |world: &World, msb: Level, lsb: Level| {
		assert_eq! (world.junctions [3], msb);
		assert_eq! (world.junctions [7], lsb);
	};

	assert_outputs (&world, false, false);

	// Junctions 0 and 8 are the input bits

	world.set_junction (0, true);
	world.step_to_settled ();
	assert_outputs (&world, false, true);

	world.set_junction (8, true);
	world.step_to_settled ();
	assert_outputs (&world, true, false);

	world.set_junction (0, false);
	world.step_to_settled ();
	assert_outputs (&world, false, true);
}

#[test]
pub fn test_full_adder () {
	let mut world = World::new_full_adder ();
	world.step_to_settled ();

	let assert_outputs = |world: &mut World, a: Level, b: Level, c: Level, carry: Level, sum: Level| {
		world.set_junction (0, a);
		world.set_junction (1, b);
		world.set_junction (2, c);

		world.step_to_settled ();

		assert_eq! (world.junctions [17], carry);
		assert_eq! (world.junctions [16], sum);
	};

	// Truth table for a full adder
	assert_outputs (&mut world, false, false, false, false, false);
	assert_outputs (&mut world, false, false, true, false, true);
	assert_outputs (&mut world, false, true, true, true, false);
	assert_outputs (&mut world, false, true, false, false, true);

	assert_outputs (&mut world, true, true, false, true, false);
	assert_outputs (&mut world, true, true, true, true, true);
	assert_outputs (&mut world, true, false, true, true, false);
	assert_outputs (&mut world, true, false, false, false, true);
}
<commit_msg>Adding some comments<commit_after>use std::cmp;

// 2 billion junctions is good enough for now
pub type JunctionIndex = usize;
pub type Time = i64;
pub type Level = bool;

pub enum GateBehavior {
	And,
	Not,
	Or,
	Xor,
}

// In the future, at time "time", junction "junction" will be set to level "level".
#[derive (Clone, Copy)]
pub struct Signal {
	junction: JunctionIndex,
	level: Level,
	time: Time,
}

#[derive (Clone, Copy)]
pub struct Wire {
	input: JunctionIndex,
	output: JunctionIndex,
	delay: Time,
}

pub struct Gate {
	inputs: Vec <JunctionIndex>,
	output: JunctionIndex,
	behavior: GateBehavior,
}

pub struct World {
	// These don't change at runtime
	wires: Vec <Wire>,
	gates: Vec <Gate>,

	junctions: Vec <Level>,
	time: Time,
	signals: Vec <Signal>,
}

pub enum WorldCreationErr {
	// Two elements point to the same junction, this is illegal
	FanIn,
}

impl Wire {
	fn new (input: JunctionIndex, output: JunctionIndex, delay: Time) -> Wire {
		Wire {
			input: input,
			output: output,
			delay: delay,
		}
	}
}

impl World {
	pub fn new (wire_tuples: Vec <(JunctionIndex, JunctionIndex, Time)>, gates: Vec <Gate>) -> Result <World, WorldCreationErr> {
		let wires: Vec <Wire> = wire_tuples.iter ().map (|tuple| Wire::new (tuple.0, tuple.1, tuple.2)).collect ();

		let max_junction_wires = wires.iter ().fold (0, |max, wire| cmp::max (max, cmp::max (wire.input, wire.output)));
		let max_junction_gates = gates.iter ().fold (0, |max, gate| {
			let max_input = gate.inputs.iter ().max ();

			let max_junction_gate = match max_input {
				Option::Some (junction) => cmp::max (*junction, gate.output),
				Option::None => gate.output,
			};

			cmp::max (max, max_junction_gate)
		});

		let max_junction = cmp::max (max_junction_gates, max_junction_wires);
		let junction_count = max_junction + 1;

		let mut junction_has_input = vec! [false; junction_count];

		for wire in wires.iter () {
			if junction_has_input [wire.output] {
				return Err (WorldCreationErr::FanIn)
			}

			junction_has_input [wire.output] = true;
		}

		for gate in gates.iter () {
			if junction_has_input [gate.output] {
				return Err (WorldCreationErr::FanIn);
			}

			junction_has_input [gate.output] = true;
		}

		Ok (World {
			time: 0,
			signals: vec![],
			junctions: vec![false; junction_count],
			wires: wires,
			gates: gates,
		})
	}

	pub fn new_half_adder () -> World {
		World::new (
		vec![
			(0, 1, 4),
			(0, 5, 8),
			(2, 3, 4),
			(6, 7, 3),
			(8, 4, 8),
			(8, 9, 4),
		],
		vec![
			Gate {
				inputs: vec![1, 4],
				output: 2,
				behavior: GateBehavior::And,
			},
			Gate {
				inputs: vec![5, 9],
				output: 6,
				behavior: GateBehavior::Xor,
			},
		]).ok ().expect ("Half adder circuit is invalid")
	}

	pub fn new_full_adder () -> World {
		World::new (
		vec![
			(0, 3, 1),
			(0, 5, 1),
			(1, 4, 1),
			(1, 6, 1),
			(2, 11, 1),
			(2, 9, 1),
			(7, 10, 1),
			(7, 8, 1),
			(12, 14, 1),
			(13, 15, 1),
		],
		vec![
			Gate {
				inputs: vec![3, 4],
				output: 7,
				behavior: GateBehavior::Xor,
			},
			Gate {
				inputs: vec![10, 11],
				output: 16,
				behavior: GateBehavior::Xor,
			},
			Gate {
				inputs: vec![8, 9],
				output: 12,
				behavior: GateBehavior::And,
			},
			Gate {
				inputs: vec![5, 6],
				output: 13,
				behavior: GateBehavior::And,
			},
			Gate {
				inputs: vec![14, 15],
				output: 17,
				behavior: GateBehavior::Or,
			},
		]).ok ().expect ("Full adder circuit is invalid")
	}

	pub fn is_settled (& self) -> bool {
		self.signals.len () == 0
	}

	fn sort_signals (&mut self) {
		// This step is just a safety since we should be insertion sorting the delays already
		self.signals.sort_by (|a, b| a.time.cmp (&b.time));
	}

	fn step_gates (&mut self) {
		let mut new_signals = Vec::<Signal>::new ();

		// TODO: Optimize to only touch gates whose inputs have changed
		for gate in self.gates.iter () {
			let inputs: Vec <bool> = gate.inputs.iter ().map (|i| self.junctions [*i]).collect ();

			let output = match gate.behavior {
				GateBehavior::And => inputs.iter ().fold (true, |sum, a| sum && *a),
				GateBehavior::Not => ! inputs [0],
				GateBehavior::Or => inputs.iter ().fold (false, |sum, a| sum || *a),
				GateBehavior::Xor => inputs.iter ().fold (false, |sum, a| sum ^ *a),
			};

			let destiny_level = self.get_junction_destiny (gate.output);

			if destiny_level != output {
				new_signals.push (Signal {
					junction: gate.output,
					time: self.time,
					level: output,
				});
			}
		};

		// TODO: Proper insertion sorting
		// If the number of new signals is small compared to the number of
		// in-flight signals, I could do a few binary insertions sorts.
		// If the number is large, it might be better to do a mergesort.
		for signal in new_signals {
			self.signals.push (signal);
		}

		self.sort_signals ();
	}

	fn get_junction_destiny (& self, junction: JunctionIndex) -> Level {
		if self.signals.len () > 0 {
			// Find the last signal that was heading for that junction
			// Remember that junctions are not allowed to fan in
			for i in 0 .. self.signals.len () {
				let signals = self.signals [self.signals.len () - i - 1];

				if signals.junction == junction {
					return signals.level;
				}
			}
		}

		self.junctions [junction]
	}

	fn step_wires (&mut self) {
		for wire in self.wires.iter () {
			let input = self.junctions [wire.input];

			// Don't push redundant delays
			let destiny_level = self.get_junction_destiny (wire.output);

			if input != destiny_level {
				self.signals.push (Signal {
					junction: wire.output,
					time: self.time + wire.delay,
					level: input,
				});
			}
		};

		// TODO: Proper insertion sorting
		self.sort_signals ();
	}

	fn propagate_signals (&mut self) {
		let next_time = self.signals [0].time;

		for signal in self.signals.iter () {
			if signal.time == next_time {
				self.junctions [signal.junction] = signal.level;
			}
			else if signal.time > next_time {
				break;
			}
		}

		self.signals.retain (|signal| signal.time > next_time);

		self.time = next_time;
	}

	pub fn set_junction (&mut self, junction: JunctionIndex, level: Level) {
		self.signals.push (Signal {
			junction: junction,
			level: level,
			time: self.time,
		});
	}

	pub fn step (&mut self) {
		if self.is_settled () {
			return;
		}

		self.sort_signals ();

		self.propagate_signals ();

		self.step_gates ();
		self.step_wires ();
	}

	pub fn step_to_settled (&mut self) {
		while ! self.is_settled () {
			self.step ();
		}
	}
}

#[test]
pub fn test_fan_in () {
	let world_or_err = World::new (
		vec! [
		(0, 2, 1),
		(1, 2, 1),
		],
		vec! []
	);

	if let Err (WorldCreationErr::FanIn) = world_or_err {
		// Good
	}
	else {
		panic! ("World creation should have thrown a FanIn error");
	}
}

#[test]
pub fn test_half_adder () {
	let mut world = World::new_half_adder ();
	world.step_to_settled ();

	let assert_outputs = |world: &World, msb: Level, lsb: Level| {
		assert_eq! (world.junctions [3], msb);
		assert_eq! (world.junctions [7], lsb);
	};

	assert_outputs (&world, false, false);

	// Junctions 0 and 8 are the input bits

	world.set_junction (0, true);
	world.step_to_settled ();
	assert_outputs (&world, false, true);

	world.set_junction (8, true);
	world.step_to_settled ();
	assert_outputs (&world, true, false);

	world.set_junction (0, false);
	world.step_to_settled ();
	assert_outputs (&world, false, true);
}

#[test]
pub fn test_full_adder () {
	let mut world = World::new_full_adder ();
	world.step_to_settled ();

	let assert_outputs = |world: &mut World, a: Level, b: Level, c: Level, carry: Level, sum: Level| {
		world.set_junction (0, a);
		world.set_junction (1, b);
		world.set_junction (2, c);

		world.step_to_settled ();

		assert_eq! (world.junctions [17], carry);
		assert_eq! (world.junctions [16], sum);
	};

	// Truth table for a full adder
	assert_outputs (&mut world, false, false, false, false, false);
	assert_outputs (&mut world, false, false, true, false, true);
	assert_outputs (&mut world, false, true, true, true, false);
	assert_outputs (&mut world, false, true, false, false, true);

	assert_outputs (&mut world, true, true, false, true, false);
	assert_outputs (&mut world, true, true, true, true, true);
	assert_outputs (&mut world, true, false, true, true, false);
	assert_outputs (&mut world, true, false, false, false, true);
}
<|endoftext|>


<commit_before>/* 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/. */

#![feature(slicing_syntax)]

extern crate "rust-md2" as md2;

use md2::{SBOX, SBOXI};
use std::iter::range_inclusive;
use std::slice::bytes::{copy_memory, MutableByteVector};

struct ByteRange {
  current: Vec<u8>,
  first: bool
}

impl ByteRange {
  fn new(num_bytes: uint) -> ByteRange {
    ByteRange { current: Vec::from_elem(num_bytes, 0u8), first: true }
  }
}

impl Iterator<Vec<u8>> for ByteRange {
  fn next(&mut self) -> Option<Vec<u8>> {
    // TODO make nicer
    if self.first {
      self.first = false;
      return Some(self.current.clone());
    }

    for i in range(0, self.current.len()).rev() {
      if self.current[i] == 255 {
        continue;
      }

      // Increase.
      self.current[i] += 1;

      // Zero all bytes right of the current index.
      self.current[mut i+1..].set_memory(0);

      return Some(self.current.clone());
    }

    None
  }
}

pub struct Candidates {
  range: ByteRange,
  state: Vec<u8>,
  row: uint
}

impl Iterator<(Vec<u8>, Vec<u8>)> for Candidates {
  fn next(&mut self) -> Option<(Vec<u8>, Vec<u8>)> {
    let next = self.range.next();

    // Bail out if we tried all possible combinations.
    if next.is_none() {
      return None;
    }

    // Set bytes for current candidate.
    let bytes = next.unwrap();
    copy_memory(self.state[mut 16..18], bytes[]);
    copy_memory(self.state[mut 32..34], bytes[]);

    // Compute the final compression value.
    let cmp = compress(self.state[], self.row);

    // Compute the original message leading to this state.
    let msg = decompress(self.state[], self.row);

    Some((cmp, msg))
  }
}

pub fn candidates(state: &[u8], row: uint) -> Candidates {
  // Test 2^16 combinations.
  Candidates { range: ByteRange::new(2), state: state.to_vec(), row: row }
}

pub fn prefill_row(num_rows: uint) -> Vec<u8> {
  let mut state = [[0u8, ..49], ..19];

  for row in range_inclusive(1, num_rows) {
    // Fill row of T1.
    for i in range(1, 17) {
      state[row][i] = SBOX[state[row][i - 1] as uint] ^ state[row - 1][i];
    }

    // Last bytes are equal.
    state[row][32] = state[row][16];
    state[row][48] = state[row][16];

    // Next t value.
    state[row + 1][0] = state[row][48] + (row as u8) - 1;
  }

  // Compute triangles.
  for col in range(0, num_rows) {
    for row in range_inclusive(2 + col, num_rows).rev() {
      // TODO make nicer
      let byte = SBOXI[(state[row][32 - col] ^ state[row - 1][32 - col]) as uint];
      state[row][32 - col - 1] = byte;
      state[row][48 - col - 1] = byte;
    }
  }

  // TODO explain
  state[num_rows][1..].to_vec()
}

fn compress(state: &[u8], iteration: uint) -> Vec<u8> {
  let mut t = state[47] + iteration as u8 - 1;
  let mut x = state.to_vec();

  // TODO explain
  for row in range(iteration, 18) {
    for byte in x.iter_mut() {
      *byte ^= SBOX[t as uint];
      t = *byte;
    }
    t += row as u8;
  }

  x[..16].to_vec()
}

fn decompress(state: &[u8], iteration: uint) -> Vec<u8> {
  let mut x = state.to_vec();

  // TODO explain
  for row in range(0, iteration).rev() {
    for col in range(1, 48).rev() {
      x[col] ^= SBOX[x[col - 1] as uint];
    }

    let t = x[47] + (row as u8) - 1;
    x[0] ^= SBOX[t as uint];
  }

  x[16..32].to_vec()
}

#[cfg(test)]
mod test {
  use candidates;
  use prefill_row;

  use md2::compress;
  use std::collections::HashMap;
  use std::collections::hash_map::Entry::{Occupied, Vacant};
  use std::sync::TaskPool;

  // Insert the given candidate pair, consisting of the compressed and the
  // original message, into the given hash map.
  fn insert(map: &mut HashMap<Vec<u8>,Vec<Vec<u8>>>, cmp: Vec<u8>, msg: Vec<u8>) {
    match map.entry(cmp) {
      Vacant(entry) => { entry.set(vec!(msg)); }
      Occupied(mut entry) => { entry.get_mut().push(msg); }
    }
  }

  // Validate all colliding entries in the given map to ensure that those
  // messages do indeed collide when computing their compressed values.
  fn validate(map: &HashMap<Vec<u8>,Vec<Vec<u8>>>) -> bool {
    let empty = [0u8, ..16];

    // Ignore compressed values with only a single message (no collisions).
    let collisions = map.iter().filter(|&(_, msgs)| msgs.len() > 1);

    collisions.all(|(cmp, msgs)| {
      msgs.iter().all(|msg| compress(&empty, msg[]) == *cmp)
    })
  }

  // Count the number of map entries that have more than a single message.
  // Those will compress to the same final value and thus represent collisions.
  fn count(map: &HashMap<Vec<u8>,Vec<Vec<u8>>>) -> uint {
    map.values().fold(0u, |count, msgs| count + msgs.len() - 1)
  }

  #[test]
  fn test_k2() {
    let state = prefill_row(14);

    // There will be ~2^16 entries (minus collisions).
    let mut map = HashMap::with_capacity(256u * 256u);

    // Iterate and record all candidate pairs.
    for (cmp, msg) in candidates(state[], 14) {
      insert(&mut map, cmp, msg);
    }

    assert!(validate(&map));
    assert_eq!(count(&map), 141);
  }

  #[test]
  fn test_k3() {
    let pool = TaskPool::new(8u);
    let (tx, rx) = channel();
    let state = prefill_row(13);

    for byte in range(0u, 256u) {
      let txc = tx.clone();
      let mut state = state.clone();

      pool.execute(move || {
        // TODO explain
        state[18] = byte as u8;
        state[34] = byte as u8;

        for candidate in candidates(state[], 13) {
          txc.send(candidate);
        }
      });
    }

    // There will be ~2^24 entries (minus collisions).
    let total = 256u * 256u * 256u;
    let mut map = HashMap::with_capacity(total);

    // Merge partial results.
    for (key, msg) in rx.iter().take(total) {
      insert(&mut map, key, msg);
    }

    assert!(validate(&map));
    assert_eq!(count(&map), 32784);
  }
}
<commit_msg>rework ByteRange where all bytes=255<commit_after>/* 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/. */

#![feature(slicing_syntax)]

extern crate "rust-md2" as md2;

use md2::{SBOX, SBOXI};
use std::iter::range_inclusive;
use std::slice::bytes::{copy_memory, MutableByteVector};

struct ByteRange {
  current: Vec<u8>,
  done: bool
}

impl ByteRange {
  fn new(num_bytes: uint) -> ByteRange {
    ByteRange { current: Vec::from_elem(num_bytes, 0u8), done: false }
  }
}

impl Iterator<Vec<u8>> for ByteRange {
  fn next(&mut self) -> Option<Vec<u8>> {
    for i in range(0, self.current.len()).rev() {
      if self.current[i] == 255 {
        continue;
      }

      let bytes = self.current.clone();

      // Increase.
      self.current[i] += 1;

      // Zero all bytes right of the current index.
      self.current[mut i+1..].set_memory(0);

      return Some(bytes);
    }

    // Need to special case where all bytes = 255.
    if self.done { None } else { self.done = true; Some(self.current.clone()) }
  }
}

pub struct Candidates {
  range: ByteRange,
  state: Vec<u8>,
  row: uint
}

impl Iterator<(Vec<u8>, Vec<u8>)> for Candidates {
  fn next(&mut self) -> Option<(Vec<u8>, Vec<u8>)> {
    let next = self.range.next();

    // Bail out if we tried all possible combinations.
    if next.is_none() {
      return None;
    }

    // Set bytes for current candidate.
    let bytes = next.unwrap();
    copy_memory(self.state[mut 16..18], bytes[]);
    copy_memory(self.state[mut 32..34], bytes[]);

    // Compute the final compression value.
    let cmp = compress(self.state[], self.row);

    // Compute the original message leading to this state.
    let msg = decompress(self.state[], self.row);

    Some((cmp, msg))
  }
}

pub fn candidates(state: &[u8], row: uint) -> Candidates {
  // Test 2^16 combinations.
  Candidates { range: ByteRange::new(2), state: state.to_vec(), row: row }
}

pub fn prefill_row(num_rows: uint) -> Vec<u8> {
  let mut state = [[0u8, ..49], ..19];

  for row in range_inclusive(1, num_rows) {
    // Fill row of T1.
    for i in range(1, 17) {
      state[row][i] = SBOX[state[row][i - 1] as uint] ^ state[row - 1][i];
    }

    // Last bytes are equal.
    state[row][32] = state[row][16];
    state[row][48] = state[row][16];

    // Next t value.
    state[row + 1][0] = state[row][48] + (row as u8) - 1;
  }

  // Compute triangles.
  for col in range(0, num_rows) {
    for row in range_inclusive(2 + col, num_rows).rev() {
      // TODO make nicer
      let byte = SBOXI[(state[row][32 - col] ^ state[row - 1][32 - col]) as uint];
      state[row][32 - col - 1] = byte;
      state[row][48 - col - 1] = byte;
    }
  }

  // TODO explain
  state[num_rows][1..].to_vec()
}

fn compress(state: &[u8], iteration: uint) -> Vec<u8> {
  let mut t = state[47] + iteration as u8 - 1;
  let mut x = state.to_vec();

  // TODO explain
  for row in range(iteration, 18) {
    for byte in x.iter_mut() {
      *byte ^= SBOX[t as uint];
      t = *byte;
    }
    t += row as u8;
  }

  x[..16].to_vec()
}

fn decompress(state: &[u8], iteration: uint) -> Vec<u8> {
  let mut x = state.to_vec();

  // TODO explain
  for row in range(0, iteration).rev() {
    for col in range(1, 48).rev() {
      x[col] ^= SBOX[x[col - 1] as uint];
    }

    let t = x[47] + (row as u8) - 1;
    x[0] ^= SBOX[t as uint];
  }

  x[16..32].to_vec()
}

#[cfg(test)]
mod test {
  use candidates;
  use prefill_row;

  use md2::compress;
  use std::collections::HashMap;
  use std::collections::hash_map::Entry::{Occupied, Vacant};
  use std::sync::TaskPool;

  // Insert the given candidate pair, consisting of the compressed and the
  // original message, into the given hash map.
  fn insert(map: &mut HashMap<Vec<u8>,Vec<Vec<u8>>>, cmp: Vec<u8>, msg: Vec<u8>) {
    match map.entry(cmp) {
      Vacant(entry) => { entry.set(vec!(msg)); }
      Occupied(mut entry) => { entry.get_mut().push(msg); }
    }
  }

  // Validate all colliding entries in the given map to ensure that those
  // messages do indeed collide when computing their compressed values.
  fn validate(map: &HashMap<Vec<u8>,Vec<Vec<u8>>>) -> bool {
    let empty = [0u8, ..16];

    // Ignore compressed values with only a single message (no collisions).
    let collisions = map.iter().filter(|&(_, msgs)| msgs.len() > 1);

    collisions.all(|(cmp, msgs)| {
      msgs.iter().all(|msg| compress(&empty, msg[]) == *cmp)
    })
  }

  // Count the number of map entries that have more than a single message.
  // Those will compress to the same final value and thus represent collisions.
  fn count(map: &HashMap<Vec<u8>,Vec<Vec<u8>>>) -> uint {
    map.values().fold(0u, |count, msgs| count + msgs.len() - 1)
  }

  #[test]
  fn test_k2() {
    let state = prefill_row(14);

    // There will be ~2^16 entries (minus collisions).
    let mut map = HashMap::with_capacity(256u * 256u);

    // Iterate and record all candidate pairs.
    for (cmp, msg) in candidates(state[], 14) {
      insert(&mut map, cmp, msg);
    }

    assert!(validate(&map));
    assert_eq!(count(&map), 141);
  }

  #[test]
  fn test_k3() {
    let pool = TaskPool::new(8u);
    let (tx, rx) = channel();
    let state = prefill_row(13);

    for byte in range(0u, 256u) {
      let txc = tx.clone();
      let mut state = state.clone();

      pool.execute(move || {
        // TODO explain
        state[18] = byte as u8;
        state[34] = byte as u8;

        for candidate in candidates(state[], 13) {
          txc.send(candidate);
        }
      });
    }

    // There will be ~2^24 entries (minus collisions).
    let total = 256u * 256u * 256u;
    let mut map = HashMap::with_capacity(total);

    // Merge partial results.
    for (key, msg) in rx.iter().take(total) {
      insert(&mut map, key, msg);
    }

    assert!(validate(&map));
    assert_eq!(count(&map), 32784);
  }
}
<|endoftext|>


<commit_before>use collections::string::ToString;
use core::ops::*;
use core::cmp::Ord;

use abs::Abs;
use approx::Approx;
use bounded::Bounded;
use clamp::Clamp;
use to_degrees::ToDegrees;
use to_radians::ToRadians;
use to_primitive::ToPrimitive;
use from_primitive::FromPrimitive;
use max::Max;
use min::Min;
use one::One;
use round::Round;
use signum::Signum;
use sqrt::Sqrt;
use trig::Trig;
use zero::Zero;

pub trait Num:
    Copy + One + Zero
    + Min + Max + Signum
    + Abs
    + Approx
    + Bounded
    + Clamp
    + ToDegrees
    + ToRadians
    + ToPrimitive
    + FromPrimitive
    + Trig
    + Sqrt
    + Round
    + Ord
    + PartialEq
    + PartialOrd
    + ToString

    + Add<Self, Output = Self>
    + Mul<Self, Output = Self>
    + Sub<Self, Output = Self>
    + Div<Self, Output = Self>
    + Rem<Self, Output = Self>

    + AddAssign<Self>
    + MulAssign<Self>
    + SubAssign<Self>
    + DivAssign<Self>
    + RemAssign<Self> {}

impl<T> Num for T where T:
    Copy + One + Zero
    + Min + Max + Signum
    + Abs
    + Approx
    + Bounded
    + Clamp
    + ToDegrees
    + ToRadians
    + ToPrimitive
    + FromPrimitive
    + Trig
    + Sqrt
    + Round
    + Ord
    + PartialEq
    + PartialOrd
    + ToString

    + Add<T, Output = T>
    + Mul<T, Output = T>
    + Sub<T, Output = T>
    + Div<T, Output = T>
    + Rem<T, Output = T>

    + AddAssign<T>
    + MulAssign<T>
    + SubAssign<T>
    + DivAssign<T>
    + RemAssign<T> {}
<commit_msg>clean up<commit_after>use collections::string::ToString;
use core::ops::*;

use abs::Abs;
use approx::Approx;
use bounded::Bounded;
use clamp::Clamp;
use to_degrees::ToDegrees;
use to_radians::ToRadians;
use to_primitive::ToPrimitive;
use from_primitive::FromPrimitive;
use max::Max;
use min::Min;
use one::One;
use round::Round;
use signum::Signum;
use sqrt::Sqrt;
use trig::Trig;
use zero::Zero;

pub trait Num:
    Copy + One + Zero
    + Min + Max + Signum
    + Abs
    + Approx
    + Bounded
    + Clamp
    + ToDegrees
    + ToRadians
    + ToPrimitive
    + FromPrimitive
    + Trig
    + Sqrt
    + Round
    + PartialEq
    + PartialOrd
    + ToString

    + Add<Self, Output = Self>
    + Mul<Self, Output = Self>
    + Sub<Self, Output = Self>
    + Div<Self, Output = Self>
    + Rem<Self, Output = Self>

    + AddAssign<Self>
    + MulAssign<Self>
    + SubAssign<Self>
    + DivAssign<Self>
    + RemAssign<Self> {}

impl<T> Num for T where T:
    Copy + One + Zero
    + Min + Max + Signum
    + Abs
    + Approx
    + Bounded
    + Clamp
    + ToDegrees
    + ToRadians
    + ToPrimitive
    + FromPrimitive
    + Trig
    + Sqrt
    + Round
    + PartialEq
    + PartialOrd
    + ToString

    + Add<T, Output = T>
    + Mul<T, Output = T>
    + Sub<T, Output = T>
    + Div<T, Output = T>
    + Rem<T, Output = T>

    + AddAssign<T>
    + MulAssign<T>
    + SubAssign<T>
    + DivAssign<T>
    + RemAssign<T> {}
<|endoftext|>


<commit_before>use std::collections::BTreeMap;
use ::prelude::*;
use ::base::*;
use ::game;

const FIELD_LENGTH: f32    = 9.010;
const CENTER_DIAMETER: f32 = 1.000;
const DEFENSE_RADIUS: f32  = 1.000;
const ROBOT_RADIUS: f32    = 0.090;
const ROBOT_MOUTH: f32     = 0.500;
const BALL_RADIUS: f32     = 0.023;
const BALL_FRICT_LOSS: f32 = 1.000;

#[derive(Clone, Debug, PartialEq)]
pub struct Ball {
    //pub pos: Vec3d,
    //pub vel: Vec3d,
    pub pos: Vec2d,
    pub vel: Vec2d,
}

#[derive(Clone, Debug, PartialEq)]
pub struct Robot {
    pub pos: Vec2d,
    pub vel: Vec2d,
    pub w: f32,
    pub vw: f32,
}

impl Robot {
    #[inline]
    pub fn new() -> Robot {
        Robot { pos: Vec2d(0.0, 0.0), w: 0.0, vel: Vec2d(0.0, 0.0), vw: 0.0 }
    }
}

#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct Id(pub Color, pub u8);

impl Id {
    pub fn color(self) -> Color { self.0 }
    pub fn id(self) -> u8 { self.1 }
}

#[derive(Clone, Debug, PartialEq)]
pub struct State {
    pub timestamp: f32,
    pub ball: Ball,
    pub robots: BTreeMap<Id, Robot>,
    pub team_side: TeamSide,
}

trait BTreeMapSimExt {
    fn initial_formation(&mut self, count: u8, color: Color, side: Side);
}

impl BTreeMapSimExt for BTreeMap<Id, Robot> {
    fn initial_formation(&mut self, count: u8, color: Color, side: Side) {
        use std::f32::consts::PI;

        let w_0 = if side.is_right() { 0.0 } else { PI };
        let w_delta = 2.0 * PI / (count as f32) * if side.is_right() { 1.0 } else { -1.0 };
        let x_offset = (CENTER_DIAMETER / 4.0 + FIELD_LENGTH / 4.0 - DEFENSE_RADIUS / 2.0) * if side.is_right() { 1.0 } else { -1.0 };
        //let radius = FIELD_LENGTH / 8.0;
        let radius = (FIELD_LENGTH / 2.0 - CENTER_DIAMETER / 2.0 - DEFENSE_RADIUS) / 3.0 - ROBOT_RADIUS;

        for i in 0..count {
            let robot_id = Id(color, i);
            let robot = self.entry(robot_id).or_insert(Robot::new());
            let w = w_0 + (i as f32) * w_delta;
            robot.pos.0 = radius * w.cos() + x_offset;
            robot.pos.1 = radius * w.sin();
            robot.w = w + PI;
        }
    }
}

impl State {
    pub fn new(team_side: TeamSide) -> State {
        let mut robots = BTreeMap::new();
        if team_side.yellow_is_left() {
            robots.initial_formation(6, Yellow, Left);
            robots.initial_formation(6, Blue, Right);
        } else {
            robots.initial_formation(6, Yellow, Right);
            robots.initial_formation(6, Blue, Left);
        }
        State {
            timestamp: 0.0,
            //ball: Ball { pos: Vec2d(0.0, 0.0), vel: Vec2d(0.0, 0.0) },
            ball: Ball { pos: Vec2d(0.0, 0.05), vel: Vec2d(-4.0, 0.0) },
            robots: robots,
            team_side: team_side,
        }
    }

    pub fn update_game(&self, game_state: &mut game::State) {
        // ball
        {
            let game_ball = game_state.get_ball_mut();
            game_ball.set_x(self.ball.pos.0);
            game_ball.set_y(self.ball.pos.1);
            //game_ball.set_z(self.ball.pos.z);
            game_ball.set_vx(self.ball.vel.0);
            game_ball.set_vy(self.ball.vel.1);
            //game_ball.set_vz(self.ball.vel.z);
        }
        // robots
        for (id, ref robot) in self.robots.iter() {
            let (robot_id, ref mut robots) = match id {
                &Id(Blue, i)   => (i, game_state.get_robots_blue_mut()),
                &Id(Yellow, i) => (i, game_state.get_robots_yellow_mut()),
            };
            let game_robot = robots.entry(robot_id).or_insert(game::Robot::new(robot_id));
            game_robot.set_x(robot.pos.0);
            game_robot.set_y(robot.pos.1);
            game_robot.set_w(robot.w);
            game_robot.set_vx(robot.vel.0);
            game_robot.set_vy(robot.vel.1);
            game_robot.set_vw(robot.vw);
        }
        game_state.inc_counter();
    }

    pub fn step(&mut self, command: game::Command, timestep: f32) {
        self.timestamp += timestep;
        let &mut State { ref mut ball, ref mut robots, .. } = self;
        let mut d_time_ball = timestep;
        let d_time = timestep;

        // XXX: overly simplified physics ahead
        for (robot_id, robot) in robots.iter_mut() {
            let robot_command = {
                if robot_id.color() == Color::yellow(command.is_yellow) {
                    command.robots.get(&robot_id.id())
                } else {
                    None
                }
            };

            if let Some(robot_command) = robot_command {
                let v_tangent = robot_command.v_tangent;
                let v_normal  = robot_command.v_normal;
                let v_angular = robot_command.v_angular;
                robot.vel = Vec2d(v_tangent, v_normal).rotate(-robot.w);
                robot.vw = v_angular;
            }

            // detect collision with ball
            let r = ROBOT_RADIUS + BALL_RADIUS;

            // Bhāskara:
            let a = (robot.vel - ball.vel).norm2();
            let b = 2.0 * ((robot.pos - ball.pos) * (robot.vel - ball.vel));
            let c = (robot.pos - ball.pos).norm2() - r * r;
            let delta = b * b - 4.0 * a * c;

            if delta >= 0.0 && a != 0.0 {
                let tc = (-b - delta.sqrt()) * 0.5 / a;
                if 0.0 <= tc && tc <= timestep {
                    use std::f32::consts::PI;

                    debug!("collision: ball and robot {:?}", robot_id);

                    ball.pos += ball.vel * tc;
                    let robot_pos = robot.pos + robot.vel * tc;
                    let cw = (ball.pos - robot_pos).angle();
                    let vel = ball.vel - robot.vel;
                    let vw = vel.angle() - PI;
                    ball.vel = robot.vel + Vec2d(vel.norm(), 0.0).rotate(2.0 * cw - vw);

                    let rw = robot.w + robot.vw * tc;
                    if rw.abs_sub(cw) < ROBOT_MOUTH {
                        if let Some(robot_command) = robot_command {
                            use ::game::RobotAction::*;
                            if let Kick(force) = robot_command.action {
                                ball.vel += Vec2d(force, 0.0).rotate(rw);
                            }
                            if let ChipKick(force) = robot_command.action {
                                // TODO: z force
                                ball.vel += Vec2d(force, 0.0).rotate(rw);
                            }
                            // TODO: dribble effect
                        }
                    }

                    d_time_ball -= tc;
                }
            }

            robot.pos += robot.vel * d_time;
            robot.w   += robot.vw  * d_time;

            // TODO: effect of robot_command.action
        }

        // update ball pos
        ball.pos += ball.vel * d_time_ball ;
        ball.vel *= 1.0 - BALL_FRICT_LOSS * timestep;
    }
}
<commit_msg>Draft for Referee sturctures, and last robot to touch the ball.<commit_after>use std::collections::BTreeMap;
use ::prelude::*;
use ::base::*;
use ::game;

const FIELD_LENGTH: f32    = 9.010;
const CENTER_DIAMETER: f32 = 1.000;
const DEFENSE_RADIUS: f32  = 1.000;
const ROBOT_RADIUS: f32    = 0.090;
const ROBOT_MOUTH: f32     = 0.500;
const BALL_RADIUS: f32     = 0.023;
const BALL_FRICT_LOSS: f32 = 1.000;

#[derive(Clone, Debug, PartialEq)]
pub struct Ball {
    //pub pos: Vec3d,
    //pub vel: Vec3d,
    pub pos: Vec2d,
    pub vel: Vec2d,
}

#[derive(Clone, Debug, PartialEq)]
pub struct Robot {
    pub pos: Vec2d,
    pub vel: Vec2d,
    pub w: f32,
    pub vw: f32,
}

impl Robot {
    #[inline]
    pub fn new() -> Robot {
        Robot { pos: Vec2d(0.0, 0.0), w: 0.0, vel: Vec2d(0.0, 0.0), vw: 0.0 }
    }
}

#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct Id(pub Color, pub u8);

impl Id {
    pub fn color(self) -> Color { self.0 }
    pub fn id(self) -> u8 { self.1 }
}

#[derive(Clone, Debug, PartialEq)]
pub enum Referee {
    Halt,
    Stop,
    Normal,
    PreKickoff(Color),
    Kickoff(Color),
    PrePenalty(Color),
    Penalty(Color),
    DirectFree(Color),
    IndirectFree(Color),
    Timeout(Color),
    Goal(Color),
}

#[derive(Clone, Debug, PartialEq)]
pub enum RefereeState {
    Idle,
    PlacingBall(f32),
}

#[derive(Clone, Debug, PartialEq)]
pub struct TeamInfo {
    name: String,
    score: u8,
    goalie: u8,
    //yellow_card_times: BTreeMap<u8, f32>,
    //yellow_cards: BTreeMap<u8, u8>,
    //red_cards: BTreeMap<u8, u8>,
}

impl Default for TeamInfo {
    fn default() -> TeamInfo {
        TeamInfo {
            name: "".to_string(),
            score: 0,
            goalie: 0,
            //yellow_card_times: BTreeMap::new(),
            //yellow_cards: BTreeMap::new(),
            //red_cards: BTreeMap::new(),
        }
    }
}

trait BTreeMapSimExt {
    fn initial_formation(&mut self, count: u8, color: Color, side: Side);
}

impl BTreeMapSimExt for BTreeMap<Id, Robot> {
    fn initial_formation(&mut self, count: u8, color: Color, side: Side) {
        use std::f32::consts::PI;

        let w_0 = if side.is_right() { 0.0 } else { PI };
        let w_delta = 2.0 * PI / (count as f32) * if side.is_right() { 1.0 } else { -1.0 };
        let x_offset = (CENTER_DIAMETER / 4.0 + FIELD_LENGTH / 4.0 - DEFENSE_RADIUS / 2.0) * if side.is_right() { 1.0 } else { -1.0 };
        //let radius = FIELD_LENGTH / 8.0;
        let radius = (FIELD_LENGTH / 2.0 - CENTER_DIAMETER / 2.0 - DEFENSE_RADIUS) / 3.0 - ROBOT_RADIUS;

        for i in 0..count {
            let robot_id = Id(color, i);
            let robot = self.entry(robot_id).or_insert(Robot::new());
            let w = w_0 + (i as f32) * w_delta;
            robot.pos.0 = radius * w.cos() + x_offset;
            robot.pos.1 = radius * w.sin();
            robot.w = w + PI;
        }
    }
}

#[derive(Clone, Debug, PartialEq)]
pub struct State {
    pub timestamp: f32,
    pub ball: Ball,
    pub robots: BTreeMap<Id, Robot>,
    pub last_ball_touch: Option<Id>,
    pub team_side: TeamSide,
    pub referee: Referee,
    pub referee_state: RefereeState,
    pub info_yellow: TeamInfo,
    pub info_blue: TeamInfo,
}

impl State {
    pub fn new(team_side: TeamSide) -> State {
        let mut robots = BTreeMap::new();
        if team_side.yellow_is_left() {
            robots.initial_formation(6, Yellow, Left);
            robots.initial_formation(6, Blue, Right);
        } else {
            robots.initial_formation(6, Yellow, Right);
            robots.initial_formation(6, Blue, Left);
        }
        State {
            timestamp: 0.0,
            //ball: Ball { pos: Vec2d(0.0, 0.0), vel: Vec2d(0.0, 0.0) },
            ball: Ball { pos: Vec2d(0.0, 0.05), vel: Vec2d(-4.0, 0.0) },
            robots: robots,
            last_ball_touch: None,
            team_side: team_side,
            referee: Referee::Normal,
            referee_state: RefereeState::Idle,
            info_yellow: Default::default(),
            info_blue: Default::default(),
        }
    }

    pub fn update_game(&self, game_state: &mut game::State) {
        // ball
        {
            let game_ball = game_state.get_ball_mut();
            game_ball.set_x(self.ball.pos.0);
            game_ball.set_y(self.ball.pos.1);
            //game_ball.set_z(self.ball.pos.z);
            game_ball.set_vx(self.ball.vel.0);
            game_ball.set_vy(self.ball.vel.1);
            //game_ball.set_vz(self.ball.vel.z);
        }
        // robots
        for (id, ref robot) in self.robots.iter() {
            let (robot_id, ref mut robots) = match id {
                &Id(Blue, i)   => (i, game_state.get_robots_blue_mut()),
                &Id(Yellow, i) => (i, game_state.get_robots_yellow_mut()),
            };
            let game_robot = robots.entry(robot_id).or_insert(game::Robot::new(robot_id));
            game_robot.set_x(robot.pos.0);
            game_robot.set_y(robot.pos.1);
            game_robot.set_w(robot.w);
            game_robot.set_vx(robot.vel.0);
            game_robot.set_vy(robot.vel.1);
            game_robot.set_vw(robot.vw);
        }
        game_state.inc_counter();
    }

    pub fn step(&mut self, command: game::Command, timestep: f32) {
        self.timestamp += timestep;
        let &mut State {
            ref mut ball,
            ref mut robots,
            ref mut last_ball_touch,
            ..
        } = self;
        let mut d_time_ball = timestep;
        let d_time = timestep;

        // XXX: overly simplified physics ahead
        for (robot_id, robot) in robots.iter_mut() {
            let robot_command = {
                if robot_id.color() == Color::yellow(command.is_yellow) {
                    command.robots.get(&robot_id.id())
                } else {
                    None
                }
            };

            if let Some(robot_command) = robot_command {
                let v_tangent = robot_command.v_tangent;
                let v_normal  = robot_command.v_normal;
                let v_angular = robot_command.v_angular;
                robot.vel = Vec2d(v_tangent, v_normal).rotate(-robot.w);
                robot.vw = v_angular;
            }

            // detect collision with ball
            let r = ROBOT_RADIUS + BALL_RADIUS;

            // Bhāskara:
            let a = (robot.vel - ball.vel).norm2();
            let b = 2.0 * ((robot.pos - ball.pos) * (robot.vel - ball.vel));
            let c = (robot.pos - ball.pos).norm2() - r * r;
            let delta = b * b - 4.0 * a * c;

            if delta >= 0.0 && a != 0.0 {
                let tc = (-b - delta.sqrt()) * 0.5 / a;
                if 0.0 <= tc && tc <= timestep {
                    use std::f32::consts::PI;

                    debug!("collision: ball and robot {:?}", robot_id);
                    *last_ball_touch = Some(*robot_id);

                    ball.pos += ball.vel * tc;
                    let robot_pos = robot.pos + robot.vel * tc;
                    let cw = (ball.pos - robot_pos).angle();
                    let vel = ball.vel - robot.vel;
                    let vw = vel.angle() - PI;
                    ball.vel = robot.vel + Vec2d(vel.norm(), 0.0).rotate(2.0 * cw - vw);

                    let rw = robot.w + robot.vw * tc;
                    if rw.abs_sub(cw) < ROBOT_MOUTH {
                        if let Some(robot_command) = robot_command {
                            use ::game::RobotAction::*;
                            if let Kick(force) = robot_command.action {
                                ball.vel += Vec2d(force, 0.0).rotate(rw);
                            }
                            if let ChipKick(force) = robot_command.action {
                                // TODO: z force
                                ball.vel += Vec2d(force, 0.0).rotate(rw);
                            }
                            // TODO: dribble effect
                        }
                    }

                    d_time_ball -= tc;
                }
            }

            robot.pos += robot.vel * d_time;
            robot.w   += robot.vw  * d_time;

            // TODO: effect of robot_command.action
        }

        // update ball pos
        ball.pos += ball.vel * d_time_ball ;
        ball.vel *= 1.0 - BALL_FRICT_LOSS * timestep;
    }
}
<|endoftext|>


<commit_before>use crate::prelude::*;

impl Uuid {
    /// Creates a random UUID.
    ///
    /// This uses the [`getrandom`] crate to utilise the operating system's RNG
    /// as the source of randomnumbers. If you'd like to use a custom generator,
    /// don't use this method: use the `rand::Rand trait`'s `rand()` method instead.
    ///
    /// Note that usage of this method requires the `v4` feature of this crate
    /// to be enabled.
    ///
    /// # Examples
    ///
    /// Basic usage:
    ///
    /// ```
    /// use uuid::Uuid;
    ///
    /// let uuid = Uuid::new_v4();
    /// ```
    ///
    /// [`getrandom`]: https://crates.io/crates/getrandom
    /// [`rand`]: https://crates.io/crates/rand
    pub fn new_v4() -> Result<Uuid, getrandom::Error> {
        let mut bytes = [0u8; 16];
        getrandom::getrandom(&mut bytes)?;

        let uuid = crate::builder::Builder::from_bytes(bytes)
            .set_variant(Variant::RFC4122)
            .set_version(Version::Random)
            .build();
        Ok(uuid)
    }
}

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

    #[test]
    fn test_new() {
        let uuid = Uuid::new_v4().unwrap();

        assert_eq!(uuid.get_version(), Some(Version::Random));
        assert_eq!(uuid.get_variant(), Some(Variant::RFC4122));
    }

    #[test]
    fn test_get_version() {
        let uuid = Uuid::new_v4().unwrap();

        assert_eq!(uuid.get_version(), Some(Version::Random));
        assert_eq!(uuid.get_version_num(), 4)
    }
}
<commit_msg>Update src/v4.rs<commit_after>use crate::prelude::*;

impl Uuid {
    /// Creates a random UUID.
    ///
    /// This uses the [`getrandom`] crate to utilise the operating system's RNG
    /// as the source of random numbers. If you'd like to use a custom generator,
    /// don't use this method: use the `rand::Rand trait`'s `rand()` method instead.
    ///
    /// Note that usage of this method requires the `v4` feature of this crate
    /// to be enabled.
    ///
    /// # Examples
    ///
    /// Basic usage:
    ///
    /// ```
    /// use uuid::Uuid;
    ///
    /// let uuid = Uuid::new_v4();
    /// ```
    ///
    /// [`getrandom`]: https://crates.io/crates/getrandom
    /// [`rand`]: https://crates.io/crates/rand
    pub fn new_v4() -> Result<Uuid, getrandom::Error> {
        let mut bytes = [0u8; 16];
        getrandom::getrandom(&mut bytes)?;

        let uuid = crate::builder::Builder::from_bytes(bytes)
            .set_variant(Variant::RFC4122)
            .set_version(Version::Random)
            .build();
        Ok(uuid)
    }
}

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

    #[test]
    fn test_new() {
        let uuid = Uuid::new_v4().unwrap();

        assert_eq!(uuid.get_version(), Some(Version::Random));
        assert_eq!(uuid.get_variant(), Some(Variant::RFC4122));
    }

    #[test]
    fn test_get_version() {
        let uuid = Uuid::new_v4().unwrap();

        assert_eq!(uuid.get_version(), Some(Version::Random));
        assert_eq!(uuid.get_version_num(), 4)
    }
}
<|endoftext|>


<commit_before>//! The Lea Virtual Machine

use mem::GcStrategy;

/// A VM context. Holds a garbage collector that manages the program's memory, the stack used for
/// local and temporary variables, the callstack, etc.
pub struct VM<G: GcStrategy> {
    gc: G,
}

impl <G: GcStrategy + Default> Default for VM<G> {
    fn default() -> VM<G> {
        VM {
            gc: Default::default(),
        }
    }
}

impl <G: GcStrategy> VM<G> {
    pub fn new(gc: G) -> VM<G> {
        VM {
            gc: gc,
        }
    }
}
<commit_msg>vm: add gc() method to access GC<commit_after>//! The Lea Virtual Machine

use mem::GcStrategy;

/// A VM context. Holds a garbage collector that manages the program's memory, the stack used for
/// local and temporary variables, the callstack, etc.
pub struct VM<G: GcStrategy> {
    gc: G,
}

impl <G: GcStrategy + Default> Default for VM<G> {
    fn default() -> VM<G> {
        VM {
            gc: Default::default(),
        }
    }
}

impl <G: GcStrategy> VM<G> {
    pub fn new(gc: G) -> VM<G> {
        VM {
            gc: gc,
        }
    }

    pub fn gc(&self) -> &G {
        &self.gc
    }
}
<|endoftext|>


<commit_before>/* diosix capsule management
 *
 * (c) Chris Williams, 2019-2020.
 *
 * See LICENSE for usage and copying.
 */

use spin::Mutex;
use hashbrown::hash_map::HashMap;
use hashbrown::hash_map::Entry::{Occupied, Vacant};
use hashbrown::hash_set::HashSet;
use alloc::vec::Vec;
use alloc::string::String;
use platform::cpu::Entry;
use platform::physmem::PhysMemBase;
use super::error::Cause;
use super::physmem;
use super::virtmem::Mapping;
use super::vcore::{self, Priority, VirtualCoreID};
use super::service::ServiceID;
use super::service;
use super::pcore;

pub type CapsuleID = usize;

/* needed to assign system-wide unique capsule ID numbers */
lazy_static!
{
    static ref CAPSULE_ID_NEXT: Mutex<CapsuleID> = Mutex::new(0);
}

/* maintain a shared table of capsules */
lazy_static!
{
    /* acquire CAPSULES lock before accessing any capsules */
    static ref CAPSULES: Mutex<HashMap<CapsuleID, Capsule>> = Mutex::new(HashMap::new());
}

struct Capsule
{
    restart: bool,                          /* true to auto-restart on death */
    vcores: HashSet<VirtualCoreID>,         /* set of virtual core IDs assigned to this capsule */
    memory: Vec<Mapping>,                   /* map capsule supervisor virtual addresses to host physical addresses */
    allowed_services: HashSet<ServiceID>,   /* set of services this capsule is allowed to provide */
    debug_buffer: String                    /* buffer to hold debug output until it's flushed */
}

impl Capsule
{
    /* create a new empty capsule using the current capsule on this physical CPU core.
    => auto_restart_flag = tue to auto-restart on death by the hypervisor
    <= capsule object, or error code */
    pub fn new(auto_restart_flag: bool) -> Result<Capsule, Cause>
    {
        Ok(Capsule
        {
            restart: auto_restart_flag,
            vcores: HashSet::new(),
            memory: Vec::new(),
            allowed_services: HashSet::new(),
            debug_buffer: String::new()
        })
    }

    /* add a mapping to this capsule */
    pub fn set_memory_mapping(&mut self, to_add: Mapping)
    {
        self.memory.push(to_add);
    }

    /* get a copy of the capsule's memory mappings */
    pub fn get_memory_mappings(&self) -> Vec<Mapping> { self.memory.clone() }

    /* boot capsules are auto-restarted by the hypervisor */
    pub fn set_auto_restart(&mut self, flag: bool)
    {
        self.restart = flag;
    }

    /* returns true if this will auto-restart, or false if not */
    pub fn will_auto_restart(&self) -> bool
    {
        self.restart
    }

    /* add a virtual core ID to the capsule */
    pub fn add_vcore(&mut self, id: VirtualCoreID)
    {
        self.vcores.insert(id);
    }

    /* remove a virtual core ID from the capsule. returns true if that
       ID was present in the registered list */
    pub fn remove_vcore(&mut self, id: VirtualCoreID) -> bool
    {
        self.vcores.remove(&id)
    }

    /* return number of registered virtual cores */
    pub fn count_vcores(&self) -> usize
    {
        self.vcores.len()
    }

    /* allow capsule to register service sid */
    pub fn allow_service(&mut self, sid: ServiceID)
    {
        self.allowed_services.insert(sid);
    }

    /* check whether this capsule is allowed to register the given service
        <= true if allowed, false if not */
    pub fn check_service(&self, sid: ServiceID) -> bool
    {
        self.allowed_services.contains(&sid)
    }
}

/* handle the destruction of a capsule */
impl Drop for Capsule
{
    fn drop(&mut self)
    {
        hvdebug!("Tearing down capsule {:p}", &self);

        /* free up memory... */
        for mapping in self.memory.clone()
        {
            if let Some(r) = mapping.get_physical()
            {
                match physmem::dealloc_region(r)
                {
                    Err(e) => hvalert!("Error during capsule {:p} teardown: {:?}", &self, e),
                    Ok(_) => ()
                };
            }
        }

        /* ...and services held by this capsule */
        for sid in self.allowed_services.iter()
        {
            match service::deregister(*sid)
            {
                Err(e) => hvalert!("Failed to deregister service during teardown of capsule {:p}: {:?}", &self, e),
                Ok(()) => ()
            };
        }
    }
}

/* create a virtual core and add it to the given capsule
   => cid = capsule ID
      vid = virtual core ID
      entry = starting address for execution of this virtual core
      dtb = physical address of the device tree blob describing
            the virtual hardware environment
      prio = priority to run this virtual core
   <= return Ok for success, or error code
*/
pub fn create_and_add_vcore(cid: CapsuleID, vid: VirtualCoreID, entry: Entry, dtb: PhysMemBase, prio: Priority) -> Result<(), Cause>
{
    vcore::VirtualCore::create(cid, vid, entry, dtb, prio)?;
    match CAPSULES.lock().get_mut(&cid)
    {
        Some(c) => c.add_vcore(vid),
        None => return Err(Cause::CapsuleBadID)
    };
    Ok(())
}

/* create a new blank capsule
   Once created, it needs to be given a supervisor image, at least.
   then it is ready to be scheduled by assigning it virtual CPU cores.
   => auto_restart = true to be auto-restarted by hypervisor
   <= CapsuleID for this new capsule, or an error code */
pub fn create(auto_restart: bool) -> Result<CapsuleID, Cause>
{
    let new_capsule = Capsule::new(auto_restart)?;

    /* assign a new ID (in the unlikely event the given ID is already in-use, try again) */
    let mut overflowed_already = false;
    loop
    {
        let mut id = CAPSULE_ID_NEXT.lock();
        let (new_id, overflow) = id.overflowing_add(1);
        *id = new_id;

        /* check to see if this capsule already exists */
        let mut capsules = CAPSULES.lock();
        match capsules.entry(new_id)
        {
            Vacant(_) =>
            {
                /* insert our new capsule */
                capsules.insert(new_id, new_capsule);

                /* we're all done here */
                return Ok(new_id);
            },
            _ => () /* try again */
        };

        /* make sure we don't loop around forever hunting for a valid ID */
        if overflow == true
        {
            /* has this overflow happened before? */
            if overflowed_already == true
            {
                /* not the first time we overflowed, so give up */
                return Err(Cause::CapsuleIDExhaustion);
            }

            overflowed_already = true;
        }
    }
}

/* mark a capsule as dying, meaning its virtual cores will be
   gradually removed and when there are none left, its RAM
   and any other resources will be deallocated.
   => cid = ID of capsule to kill
   <= Ok for success, or an error code
*/
pub fn destroy(cid: CapsuleID) -> Result<(), Cause>
{
    if let Some(victim) = CAPSULES.lock().remove(&cid)
    {
        drop(victim); // see above implementation of drop for Capsule
        Ok(())
    }
    else
    {
        Err(Cause::CapsuleBadID)
    }
}

/* destroy() the currently running capsule */
pub fn destroy_current() -> Result<(), Cause>
{
    if let Some(c) = pcore::PhysicalCore::get_capsule_id()
    {
        if let Err(e) = destroy(c)
        {
            hvalert!("BUG: Could not kill currently running capsule ID {} ({:?})", c, e);
        }

        return Ok(())
    }

    Err(Cause::CapsuleBadID)
}

/* buffer a character from the guest kernel, and flush output if a newline */
pub fn debug_write(cid: CapsuleID, character: char) -> Result<(), Cause>
{
    match CAPSULES.lock().entry(cid)
    {
        Occupied(mut capsule) =>
        {
            let c = capsule.get_mut();

            if character != '\n'
            {
                /* buffer the character if we're not at a newline */
                c.debug_buffer.push(character);
            }
            else
            {
                /* flush the buffer and reinitialize it */
                hvdebug!("Capsule {}: {}", cid, c.debug_buffer);
                c.debug_buffer = String::new();
            }
            Ok(())
        },
        Vacant(_) => Err(Cause::CapsuleBadID)
    }
}

/* allow a given capsule to offer a given service. if the capsule was already allowed the
   service then this returns without error.
    => cid = capsule ID
       sid = service ID
    <= Ok for success, or an error code */
pub fn allow_service(cid: CapsuleID, sid: ServiceID) -> Result<(), Cause>
{
    match CAPSULES.lock().entry(cid)
    {
        Occupied(mut c) =>
        {
            c.get_mut().allowed_services.insert(sid);
            Ok(())
        },
        Vacant(_) => Err(Cause::CapsuleBadID)
    }
}

/* check whether a capsule is allowed to run the given service
    => cid = capsule ID to check
       sid = service ID to check
    <= Some true if the capsule exists and is allowed, or Some false
        if the capsule exists and is not allowed, or None if
        the capsule doesn't exist */
pub fn is_service_allowed(cid: CapsuleID, sid: ServiceID) -> Option<bool>
{
    match CAPSULES.lock().entry(cid)
    {
        Occupied(c) => Some(c.get().check_service(sid)),
        Vacant(_) => None
    }
}

/* add a memory mapping to a capsule
   cid = ID of capsule to add the mapping to
   to_map = memory mapping object to add
   Ok for success, or an error code
*/
pub fn map_memory(cid: CapsuleID, to_map: Mapping) -> Result<(), Cause>
{
    if let Occupied(mut c) = CAPSULES.lock().entry(cid)
    {
        c.get_mut().set_memory_mapping(to_map);
        return Ok(());
    }
    else
    {
        return Err(Cause::CapsuleBadID);
    }
}

/* enforce hardware security restrictions for the given capsule.
   supervisor-level code will only be able to access the physical
   RAM covered by that assigned to the given capsule. call this
   when switching to a capsule, so that the previous enforcement is
   replaced by enforcement of this capsule.
   => id = capsule to enforce
   <= true for success, or fail for failure
*/
pub fn enforce(id: CapsuleID) -> bool
{
    /* this is a filthy hardcode hack that I hate but it's needed for now */
    let mut index = 0;

    match CAPSULES.lock().entry(id)
    {
        Occupied(c) =>
        {
            for mapping in c.get().get_memory_mappings()
            {
                if let Some(r) = mapping.get_physical()
                {
                    if index > 0
                    {
                        panic!("TODO / FIXME: Capsules can't have more than one physical RAM region");
                    }

                    r.grant_access();
                    index = index + 1;
                }
            }
            return true
        },
        _ => false
    }
}
<commit_msg>Support restarting of capsules<commit_after>/* diosix capsule management
 *
 * (c) Chris Williams, 2019-2021.
 *
 * See LICENSE for usage and copying.
 */

use core::sync::atomic::{AtomicUsize, Ordering};
use spin::Mutex;
use hashbrown::hash_map::HashMap;
use hashbrown::hash_map::Entry::{Occupied, Vacant};
use hashbrown::hash_set::HashSet;
use alloc::vec::Vec;
use alloc::string::String;
use platform::cpu::Entry;
use platform::physmem::PhysMemBase;
use super::error::Cause;
use super::physmem;
use super::virtmem::Mapping;
use super::vcore::{self, Priority, VirtualCoreID};
use super::service::ServiceID;
use super::service;
use super::pcore;
use super::manifest;

pub type CapsuleID = usize;

/* arbitrarily allow up to CAPSULES_MAX capsules in the system */
const CAPSULES_MAX: usize = 1000000;

/* needed to assign system-wide unique capsule ID numbers */
lazy_static!
{
    static ref CAPSULE_ID_NEXT: AtomicUsize = AtomicUsize::new(0);
}

/* maintain a shared table of capsules */
lazy_static!
{
    /* acquire CAPSULES lock before accessing any capsules */
    static ref CAPSULES: Mutex<HashMap<CapsuleID, Capsule>> = Mutex::new(HashMap::new());
}

/* define who or what is responsible for restarting this capsule */
#[derive(Debug)]
pub enum RestartMethod
{
    FromManifest(String),   /* reload from the included manifest fs image */
    FromSpawnService        /* tell the capsule spawning service to reload us */
}

struct Capsule
{
    restart_on_crash: bool,                 /* true to auto-restart on unexpected death */
    restart_method: RestartMethod,          /* how to restart this capsule */
    vcores: HashSet<VirtualCoreID>,         /* set of virtual core IDs assigned to this capsule */
    memory: Vec<Mapping>,                   /* map capsule supervisor virtual addresses to host physical addresses */
    allowed_services: HashSet<ServiceID>,   /* set of services this capsule is allowed to provide */
    debug_buffer: String                    /* buffer to hold debug output until it's flushed */
}

impl Capsule
{
    /* create a new empty capsule using the current capsule on this physical CPU core.
    => auto_restart_flag = tue to auto-restart on crash by the hypervisor
    <= capsule object, or error code */
    pub fn new(restart_on_crash: bool, restart_method: RestartMethod) -> Result<Capsule, Cause>
    {
        Ok(Capsule
        {
            restart_on_crash,
            restart_method,
            vcores: HashSet::new(),
            memory: Vec::new(),
            allowed_services: HashSet::new(),
            debug_buffer: String::new()
        })
    }

    /* add a mapping to this capsule */
    pub fn set_memory_mapping(&mut self, to_add: Mapping)
    {
        self.memory.push(to_add);
    }

    /* get a copy of the capsule's memory mappings */
    pub fn get_memory_mappings(&self) -> Vec<Mapping> { self.memory.clone() }

    /* boot capsules are auto-restarted by the hypervisor */
    pub fn set_auto_crash_restart(&mut self, flag: bool)
    {
        self.restart_on_crash = flag;
    }

    /* returns true if this will auto-restart, or false if not */
    pub fn will_auto_crash_restart(&self) -> bool
    {
        self.restart_on_crash
    }

    /* returns restart method */
    pub fn get_restart_method(&self) -> &RestartMethod { &self.restart_method }

    /* add a virtual core ID to the capsule */
    pub fn add_vcore(&mut self, id: VirtualCoreID)
    {
        self.vcores.insert(id);
    }

    /* remove a virtual core ID from the capsule. returns true if that
       ID was present in the registered list */
    pub fn remove_vcore(&mut self, id: VirtualCoreID) -> bool
    {
        self.vcores.remove(&id)
    }

    /* return number of registered virtual cores */
    pub fn count_vcores(&self) -> usize
    {
        self.vcores.len()
    }

    /* allow capsule to register service sid */
    pub fn allow_service(&mut self, sid: ServiceID)
    {
        self.allowed_services.insert(sid);
    }

    /* check whether this capsule is allowed to register the given service
        <= true if allowed, false if not */
    pub fn check_service(&self, sid: ServiceID) -> bool
    {
        self.allowed_services.contains(&sid)
    }
}

/* handle the destruction of a capsule */
impl Drop for Capsule
{
    fn drop(&mut self)
    {
        hvdebug!("Tearing down capsule {:p}", &self);

        /* free up memory... */
        for mapping in self.memory.clone()
        {
            if let Some(r) = mapping.get_physical()
            {
                match physmem::dealloc_region(r)
                {
                    Err(e) => hvalert!("Error during capsule {:p} teardown: {:?}", &self, e),
                    Ok(_) => ()
                };
            }
        }

        /* ...and services held by this capsule */
        for sid in self.allowed_services.iter()
        {
            match service::deregister(*sid)
            {
                Err(e) => hvalert!("Failed to deregister service during teardown of capsule {:p}: {:?}", &self, e),
                Ok(()) => ()
            };
        }
    }
}

/* create a virtual core and add it to the given capsule
   => cid = capsule ID
      vid = virtual core ID
      entry = starting address for execution of this virtual core
      dtb = physical address of the device tree blob describing
            the virtual hardware environment
      prio = priority to run this virtual core
   <= return Ok for success, or error code
*/
pub fn add_vcore(cid: CapsuleID, vid: VirtualCoreID, entry: Entry, dtb: PhysMemBase, prio: Priority) -> Result<(), Cause>
{
    vcore::VirtualCore::create(cid, vid, entry, dtb, prio)?;
    match CAPSULES.lock().get_mut(&cid)
    {
        Some(c) => c.add_vcore(vid),
        None => return Err(Cause::CapsuleBadID)
    };
    Ok(())
}

/* create a new blank capsule
   Once created, it needs to be given a supervisor image, at least.
   then it is ready to be scheduled by assigning it virtual CPU cores.
   => auto_crash_restart = true to be auto-restarted by hypervisor if the capsule crashes
   <= CapsuleID for this new capsule, or an error code */
pub fn create(auto_crash_restart: bool, restart_method: RestartMethod) -> Result<CapsuleID, Cause>
{
    /* repeatedly try to generate an available ID */
    loop
    {
        /* bail out if we're at the limit */
        let mut capsules = CAPSULES.lock();
        if capsules.len() > CAPSULES_MAX
        {
            return Err(Cause::CapsuleIDExhaustion);
        }

        /* get next ID and check to see if this capsule already exists */
        let new_id = CAPSULE_ID_NEXT.fetch_add(1, Ordering::SeqCst);
        match capsules.entry(new_id)
        {
            Vacant(_) =>
            {
                /* insert our new capsule */
                capsules.insert(new_id, Capsule::new(auto_crash_restart, restart_method)?);

                /* we're all done here */
                return Ok(new_id);
            },
            _ => () /* try again */
        };
    }
}

/* mark a capsule as dying, meaning its virtual cores will be
   gradually removed and when there are none left, its RAM
   and any other resources will be deallocated.
   => cid = ID of capsule to kill
   <= Ok for success, or an error code
*/
pub fn destroy(cid: CapsuleID) -> Result<(), Cause>
{
    if let Some(victim) = CAPSULES.lock().remove(&cid)
    {
        drop(victim); // see above implementation of drop for Capsule
        Ok(())
    }
    else
    {
        Err(Cause::CapsuleBadID)
    }
}

/* mark a capsule as not only dying, but also recreate it.
   => cid = ID of capsule to restart
   <= Ok for success, or an error code
*/
pub fn restart(cid: CapsuleID) -> Result<(), Cause>
{
    let mut lock = CAPSULES.lock();
    if let Some(victim) = lock.remove(&cid)
    {
        match victim.get_restart_method()
        {
            RestartMethod::FromManifest(s) => match manifest::get_named_asset(&s.as_str())
            {
                Ok(a) =>
                {
                    drop(victim);
                    drop(lock);
                    manifest::load_asset(a)?;
                },
                Err(e) => return Err(e)
            },
            method => hvdebug!("Cannot restart capsule ID {} with method {:?}", cid, method)
        }
    }
    else
    {
        return Err(Cause::CapsuleBadID)
    }

    return Ok(());
}

/* destroy the currently running capsule */
pub fn destroy_current() -> Result<(), Cause>
{
    if let Some(c) = pcore::PhysicalCore::get_capsule_id()
    {
        if let Err(e) = destroy(c)
        {
            hvalert!("BUG: Could not kill currently running capsule ID {} ({:?})", c, e);
        }

        return Ok(())
    }

    Err(Cause::CapsuleBadID)
}

/* restart the currently running capsule */
pub fn restart_current() -> Result<(), Cause>
{
    if let Some(c) = pcore::PhysicalCore::get_capsule_id()
    {
        if let Err(e) = restart(c)
        {
            hvalert!("BUG: Could not restart currently running capsule ID {} ({:?})", c, e);
        }

        return Ok(())
    }

    Err(Cause::CapsuleBadID)
}

/* buffer a character from the guest kernel, and flush output if a newline */
pub fn debug_write(cid: CapsuleID, character: char) -> Result<(), Cause>
{
    match CAPSULES.lock().entry(cid)
    {
        Occupied(mut capsule) =>
        {
            let c = capsule.get_mut();

            if character != '\n'
            {
                /* buffer the character if we're not at a newline */
                c.debug_buffer.push(character);
            }
            else
            {
                /* flush the buffer and reinitialize it */
                hvdebug!("Capsule {}: {}", cid, c.debug_buffer);
                c.debug_buffer = String::new();
            }
            Ok(())
        },
        Vacant(_) => Err(Cause::CapsuleBadID)
    }
}

/* allow a given capsule to offer a given service. if the capsule was already allowed the
   service then this returns without error.
    => cid = capsule ID
       sid = service ID
    <= Ok for success, or an error code */
pub fn allow_service(cid: CapsuleID, sid: ServiceID) -> Result<(), Cause>
{
    match CAPSULES.lock().entry(cid)
    {
        Occupied(mut c) =>
        {
            c.get_mut().allowed_services.insert(sid);
            Ok(())
        },
        Vacant(_) => Err(Cause::CapsuleBadID)
    }
}

/* check whether a capsule is allowed to run the given service
    => cid = capsule ID to check
       sid = service ID to check
    <= Some true if the capsule exists and is allowed, or Some false
        if the capsule exists and is not allowed, or None if
        the capsule doesn't exist */
pub fn is_service_allowed(cid: CapsuleID, sid: ServiceID) -> Option<bool>
{
    match CAPSULES.lock().entry(cid)
    {
        Occupied(c) => Some(c.get().check_service(sid)),
        Vacant(_) => None
    }
}

/* add a memory mapping to a capsule
   cid = ID of capsule to add the mapping to
   to_map = memory mapping object to add
   Ok for success, or an error code
*/
pub fn map_memory(cid: CapsuleID, to_map: Mapping) -> Result<(), Cause>
{
    if let Occupied(mut c) = CAPSULES.lock().entry(cid)
    {
        c.get_mut().set_memory_mapping(to_map);
        return Ok(());
    }
    else
    {
        return Err(Cause::CapsuleBadID);
    }
}

/* enforce hardware security restrictions for the given capsule.
   supervisor-level code will only be able to access the physical
   RAM covered by that assigned to the given capsule. call this
   when switching to a capsule, so that the previous enforcement is
   replaced by enforcement of this capsule.
   => id = capsule to enforce
   <= true for success, or fail for failure
*/
pub fn enforce(id: CapsuleID) -> bool
{
    /* this is a filthy hardcode hack that I hate but it's needed for now */
    let mut index = 0;

    match CAPSULES.lock().entry(id)
    {
        Occupied(c) =>
        {
            for mapping in c.get().get_memory_mappings()
            {
                if let Some(r) = mapping.get_physical()
                {
                    if index > 0
                    {
                        panic!("TODO / FIXME: Capsules can't have more than one physical RAM region");
                    }

                    r.grant_access();
                    index = index + 1;
                }
            }
            return true
        },
        _ => false
    }
}
<|endoftext|>