<commit_before>//! Shared settings module.
//!
//! This module defines data structures to access the settings defined in the meta language.
//!
//! Each settings group is translated to a `Flags` struct either in this module or in its
//! ISA-specific `settings` module. The struct provides individual getter methods for all of the
//! settings as well as computed predicate flags.
//!
//! The `Flags` struct is immutable once it has been created. A `Builder` instance is used to
//! create it.
//!
//! # Example
//! ```
//! use cretonne::settings::{self, Configurable};
//!
//! let mut b = settings::builder();
//! b.set("opt_level", "fastest");
//!
//! let f = settings::Flags::new(&b);
//! assert_eq!(f.opt_level(), settings::OptLevel::Fastest);
//! ```

use std::fmt;
use std::result;

use constant_hash::{probe, simple_hash};

/// A string-based configurator for settings groups.
///
/// The `Configurable` protocol allows settings to be modified by name before a finished `Flags`
/// struct is created.
pub trait Configurable {
    /// Set the string value of any setting by name.
    ///
    /// This can set any type of setting whether it is numeric, boolean, or enumerated.
    fn set(&mut self, name: &str, value: &str) -> Result<()>;

    /// Set the value of a boolean setting by name.
    ///
    /// If the identified setting isn't a boolean, a `BadType` error is returned.
    fn set_bool(&mut self, name: &str, value: bool) -> Result<()>;
}

/// Collect settings values based on a template.
pub struct Builder {
    template: &'static detail::Template,
    bytes: Vec<u8>,
}

impl Builder {
    /// Create a new builder with defaults and names from the given template.
    pub fn new(tmpl: &'static detail::Template) -> Builder {
        Builder {
            template: tmpl,
            bytes: tmpl.defaults.into(),
        }
    }

    /// Extract contents of builder once everything is configured.
    pub fn state_for(&self, name: &str) -> &[u8] {
        assert_eq!(name, self.template.name);
        &self.bytes[..]
    }

    /// Set the value of a single bit.
    fn set_bit(&mut self, offset: usize, bit: u8, value: bool) {
        let byte = &mut self.bytes[offset];
        let mask = 1 << bit;
        if value {
            *byte |= mask;
        } else {
            *byte &= !mask;
        }
    }

    /// Look up a descriptor by name.
    fn lookup(&self, name: &str) -> Result<(usize, detail::Detail)> {
        match probe(self.template, name, simple_hash(name)) {
            None => Err(Error::BadName),
            Some(entry) => {
                let d = &self.template.descriptors[self.template.hash_table[entry] as usize];
                Ok((d.offset as usize, d.detail))
            }
        }
    }
}

fn parse_bool_value(value: &str) -> Result<bool> {
    match value {
        "true" | "on" | "yes" | "1" => Ok(true),
        "false" | "off" | "no" | "0" => Ok(false),
        _ => Err(Error::BadValue),
    }
}

fn parse_enum_value(value: &str, choices: &[&str]) -> Result<u8> {
    match choices.iter().position(|&tag| tag == value) {
        Some(idx) => Ok(idx as u8),
        None => Err(Error::BadValue),
    }
}

impl Configurable for Builder {
    fn set_bool(&mut self, name: &str, value: bool) -> Result<()> {
        use self::detail::Detail;
        let (offset, detail) = self.lookup(name)?;
        if let Detail::Bool { bit } = detail {
            self.set_bit(offset, bit, value);
            Ok(())
        } else {
            Err(Error::BadType)
        }
    }

    fn set(&mut self, name: &str, value: &str) -> Result<()> {
        use self::detail::Detail;
        let (offset, detail) = self.lookup(name)?;
        match detail {
            Detail::Bool { bit } => {
                self.set_bit(offset, bit, parse_bool_value(value))?;
            }
            Detail::Num => {
                self.bytes[offset] = value.parse().map_err(|_| Error::BadValue)?;
            }
            Detail::Enum { last, enumerators } => {
                self.bytes[offset] = parse_enum_value(value,
                                                           self.template.enums(last, enumerators))?;
            }
        }
        Ok(())
    }
}

/// An error produced when changing a setting.
#[derive(Debug, PartialEq, Eq)]
pub enum Error {
    /// No setting by this name exists.
    BadName,

    /// Type mismatch for setting (e.g., setting an enum setting as a bool).
    BadType,

    /// This is not a valid value for this setting.
    BadValue,
}

/// A result returned when changing a setting.
pub type Result<T> = result::Result<T, Error>;

/// Implementation details for generated code.
///
/// This module holds definitions that need to be public so the can be instantiated by generated
/// code in other modules.
pub mod detail {
    use std::fmt;
    use constant_hash;

    /// An instruction group template.
    pub struct Template {
        /// Name of the instruction group.
        pub name: &'static str,
        /// List of setting descriptors.
        pub descriptors: &'static [Descriptor],
        /// Union of all enumerators.
        pub enumerators: &'static [&'static str],
        /// Hash table of settings.
        pub hash_table: &'static [u16],
        /// Default values.
        pub defaults: &'static [u8],
    }

    impl Template {
        /// Get enumerators corresponding to a `Details::Enum`.
        pub fn enums(&self, last: u8, enumerators: u16) -> &[&'static str] {
            let from = enumerators as usize;
            let len = last as usize + 1;
            &self.enumerators[from..from + len]
        }

        /// Format a setting value as a TOML string. This is mostly for use by the generated
        /// `Display` implementation.
        pub fn format_toml_value(&self,
                                 detail: Detail,
                                 byte: u8,
                                 f: &mut fmt::Formatter)
                                 -> fmt::Result {
            match detail {
                Detail::Bool { bit } => write!(f, "{}", (byte & (1 << bit)) != 0),
                Detail::Num => write!(f, "{}", byte),
                Detail::Enum { last, enumerators } => {
                    if byte <= last {
                        let tags = self.enums(last, enumerators);
                        write!(f, "\"{}\"", tags[byte as usize])
                    } else {
                        write!(f, "{}", byte)
                    }
                }
            }
        }
    }

    /// The template contains a hash table for by-name lookup.
    impl<'a> constant_hash::Table<&'a str> for Template {
        fn len(&self) -> usize {
            self.hash_table.len()
        }

        fn key(&self, idx: usize) -> Option<&'a str> {
            let e = self.hash_table[idx] as usize;
            if e < self.descriptors.len() {
                Some(self.descriptors[e].name)
            } else {
                None
            }
        }
    }

    /// A setting descriptor holds the information needed to generically set and print a setting.
    ///
    /// Each settings group will be represented as a constant DESCRIPTORS array.
    pub struct Descriptor {
        /// Lower snake-case name of setting as defined in meta.
        pub name: &'static str,

        /// Offset of byte containing this setting.
        pub offset: u32,

        /// Additional details, depending on the kind of setting.
        pub detail: Detail,
    }

    /// The different kind of settings along with descriptor bits that depend on the kind.
    #[derive(Clone, Copy)]
    pub enum Detail {
        /// A boolean setting only uses one bit, numbered from LSB.
        Bool {
            /// 0-7.
            bit: u8,
        },

        /// A numerical setting uses the whole byte.
        Num,

        /// An Enum setting uses a range of enumerators.
        Enum {
            /// Numerical value of last enumerator, allowing for 1-256 enumerators.
            last: u8,

            /// First enumerator in the ENUMERATORS table.
            enumerators: u16,
        },
    }
}

// Include code generated by `meta/gen_settings.py`. This file contains a public `Flags` struct
// with an impl for all of the settings defined in `meta/cretonne/settings.py`.
include!(concat!(env!("OUT_DIR"), "/settings.rs"));

#[cfg(test)]
mod tests {
    use super::{builder, Flags};
    use super::Error::*;
    use super::Configurable;

    #[test]
    fn display_default() {
        let b = builder();
        let f = Flags::new(&b);
        assert_eq!(f.to_string(),
                   "[shared]\n\
                    opt_level = \"default\"\n\
                    is_64bit = false\n\
                    is_compressed = false\n\
                    enable_float = true\n\
                    enable_simd = true\n\
                    enable_atomics = true\n");
        assert_eq!(f.opt_level(), super::OptLevel::Default);
        assert_eq!(f.enable_simd(), true);
    }

    #[test]
    fn modify_bool() {
        let mut b = builder();
        assert_eq!(b.set_bool("not_there", true), Err(BadName));
        assert_eq!(b.set_bool("enable_simd", true), Ok(()));
        assert_eq!(b.set_bool("enable_simd", false), Ok(()));

        let f = Flags::new(&b);
        assert_eq!(f.enable_simd(), false);
    }

    #[test]
    fn modify_string() {
        let mut b = builder();
        assert_eq!(b.set("not_there", "true"), Err(BadName));
        assert_eq!(b.set("enable_simd", ""), Err(BadValue));
        assert_eq!(b.set("enable_simd", "best"), Err(BadValue));
        assert_eq!(b.set("opt_level", "true"), Err(BadValue));
        assert_eq!(b.set("opt_level", "best"), Ok(()));
        assert_eq!(b.set("enable_simd", "0"), Ok(()));

        let f = Flags::new(&b);
        assert_eq!(f.enable_simd(), false);
        assert_eq!(f.opt_level(), super::OptLevel::Best);
    }
}
<commit_msg>Fixes formatting for settings.rs<commit_after>//! Shared settings module.
//!
//! This module defines data structures to access the settings defined in the meta language.
//!
//! Each settings group is translated to a `Flags` struct either in this module or in its
//! ISA-specific `settings` module. The struct provides individual getter methods for all of the
//! settings as well as computed predicate flags.
//!
//! The `Flags` struct is immutable once it has been created. A `Builder` instance is used to
//! create it.
//!
//! # Example
//! ```
//! use cretonne::settings::{self, Configurable};
//!
//! let mut b = settings::builder();
//! b.set("opt_level", "fastest");
//!
//! let f = settings::Flags::new(&b);
//! assert_eq!(f.opt_level(), settings::OptLevel::Fastest);
//! ```

use std::fmt;
use std::result;

use constant_hash::{probe, simple_hash};

/// A string-based configurator for settings groups.
///
/// The `Configurable` protocol allows settings to be modified by name before a finished `Flags`
/// struct is created.
pub trait Configurable {
    /// Set the string value of any setting by name.
    ///
    /// This can set any type of setting whether it is numeric, boolean, or enumerated.
    fn set(&mut self, name: &str, value: &str) -> Result<()>;

    /// Set the value of a boolean setting by name.
    ///
    /// If the identified setting isn't a boolean, a `BadType` error is returned.
    fn set_bool(&mut self, name: &str, value: bool) -> Result<()>;
}

/// Collect settings values based on a template.
pub struct Builder {
    template: &'static detail::Template,
    bytes: Vec<u8>,
}

impl Builder {
    /// Create a new builder with defaults and names from the given template.
    pub fn new(tmpl: &'static detail::Template) -> Builder {
        Builder {
            template: tmpl,
            bytes: tmpl.defaults.into(),
        }
    }

    /// Extract contents of builder once everything is configured.
    pub fn state_for(&self, name: &str) -> &[u8] {
        assert_eq!(name, self.template.name);
        &self.bytes[..]
    }

    /// Set the value of a single bit.
    fn set_bit(&mut self, offset: usize, bit: u8, value: bool) {
        let byte = &mut self.bytes[offset];
        let mask = 1 << bit;
        if value {
            *byte |= mask;
        } else {
            *byte &= !mask;
        }
    }

    /// Look up a descriptor by name.
    fn lookup(&self, name: &str) -> Result<(usize, detail::Detail)> {
        match probe(self.template, name, simple_hash(name)) {
            None => Err(Error::BadName),
            Some(entry) => {
                let d = &self.template.descriptors[self.template.hash_table[entry] as usize];
                Ok((d.offset as usize, d.detail))
            }
        }
    }
}

fn parse_bool_value(value: &str) -> Result<bool> {
    match value {
        "true" | "on" | "yes" | "1" => Ok(true),
        "false" | "off" | "no" | "0" => Ok(false),
        _ => Err(Error::BadValue),
    }
}

fn parse_enum_value(value: &str, choices: &[&str]) -> Result<u8> {
    match choices.iter().position(|&tag| tag == value) {
        Some(idx) => Ok(idx as u8),
        None => Err(Error::BadValue),
    }
}

impl Configurable for Builder {
    fn set_bool(&mut self, name: &str, value: bool) -> Result<()> {
        use self::detail::Detail;
        let (offset, detail) = self.lookup(name)?;
        if let Detail::Bool { bit } = detail {
            self.set_bit(offset, bit, value);
            Ok(())
        } else {
            Err(Error::BadType)
        }
    }

    fn set(&mut self, name: &str, value: &str) -> Result<()> {
        use self::detail::Detail;
        let (offset, detail) = self.lookup(name)?;
        match detail {
            Detail::Bool { bit } => {
                self.set_bit(offset, bit, parse_bool_value(value))?;
            }
            Detail::Num => {
                self.bytes[offset] = value.parse().map_err(|_| Error::BadValue)?;
            }
            Detail::Enum { last, enumerators } => {
                self.bytes[offset] = parse_enum_value(value,
                                                      self.template.enums(last, enumerators))?;
            }
        }
        Ok(())
    }
}

/// An error produced when changing a setting.
#[derive(Debug, PartialEq, Eq)]
pub enum Error {
    /// No setting by this name exists.
    BadName,

    /// Type mismatch for setting (e.g., setting an enum setting as a bool).
    BadType,

    /// This is not a valid value for this setting.
    BadValue,
}

/// A result returned when changing a setting.
pub type Result<T> = result::Result<T, Error>;

/// Implementation details for generated code.
///
/// This module holds definitions that need to be public so the can be instantiated by generated
/// code in other modules.
pub mod detail {
    use std::fmt;
    use constant_hash;

    /// An instruction group template.
    pub struct Template {
        /// Name of the instruction group.
        pub name: &'static str,
        /// List of setting descriptors.
        pub descriptors: &'static [Descriptor],
        /// Union of all enumerators.
        pub enumerators: &'static [&'static str],
        /// Hash table of settings.
        pub hash_table: &'static [u16],
        /// Default values.
        pub defaults: &'static [u8],
    }

    impl Template {
        /// Get enumerators corresponding to a `Details::Enum`.
        pub fn enums(&self, last: u8, enumerators: u16) -> &[&'static str] {
            let from = enumerators as usize;
            let len = last as usize + 1;
            &self.enumerators[from..from + len]
        }

        /// Format a setting value as a TOML string. This is mostly for use by the generated
        /// `Display` implementation.
        pub fn format_toml_value(&self,
                                 detail: Detail,
                                 byte: u8,
                                 f: &mut fmt::Formatter)
                                 -> fmt::Result {
            match detail {
                Detail::Bool { bit } => write!(f, "{}", (byte & (1 << bit)) != 0),
                Detail::Num => write!(f, "{}", byte),
                Detail::Enum { last, enumerators } => {
                    if byte <= last {
                        let tags = self.enums(last, enumerators);
                        write!(f, "\"{}\"", tags[byte as usize])
                    } else {
                        write!(f, "{}", byte)
                    }
                }
            }
        }
    }

    /// The template contains a hash table for by-name lookup.
    impl<'a> constant_hash::Table<&'a str> for Template {
        fn len(&self) -> usize {
            self.hash_table.len()
        }

        fn key(&self, idx: usize) -> Option<&'a str> {
            let e = self.hash_table[idx] as usize;
            if e < self.descriptors.len() {
                Some(self.descriptors[e].name)
            } else {
                None
            }
        }
    }

    /// A setting descriptor holds the information needed to generically set and print a setting.
    ///
    /// Each settings group will be represented as a constant DESCRIPTORS array.
    pub struct Descriptor {
        /// Lower snake-case name of setting as defined in meta.
        pub name: &'static str,

        /// Offset of byte containing this setting.
        pub offset: u32,

        /// Additional details, depending on the kind of setting.
        pub detail: Detail,
    }

    /// The different kind of settings along with descriptor bits that depend on the kind.
    #[derive(Clone, Copy)]
    pub enum Detail {
        /// A boolean setting only uses one bit, numbered from LSB.
        Bool {
            /// 0-7.
            bit: u8,
        },

        /// A numerical setting uses the whole byte.
        Num,

        /// An Enum setting uses a range of enumerators.
        Enum {
            /// Numerical value of last enumerator, allowing for 1-256 enumerators.
            last: u8,

            /// First enumerator in the ENUMERATORS table.
            enumerators: u16,
        },
    }
}

// Include code generated by `meta/gen_settings.py`. This file contains a public `Flags` struct
// with an impl for all of the settings defined in `meta/cretonne/settings.py`.
include!(concat!(env!("OUT_DIR"), "/settings.rs"));

#[cfg(test)]
mod tests {
    use super::{builder, Flags};
    use super::Error::*;
    use super::Configurable;

    #[test]
    fn display_default() {
        let b = builder();
        let f = Flags::new(&b);
        assert_eq!(f.to_string(),
                   "[shared]\n\
                    opt_level = \"default\"\n\
                    is_64bit = false\n\
                    is_compressed = false\n\
                    enable_float = true\n\
                    enable_simd = true\n\
                    enable_atomics = true\n");
        assert_eq!(f.opt_level(), super::OptLevel::Default);
        assert_eq!(f.enable_simd(), true);
    }

    #[test]
    fn modify_bool() {
        let mut b = builder();
        assert_eq!(b.set_bool("not_there", true), Err(BadName));
        assert_eq!(b.set_bool("enable_simd", true), Ok(()));
        assert_eq!(b.set_bool("enable_simd", false), Ok(()));

        let f = Flags::new(&b);
        assert_eq!(f.enable_simd(), false);
    }

    #[test]
    fn modify_string() {
        let mut b = builder();
        assert_eq!(b.set("not_there", "true"), Err(BadName));
        assert_eq!(b.set("enable_simd", ""), Err(BadValue));
        assert_eq!(b.set("enable_simd", "best"), Err(BadValue));
        assert_eq!(b.set("opt_level", "true"), Err(BadValue));
        assert_eq!(b.set("opt_level", "best"), Ok(()));
        assert_eq!(b.set("enable_simd", "0"), Ok(()));

        let f = Flags::new(&b);
        assert_eq!(f.enable_simd(), false);
        assert_eq!(f.opt_level(), super::OptLevel::Best);
    }
}
<|endoftext|>


<commit_before>extern crate robots;

use robots::actors::{Actor,ActorCell};
use std::sync::Mutex;
use std::any::Any;
use std::thread;
use std::time;
use super::super::gpioaccess::{DirectionProxied,PinProxyContract,PwmProxyContract};
use super::super::gpioaccess::proxyimpl::{PinProxy,PwmProxy};

#[derive(Copy, Clone, PartialEq,Debug)]
pub enum PinCommands {
	Blink(u64),
	Switch,
}

pub struct PinActor {
	pinproxy :  Mutex<PinProxy>,
}

impl Actor for PinActor {
	fn receive(&self, _message: Box<Any>, _context: ActorCell) {
		if let Ok(_message) = Box::<Any>::downcast::<PinCommands>(_message){
			match *_message {
				PinCommands::Blink(times) => {
					let pin = self.pinproxy.lock().unwrap();
					match pin.export() {
						Ok(()) => {
							println!("Pin exported");
							for _ in 1..times {
								pin.set_direction(DirectionProxied::Out);
								pin.set_direction(DirectionProxied::High);
								thread::sleep(time::Duration::from_millis(400));
								pin.set_direction(DirectionProxied::Low);
								thread::sleep(time::Duration::from_millis(400));
								println!("Blink");
							}
							match pin.unexport() {
								Ok(()) => {
									println!("Pin unexported");
								}
								_ => {}
							}
						}
						_ => {}
					}
				},
				PinCommands::Switch => { }
			}
		}
	}
}

impl PinActor{
	pub fn new(pin_number: u64) -> PinActor {
		PinActor{ pinproxy : Mutex::new(PinProxy::new(pin_number)) }
	}
}

#[derive(Copy, Clone, PartialEq,Debug)]
pub enum PwmCommands {
	MoveToDegree(u16)
}

pub struct PwmActor {
	pwmproxy :  Result<Mutex<PwmProxy>,()>,
}

impl Actor for PwmActor {
	fn receive(&self, _message: Box<Any>, _context: ActorCell) {
		println!("pwm received message");
		if let Ok(_message) = Box::<Any>::downcast::<PwmCommands>(_message){
			println!("pwm recognized command");
			match *_message {
				PwmCommands::MoveToDegree(degree) => {
					println!("pwm movetodegree");
					match self.pwmproxy {
						Ok(ref pwmproxyresult) =>
						{
							let pwm = pwmproxyresult.lock().unwrap();
							match pwm.export() {
								Ok(()) => {
									println!("Pwm exported");
									match pwm.set_period_ns(1_500_000)
									{
										Ok(()) => {
											println!("Pwm setting period");
											match pwm.enable(true)
											{
												Ok(()) => {
													println!("Pwm enable");
													for x in 1..10 {
														println!("Pwm {}",x);
														pwm.increase_to_max(1000, 2);
														pwm.decrease_to_minimum(1000, 2);
													}
													match pwm.enable(false)
													{
														Ok(()) => {
															println!("Pwm disable");
														}
														_ => println!("Pwm disabled failed")
													}
												}
												_ => println!("Pwm enabled failed")
											}
										}
										_ => println!("Pwm set period failed")
									}
									match pwm.unexport() {
										Ok(()) => {
											println!("Pwm unexported");
										}
										_ => println!("Pwm unexport failed")
									}
								}
								_ => println!("Pwm export failed")
							}
						}
						_ => println!("No Pwm")
					}

					println!("pwm done");
				}
			}
		}
	}
}

impl PwmActor{
	pub fn new(number: u32) -> PwmActor {
		match PwmProxy::new(0,number)
		{
			Ok(pwmproxy) =>  PwmActor{ pwmproxy : Ok(Mutex::new(pwmproxy)) },
			_ => PwmActor{ pwmproxy : Err(()) },
		}
	}
}<commit_msg>modif<commit_after>extern crate robots;

use robots::actors::{Actor,ActorCell};
use std::sync::Mutex;
use std::any::Any;
use std::thread;
use std::time;
use super::super::gpioaccess::{DirectionProxied,PinProxyContract,PwmProxyContract};
use super::super::gpioaccess::proxyimpl::{PinProxy,PwmProxy};

#[derive(Copy, Clone, PartialEq,Debug)]
pub enum PinCommands {
	Blink(u64),
	Switch,
}

pub struct PinActor {
	pinproxy :  Mutex<PinProxy>,
}

impl Actor for PinActor {
	fn receive(&self, _message: Box<Any>, _context: ActorCell) {
		if let Ok(_message) = Box::<Any>::downcast::<PinCommands>(_message){
			match *_message {
				PinCommands::Blink(times) => {
					let pin = self.pinproxy.lock().unwrap();
					match pin.export() {
						Ok(()) => {
							println!("Pin exported");
							for _ in 1..times {
								pin.set_direction(DirectionProxied::Out);
								pin.set_direction(DirectionProxied::High);
								thread::sleep(time::Duration::from_millis(400));
								pin.set_direction(DirectionProxied::Low);
								thread::sleep(time::Duration::from_millis(400));
								println!("Blink");
							}
							match pin.unexport() {
								Ok(()) => {
									println!("Pin unexported");
								}
								_ => {}
							}
						}
						_ => {}
					}
				},
				PinCommands::Switch => { }
			}
		}
	}
}

impl PinActor{
	pub fn new(pin_number: u64) -> PinActor {
		PinActor{ pinproxy : Mutex::new(PinProxy::new(pin_number)) }
	}
}

#[derive(Copy, Clone, PartialEq,Debug)]
pub enum PwmCommands {
	MoveToDegree(u16)
}

pub struct PwmActor {
	pwmproxy :  Result<Mutex<PwmProxy>,()>,
}

impl Actor for PwmActor {
	fn receive(&self, _message: Box<Any>, _context: ActorCell) {
		println!("pwm received message");
		if let Ok(_message) = Box::<Any>::downcast::<PwmCommands>(_message){
			println!("pwm recognized command");
			match *_message {
				PwmCommands::MoveToDegree(degree) => {
					println!("pwm movetodegree");
					match self.pwmproxy {
						Ok(ref pwmproxyresult) =>
						{
							let pwm = pwmproxyresult.lock().unwrap();
							match pwm.export() {
								Ok(()) => {
									println!("Pwm exported");
									match pwm.set_period_ns(1_500_000)
									{
										Ok(()) => {
											println!("Pwm setting period");
											match pwm.enable(true)
											{
												Ok(()) => {
													println!("Pwm enable");
													for x in 1..10 {
														println!("Pwm {}",x);
														pwm.increase_to_max(1000, 200);
														pwm.decrease_to_minimum(1000, 200);
													}
													match pwm.enable(false)
													{
														Ok(()) => {
															println!("Pwm disable");
														}
														_ => println!("Pwm disabled failed")
													}
												}
												_ => println!("Pwm enabled failed")
											}
										}
										_ => println!("Pwm set period failed")
									}
									match pwm.unexport() {
										Ok(()) => {
											println!("Pwm unexported");
										}
										_ => println!("Pwm unexport failed")
									}
								}
								_ => println!("Pwm export failed")
							}
						}
						_ => println!("No Pwm")
					}

					println!("pwm done");
				}
			}
		}
	}
}

impl PwmActor{
	pub fn new(number: u32) -> PwmActor {
		match PwmProxy::new(0,number)
		{
			Ok(pwmproxy) =>  PwmActor{ pwmproxy : Ok(Mutex::new(pwmproxy)) },
			_ => PwmActor{ pwmproxy : Err(()) },
		}
	}
}<|endoftext|>


<commit_before>use std::{mem, slice};
use std::sync::atomic::{AtomicUsize, Ordering};
use std::ops::{Index, IndexMut};
use std::marker::PhantomData;
use std::collections::LinkedList;

/// A pool represents a fixed number of ref-counted objects.
/// The pool treats all given space as an unallocated
/// pool of objects. Each object is prefixed with a header.
/// The header is formatted as follows:
/// * V1
///   - [0..2] ref_count: u16
///
pub struct Pool<T> {
    item_type: PhantomData<T>,

    buffer: *mut u8,
    buffer_size: usize,
    capacity: usize,

    tail: AtomicUsize, // One past the end index

    // Cached values
    slot_size: usize,
    header_size: usize,

    free_list: LinkedList<usize>,
}

struct SlotHeader {
    ref_count: AtomicUsize,
}

/// Public interface
impl <T> Pool<T> {
    pub fn new(mem: &mut [u8]) -> Pool<T> {
        let ptr: *mut u8 = mem.as_mut_ptr();
        let header_size = mem::size_of::<SlotHeader>();
        let slot_size = mem::size_of::<T>() + header_size;
        Pool {
            item_type: PhantomData,
            buffer: ptr,
            buffer_size: mem.len(),
            tail: AtomicUsize::new(0),
            slot_size: slot_size,
            capacity: mem.len() / slot_size,
            header_size: header_size,
            free_list: LinkedList::new(),
        }
    }

    /// Remove all objects from the pool
    /// and zero the memory
    pub unsafe fn clear(&mut self) {
        let mut i = self.buffer.clone();
        let end = self.buffer.clone().offset(self.buffer_size as isize);
        while i != end {
            *i = 0u8;
            i = i.offset(1);
        }
    }

    /// Try to allocate a new item from the pool.
    /// A mutable reference to the item is returned on success
    pub fn alloc(&mut self) -> Result<&mut T, &'static str> {
        let index = try!(self.claim_free_index());
        Ok(&mut self[index])
    }

    // Increase the ref count for the cell at the given index
    pub fn retain(&mut self, index: usize) {
        let h = self.header_for(index);
        loop {
            let old = h.ref_count.load(Ordering::Relaxed);
            let swap = h.ref_count
                .compare_and_swap(old, old+1, Ordering::Relaxed);
            if swap == old {
                break
            }
        }
    }

    // Decrease the ref count for the cell at the given index
    pub fn release(&mut self, index: usize) {
        let mut is_free = false;
        { // Make the borrow checker happy
            let h = self.header_for(index);
            loop {
                let old = h.ref_count.load(Ordering::Relaxed);
                assert!(old > 0, "Release called on [{}] which has no refs!", index);

                let swap = h.ref_count
                    .compare_and_swap(old, old-1, Ordering::Relaxed);
                if swap == old {
                    if old == 1 { // this was the last reference
                        is_free = true;
                    }
                    break
                }
            }
        }
        if is_free {
            self.free_list.push_back(index);
        }
    }
}

/// Internal Functions
impl <T> Pool<T> {
    // Returns an item from the free list, or
    // tries to allocate a new one from the buffer
    fn claim_free_index(&mut self) -> Result<usize, &'static str> {
        let index = try!(self.free_list.pop_front()
                .ok_or("")
                .or(self.push_back_alloc()));
        self.retain(index);
        Ok(index)
    }

    // Pushes the end of the used space in the buffer back
    // returns the previous index
    fn push_back_alloc(&mut self) -> Result<usize, &'static str> {
        loop {
            let old_tail = self.tail.load(Ordering::Relaxed);
            let swap = self.tail.compare_and_swap(old_tail, old_tail+1, Ordering::Relaxed);
            // If we were the ones to claim this slot, or
            // we've overrun the buffer, return
            if old_tail >= self.capacity {
                return Err("OOM")
            } else if swap == old_tail {
                return Ok(old_tail)
            }
        }
    }

    fn header_for<'a>(&'a mut self, i: usize) -> &'a mut SlotHeader {
        unsafe {
            let ptr = self.buffer.clone()
                .offset((i * self.slot_size) as isize);
            mem::transmute(ptr)
        }
    }

    /// Without free list, try to claim the first re-usable
    /// item.
    /// #[deprecated]
    fn claim_first_free_slot(&mut self) -> usize {
        let mut i = self.buffer.clone();
        let mut index = 0;
        while index < self.capacity {
            unsafe {
                let header: *mut SlotHeader = mem::transmute(i);
                if (*header).ref_count
                    .compare_and_swap(0, 1, Ordering::Relaxed) == 0 {
                    break
                }
                // Otherwise, look for the next one
                i = i.offset(self.slot_size as isize);
                index += 1;
            }
        }
        index
    }
}

impl <T> Index<usize> for Pool<T> {
    type Output = T;

    fn index<'a>(&'a self, i: usize) -> &'a T {
        unsafe {
            let ptr = self.buffer.clone()
                .offset((i * self.slot_size) as isize)
                .offset(self.header_size as isize);
            mem::transmute(ptr)
        }
    }
}

impl <T> IndexMut<usize> for Pool<T> {
    fn index_mut<'a>(&'a mut self, i: usize) -> &'a mut T {
        unsafe {
            let ptr = self.buffer.clone()
                .offset((i * self.slot_size) as isize)
                .offset(self.header_size as isize);
            mem::transmute(ptr)
        }
    }
}

#[test]
fn construction() {
   let mut buf: [u8; 100] = [0; 100];
   let mut p = Pool::<u32>::new(&mut buf[..]);

   assert_eq!(100, p.buffer_size);
   assert_eq!(mem::size_of::<usize>(), p.header_size);

   let expected_size = mem::size_of::<usize>() + mem::size_of::<u32>();
   assert_eq!(expected_size, p.slot_size);
   assert_eq!(100/expected_size, p.capacity); // expected_size should be 8+4=12
   assert_eq!(8, p.capacity);
}

#[test]
fn free_list_alloc_works() {
   let mut buf: [u8; 100] = [0; 100];
   let mut p = Pool::<u32>::new(&mut buf[..]);
   {
        let int1:&mut u32 = p.alloc().unwrap();
        *int1 = 42;
   }
   // Check ref_count
   assert_eq!([1u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8][..], buf[0..8]);
   // Check payload
   assert_eq!([42u8, 0u8, 0u8, 0u8][..], buf[8..12]);
}

#[test]
fn check_oom_error() {
    let mut buf: [u8; 1] = [0; 1];
    let mut p = Pool::<u32>::new(&mut buf[..]);
    assert_eq!(Err("OOM"), p.alloc());
}

#[test]
fn multiple_allocations_work() {
   let mut buf: [u8; 120] = [0; 120];
   let mut p = Pool::<u32>::new(&mut buf[..]);
   for i in 0..10 {
        let int1 = p.alloc().unwrap();
        *int1 = i;
   }
   for i in 0..10 {
       let start = 12*i;
       // Check ref_count
       assert_eq!([1u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8][..],
            buf[start..start+8]);
       // Check payload
       assert_eq!([i as u8, 0u8, 0u8, 0u8][..],
           buf[start+8..start+12]);
   }
}
<commit_msg>removed linear scan<commit_after>use std::{mem, slice};
use std::sync::atomic::{AtomicUsize, Ordering};
use std::ops::{Index, IndexMut};
use std::marker::PhantomData;
use std::collections::LinkedList;

/// A pool represents a fixed number of ref-counted objects.
/// The pool treats all given space as an unallocated
/// pool of objects. Each object is prefixed with a header.
/// The header is formatted as follows:
/// * V1
///   - [0..2] ref_count: u16
///
pub struct Pool<T> {
    item_type: PhantomData<T>,

    buffer: *mut u8,
    buffer_size: usize,
    capacity: usize,

    tail: AtomicUsize, // One past the end index

    // Cached values
    slot_size: usize,
    header_size: usize,

    free_list: LinkedList<usize>,
}

struct SlotHeader {
    ref_count: AtomicUsize,
}

/// Public interface
impl <T> Pool<T> {
    pub fn new(mem: &mut [u8]) -> Pool<T> {
        let ptr: *mut u8 = mem.as_mut_ptr();
        let header_size = mem::size_of::<SlotHeader>();
        let slot_size = mem::size_of::<T>() + header_size;
        Pool {
            item_type: PhantomData,
            buffer: ptr,
            buffer_size: mem.len(),
            tail: AtomicUsize::new(0),
            slot_size: slot_size,
            capacity: mem.len() / slot_size,
            header_size: header_size,
            free_list: LinkedList::new(),
        }
    }

    /// Remove all objects from the pool
    /// and zero the memory
    pub unsafe fn clear(&mut self) {
        let mut i = self.buffer.clone();
        let end = self.buffer.clone().offset(self.buffer_size as isize);
        while i != end {
            *i = 0u8;
            i = i.offset(1);
        }
    }

    /// Try to allocate a new item from the pool.
    /// A mutable reference to the item is returned on success
    pub fn alloc(&mut self) -> Result<&mut T, &'static str> {
        let index = try!(self.claim_free_index());
        Ok(&mut self[index])
    }

    // Increase the ref count for the cell at the given index
    pub fn retain(&mut self, index: usize) {
        let h = self.header_for(index);
        loop {
            let old = h.ref_count.load(Ordering::Relaxed);
            let swap = h.ref_count
                .compare_and_swap(old, old+1, Ordering::Relaxed);
            if swap == old {
                break
            }
        }
    }

    // Decrease the ref count for the cell at the given index
    pub fn release(&mut self, index: usize) {
        let mut is_free = false;
        { // Make the borrow checker happy
            let h = self.header_for(index);
            loop {
                let old = h.ref_count.load(Ordering::Relaxed);
                assert!(old > 0, "Release called on [{}] which has no refs!", index);

                let swap = h.ref_count
                    .compare_and_swap(old, old-1, Ordering::Relaxed);
                if swap == old {
                    if old == 1 { // this was the last reference
                        is_free = true;
                    }
                    break
                }
            }
        }
        if is_free {
            self.free_list.push_back(index);
        }
    }
}

/// Internal Functions
impl <T> Pool<T> {
    // Returns an item from the free list, or
    // tries to allocate a new one from the buffer
    fn claim_free_index(&mut self) -> Result<usize, &'static str> {
        let index = try!(self.free_list.pop_front()
                .ok_or("")
                .or(self.push_back_alloc()));
        self.retain(index);
        Ok(index)
    }

    // Pushes the end of the used space in the buffer back
    // returns the previous index
    fn push_back_alloc(&mut self) -> Result<usize, &'static str> {
        loop {
            let old_tail = self.tail.load(Ordering::Relaxed);
            let swap = self.tail.compare_and_swap(old_tail, old_tail+1, Ordering::Relaxed);
            // If we were the ones to claim this slot, or
            // we've overrun the buffer, return
            if old_tail >= self.capacity {
                return Err("OOM")
            } else if swap == old_tail {
                return Ok(old_tail)
            }
        }
    }

    fn header_for<'a>(&'a mut self, i: usize) -> &'a mut SlotHeader {
        unsafe {
            let ptr = self.buffer.clone()
                .offset((i * self.slot_size) as isize);
            mem::transmute(ptr)
        }
    }
}

impl <T> Index<usize> for Pool<T> {
    type Output = T;

    fn index<'a>(&'a self, i: usize) -> &'a T {
        unsafe {
            let ptr = self.buffer.clone()
                .offset((i * self.slot_size) as isize)
                .offset(self.header_size as isize);
            mem::transmute(ptr)
        }
    }
}

impl <T> IndexMut<usize> for Pool<T> {
    fn index_mut<'a>(&'a mut self, i: usize) -> &'a mut T {
        unsafe {
            let ptr = self.buffer.clone()
                .offset((i * self.slot_size) as isize)
                .offset(self.header_size as isize);
            mem::transmute(ptr)
        }
    }
}

#[test]
fn construction() {
   let mut buf: [u8; 100] = [0; 100];
   let mut p = Pool::<u32>::new(&mut buf[..]);

   assert_eq!(100, p.buffer_size);
   assert_eq!(mem::size_of::<usize>(), p.header_size);

   let expected_size = mem::size_of::<usize>() + mem::size_of::<u32>();
   assert_eq!(expected_size, p.slot_size);
   assert_eq!(100/expected_size, p.capacity); // expected_size should be 8+4=12
   assert_eq!(8, p.capacity);
}

#[test]
fn free_list_alloc_works() {
   let mut buf: [u8; 100] = [0; 100];
   let mut p = Pool::<u32>::new(&mut buf[..]);
   {
        let int1:&mut u32 = p.alloc().unwrap();
        *int1 = 42;
   }
   // Check ref_count
   assert_eq!([1u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8][..], buf[0..8]);
   // Check payload
   assert_eq!([42u8, 0u8, 0u8, 0u8][..], buf[8..12]);
}

#[test]
fn check_oom_error() {
    let mut buf: [u8; 1] = [0; 1];
    let mut p = Pool::<u32>::new(&mut buf[..]);
    assert_eq!(Err("OOM"), p.alloc());
}

#[test]
fn multiple_allocations_work() {
   let mut buf: [u8; 120] = [0; 120];
   let mut p = Pool::<u32>::new(&mut buf[..]);
   for i in 0..10 {
        let int1 = p.alloc().unwrap();
        *int1 = i;
   }
   for i in 0..10 {
       let start = 12*i;
       // Check ref_count
       assert_eq!([1u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8][..],
            buf[start..start+8]);
       // Check payload
       assert_eq!([i as u8, 0u8, 0u8, 0u8][..],
           buf[start+8..start+12]);
   }
}
<|endoftext|>


<commit_before>#![feature(test)]

extern crate cell_gc;
extern crate lisp;
extern crate test;

use lisp::{parse, vm};
use std::fs;
use std::io::Read;

macro_rules! bench {
    ( $filename:ident ) => {
        #[bench]
        fn $filename(b: &mut test::Bencher) {
            let filename = concat!(env!("CARGO_MANIFEST_DIR"),
                                   "/benches/",
                                   stringify!($filename),
                                   ".scm");
            let mut file = fs::File::open(filename).expect("should open file OK");
            let mut source = String::new();
            file.read_to_string(&mut source).expect("should read file OK");

            b.iter(|| {
                cell_gc::with_heap(|hs| {
                    let env = vm::Environment::default_env(hs);
                    let exprs = parse::parse(hs, &source).expect("should parse source OK");
                    for expr in exprs {
                        vm::eval(hs, expr, env.clone()).expect("should eval OK");
                    }
                });
            });
        }
    }
}

bench!(allocate_no_garbage);
bench!(allocate_all_garbage);
<commit_msg>lisp: Refactor benchmark code (to no real effect).<commit_after>#![feature(test)]

extern crate cell_gc;
extern crate lisp;
extern crate test;

use lisp::parse;
use lisp::vm::{self, Environment};

macro_rules! bench {
    ( $filename:ident ) => {
        #[bench]
        fn $filename(b: &mut test::Bencher) {
            run_benchmark(
                b,
                include_str!(
                    concat!(env!("CARGO_MANIFEST_DIR"),
                            "/benches/",
                            stringify!($filename),
                            ".scm")
                ),
            );
        }
    }
}

fn run_benchmark(b: &mut test::Bencher, code: &str) {
    b.iter(|| {
        let mut heap = cell_gc::GcHeap::new();
        heap.enter(|hs| {
            let env = Environment::default_env(hs);
            let exprs = parse::parse(hs, code).expect("should parse source OK");
            for expr in exprs {
                vm::eval(hs, expr, env.clone()).expect("should eval OK");
            }
        });
    });
}

bench!(allocate_no_garbage);
bench!(allocate_all_garbage);
<|endoftext|>


<commit_before>use std::fmt::Debug;
use adapton_syntax::*;
use adapton_sigs::*;
use std::rc::Rc;
use std::hash::{Hash,Hasher};
use std::fmt::{Formatter,Result};

pub type Loc = usize;

#[derive(Debug,Clone,Hash,Eq,PartialEq)]
pub struct Name;

#[derive(Debug)]
pub struct Whatever;
pub struct AdaptonFromScratch {
    /// just need a store; the Adapton trait provides a store semantics
    store : Vec<Box<Whatever>>
}

impl Adapton for AdaptonFromScratch {
    type Name = Name;
    type Loc  = Loc;

    fn new () -> AdaptonFromScratch {
        AdaptonFromScratch {
            store : Vec::new (),
        }
    }

    fn name_of_string (self:&mut AdaptonFromScratch, sym:String) -> Name { Name }
    fn name_of_u64 (self:&mut AdaptonFromScratch, sym:u64) -> Name { Name }
    fn name_pair (self: &mut AdaptonFromScratch, fst:Name, snd:Name) -> Name { Name }
    fn name_fork (self:&mut AdaptonFromScratch, nm:Name) -> (Name, Name) { (Name,Name) }
    fn ns<T,F> (self: &mut AdaptonFromScratch, nm:Name, body:F) -> T where F:FnOnce(&mut AdaptonFromScratch) -> T { body(self) }
    fn put<T:Eq> (self:&mut AdaptonFromScratch, x:T) -> Art<T,Loc> { Art::Rc(Rc::new(x)) }

    fn cell<T:Eq+Debug+Clone
        +'static // TODO-Later: Needed on T because of lifetime issues.
        >
        (self:&mut AdaptonFromScratch, nm:Name, val:T) -> MutArt<T,Loc> {
            panic!("")
        }

    fn set<T:Eq+Debug> (self:&mut AdaptonFromScratch, cell:MutArt<T,Loc>, val:T) {
        panic!("")
    }

    fn thunk<Arg:Eq+Hash+Debug+Clone+'static,Spurious:'static+Clone,Res:Eq+Debug+Clone+'static>
        (self:&mut AdaptonFromScratch,
         id:ArtIdChoice<Name>,
         prog_pt:ProgPt,
         fn_box:Rc<Box<Fn(&mut AdaptonFromScratch, Arg, Spurious) -> Res>>,
         arg:Arg, spurious:Spurious)
         -> Art<Res,Loc>
    {
        panic!("")
    }

    fn force<T:'static+Eq+Debug+Clone> (self:&mut AdaptonFromScratch,
                                        art:&Art<T,Loc>) -> T
    {
        panic!("")
    }
}

// Produce a value of type Res.
trait Producer<Res> {
    fn produce(self:&Self, st:&mut AdaptonFromScratch) -> Res;
}

#[derive(Clone)]
struct App<Arg,Spurious,Res> {
    prog_pt:  ProgPt,
    fn_box:   Rc<Box<Fn(&mut AdaptonFromScratch, Arg, Spurious) -> Res>>,
    arg:      Arg,
    spurious: Spurious,
}

impl<Arg,Spurious,Res> Debug for App<Arg,Spurious,Res> {
    fn fmt(&self, f: &mut Formatter) -> Result { self.prog_pt.fmt(f) }
}

impl<Arg:Hash,Spurious,Res> Hash for App<Arg,Spurious,Res> {
    fn hash<H>(&self, state: &mut H) where H: Hasher { (&self.prog_pt,&self.arg).hash(state) }
}

impl<Arg:'static+PartialEq+Eq+Clone,Spurious:'static+Clone,Res:'static> Producer<Res>
    for App<Arg,Spurious,Res>
{
    fn produce(self:&Self, st:&mut AdaptonFromScratch) -> Res {
        let f = self.fn_box.clone() ;
        f (st,self.arg.clone(),self.spurious.clone())
    }
}
<commit_msg>implemented thunk and force for AdaptonFromScratch<commit_after>use std::fmt::Debug;
use adapton_syntax::*;
use adapton_sigs::*;
use std::rc::Rc;
use std::hash::{Hash,Hasher};
use std::fmt::{Formatter,Result};
use std::mem::transmute;
use std::marker::PhantomData;

pub type Loc = usize;

#[derive(Debug,Clone,Hash,Eq,PartialEq)]
pub struct Name;

trait Void { }

pub struct AdaptonFromScratch {
    /// need a store; the Adapton trait provides a store semantics (viz., see `set` and `force`).
    store : Vec<Box<Void>>
}

impl Adapton for AdaptonFromScratch {
    type Name = Name;
    type Loc  = Loc;

    fn new () -> AdaptonFromScratch {
        AdaptonFromScratch {
            store : Vec::new (),
        }
    }

    fn name_of_string (self:&mut AdaptonFromScratch, sym:String) -> Name { Name }
    fn name_of_u64 (self:&mut AdaptonFromScratch, sym:u64) -> Name { Name }
    fn name_pair (self: &mut AdaptonFromScratch, fst:Name, snd:Name) -> Name { Name }
    fn name_fork (self:&mut AdaptonFromScratch, nm:Name) -> (Name, Name) { (Name,Name) }
    fn ns<T,F> (self: &mut AdaptonFromScratch, nm:Name, body:F) -> T where F:FnOnce(&mut AdaptonFromScratch) -> T { body(self) }
    fn put<T:Eq> (self:&mut AdaptonFromScratch, x:T) -> Art<T,Loc> { Art::Rc(Rc::new(x)) }

    fn cell<T:Eq+Debug+Clone
        +'static // TODO-Later: Needed on T because of lifetime issues.
        >
        (self:&mut AdaptonFromScratch, nm:Name, val:T) -> MutArt<T,Loc> {
            panic!("")
        }

    fn set<T:Eq+Debug> (self:&mut AdaptonFromScratch, cell:MutArt<T,Loc>, val:T) {
        panic!("")
    }

    fn thunk<Arg:Eq+Hash+Debug+Clone+'static,Spurious:'static+Clone,Res:Eq+Debug+Clone+'static>
        (self:&mut AdaptonFromScratch,
         id:ArtIdChoice<Name>,
         prog_pt:ProgPt,
         fn_box:Rc<Box<Fn(&mut AdaptonFromScratch, Arg, Spurious) -> Res>>,
         arg:Arg, spurious:Spurious)
         -> Art<Res,Loc>
    {
        let producer : Box<Producer<Res>> =
            Box::new(App{prog_pt:prog_pt,
                         fn_box:fn_box,
                         arg:arg.clone(),
                         spurious:spurious.clone()});
        let producer : Box<Void> =
            unsafe {
                // This transmute is always safe: `Void`s have no information.
                // `force`'s transmute from `Void` to a `Producer<Res>` does require justification.
                // The justification is the phantom type `Res` in the `Art<Res,Loc>` type.
                transmute::<_,_>(producer)
            };
        self.store.push( producer );
        Art::Loc( Rc::new( self.store.len() ) )
    }

    fn force<Res:'static+Eq+Debug+Clone> (self:&mut AdaptonFromScratch,
                                          art:&Art<Res,Loc>) -> Res
    {
        match *art {
            Art::Loc(ref index) => {
                let producer = {
                    let producer : &Box<Void> = & self.store[ **index ] ;
                    let producer : &Box<Producer<Res>> =
                        unsafe {
                            // This transmute is always safe:
                            // The justification is the phantom type `Res` in the `Art<Res,Loc>` type.
                            transmute::<_,_>(producer)
                        };
                    producer.copy()
                };
                producer.produce(self)
            },
            Art::Rc(ref rc) => (**rc).clone(),
        }
    }
}

// Produce a value of type Res.
trait Producer<Res> {
    fn produce(self:&Self, st:&mut AdaptonFromScratch) -> Res;
    fn copy(self:&Self) -> Box<Producer<Res>>;
}

#[derive(Clone)]
struct App<Arg,Spurious,Res> {
    prog_pt:  ProgPt,
    fn_box:   Rc<Box<Fn(&mut AdaptonFromScratch, Arg, Spurious) -> Res>>,
    arg:      Arg,
    spurious: Spurious,
}

impl<Arg,Spurious,Res> Debug for App<Arg,Spurious,Res> {
    fn fmt(&self, f: &mut Formatter) -> Result { self.prog_pt.fmt(f) }
}

impl<Arg:Hash,Spurious,Res> Hash for App<Arg,Spurious,Res> {
    fn hash<H>(&self, state: &mut H) where H: Hasher { (&self.prog_pt,&self.arg).hash(state) }
}

impl<Arg:'static+PartialEq+Eq+Clone,Spurious:'static+Clone,Res:'static> Producer<Res>
    for App<Arg,Spurious,Res>
{
    fn produce(self:&Self, st:&mut AdaptonFromScratch) -> Res {
        let f = self.fn_box.clone() ;
        f (st,self.arg.clone(),self.spurious.clone())
    }
    fn copy(self:&Self) -> Box<Producer<Res>> {
        Box::new(App{
            prog_pt:self.prog_pt.clone(),
            fn_box:self.fn_box.clone(),
            arg:self.arg.clone(),
            spurious:self.spurious.clone(),
        })
    }
}
<|endoftext|>


<commit_before>use compact::CVec;
use kay::World;
use planning::plan_manager::{PlanManagerID, PlanManager, Intent, IntentProgress};
use super::{RoadIntent, RoadSelections, SelectableStrokeRef};
use super::super::road_plan::RoadPlanDelta;
use super::super::materialized_roads::BuiltStrokes;
use stagemaster::combo::{Bindings, Combo2};
use stagemaster::user_interface::Event3d;
use descartes::{N, P2, V2, FiniteCurve, Norm};
use rand::{Rng, thread_rng};

use super::helper_interactables::{DeselecterID, AddableID, DraggableID, SelectableID,
                                  StrokeCanvasID, StrokeState};

#[derive(Compact, Clone)]
pub struct RoadInteraction {
    selectables: CVec<SelectableID>,
    addables: CVec<AddableID>,
    draggables: CVec<DraggableID>,
    pub stroke_canvas: StrokeCanvasID,
    deselecter: Option<DeselecterID>,
}

pub fn default_road_planning_bindings() -> Vec<(&'static str, Combo2)> {
    use stagemaster::combo::Button::*;

    vec![
        ("Create Rural Roads", Combo2::new(&[R], &[])),
        ("Create Small Grid", Combo2::new(&[G], &[])),
        ("Create Large Grid", Combo2::new(&[LShift, G], &[])),
        ("Delete Selection", Combo2::new(&[Back], &[Delete])),
    ]
}

use monet::{RendererID, EyeListener, Eye, Movement, EyeListenerID};
use stagemaster::UserInterfaceID;

impl RoadInteraction {
    pub fn init(
        world: &mut World,
        renderer_id: RendererID,
        user_interface: UserInterfaceID,
        plan_manager: PlanManagerID,
    ) -> RoadInteraction {
        renderer_id.add_eye_listener(0, plan_manager.into(), world);
        RoadInteraction {
            selectables: CVec::new(),
            addables: CVec::new(),
            draggables: CVec::new(),
            stroke_canvas: StrokeCanvasID::spawn(user_interface, plan_manager, world),
            deselecter: None,
        }
    }
}

impl EyeListener for PlanManager {
    fn eye_moved(&mut self, eye: Eye, _movement: Movement, _: &mut World) {
        if eye.position.z < 100.0 {
            self.settings.select_parallel = false;
            self.settings.select_opposite = false;
        } else if eye.position.z < 130.0 {
            self.settings.select_parallel = true;
            self.settings.select_opposite = false;
        } else {
            self.settings.select_parallel = true;
            self.settings.select_opposite = true;
        }
    }
}

impl PlanManager {
    pub fn on_stroke(&mut self, points: &CVec<P2>, state: StrokeState, _: &mut World) {
        let maybe_new_intent = match self.current.intent {
            Intent::RoadIntent(RoadIntent::ContinueRoad(ref continue_from,
                                                        _,
                                                        start_reference_point)) => {
                Some(Intent::RoadIntent(RoadIntent::ContinueRoad(
                    continue_from.clone(),
                    points.clone(),
                    start_reference_point,
                )))
            }
            Intent::None |
            Intent::RoadIntent(..) => {
                if points.len() >= 2 {
                    self.invalidate_interactables();
                    Some(Intent::RoadIntent(RoadIntent::NewRoad(points.clone())))
                } else {
                    None
                }
            }
        };
        if let Some(new_intent) = maybe_new_intent {
            self.current.intent = new_intent;
            match state {
                StrokeState::Preview => {
                    self.invalidate_preview();
                }
                StrokeState::Intermediate => {
                    self.commit_substep();
                }
                StrokeState::Finished => {
                    self.commit();
                }
            }

        }
    }

    pub fn set_n_lanes(&mut self, n_lanes: usize, _: &mut World) {
        self.settings.n_lanes_per_side = n_lanes;
        self.invalidate_preview();
    }

    pub fn toggle_both_sides(&mut self, _: &mut World) {
        self.settings.create_both_sides = !self.settings.create_both_sides;
        self.invalidate_preview();
    }
}

impl RoadInteraction {
    #[allow(too_many_arguments)]
    pub fn update_interactables(
        &mut self,
        world: &mut World,
        intent: Option<&RoadIntent>,
        road_delta: &RoadPlanDelta,
        selections: &RoadSelections,
        built_strokes_after_delta: &BuiltStrokes,
        user_interface: UserInterfaceID,
        plan_manager: PlanManagerID,
    ) {
        for selectable in self.selectables.drain() {
            selectable.clear(user_interface, world);
        }
        for draggable in self.selectables.drain() {
            draggable.clear(user_interface, world);
        }
        for addable in self.selectables.drain() {
            addable.clear(user_interface, world);
        }
        if let Some(deselecter) = self.deselecter.take() {
            deselecter.clear(user_interface, world);
        }

        self.deselecter = if selections.is_empty() {
            None
        } else {
            Some(DeselecterID::spawn(user_interface, plan_manager, world))
        };

        match intent {
            Some(&RoadIntent::ContinueRoad(..)) |
            Some(&RoadIntent::NewRoad(..)) |
            Some(&RoadIntent::ContinueRoadAround(..)) => {}
            _ => {
                for (i, stroke) in road_delta.new_strokes.iter().enumerate() {
                    self.selectables.push(SelectableID::spawn(
                        SelectableStrokeRef::New(i),
                        stroke.path().clone(),
                        user_interface,
                        plan_manager,
                        world,
                    ));
                }
                for (old_stroke_ref, stroke) in built_strokes_after_delta.mapping.pairs() {
                    self.selectables.push(SelectableID::spawn(
                        SelectableStrokeRef::Built(*old_stroke_ref),
                        stroke.path().clone(),
                        user_interface,
                        plan_manager,
                        world,
                    ));
                }
            }
        }
        for (&selection_ref, &(start, end)) in selections.pairs() {
            let stroke = selection_ref.get_stroke(road_delta, built_strokes_after_delta);
            if let Some(subsection) = stroke.path().subsection(start, end) {
                self.draggables.push(DraggableID::spawn(
                    selection_ref,
                    subsection.clone(),
                    user_interface,
                    plan_manager,
                    world,
                ));
                if let Some(next_lane_path) = subsection.shift_orthogonally(5.0) {
                    self.addables.push(AddableID::spawn(
                        next_lane_path,
                        user_interface,
                        plan_manager,
                        world,
                    ));
                }
            }
        }
    }

    pub fn on_step(&mut self, world: &mut World, intent: Option<&RoadIntent>) {
        let points = match intent {
            Some(&RoadIntent::ContinueRoad(_, ref points, _)) |
            Some(&RoadIntent::NewRoad(ref points)) => points.clone(),
            _ => CVec::new(),
        };

        self.stroke_canvas.set_points(points, world);
    }

    pub fn handle_event(
        &mut self,
        world: &mut World,
        plan_manager: PlanManagerID,
        event: Event3d,
        bindings: &Bindings,
    ) {
        match event {
            Event3d::Combos(combos) => {
                let maybe_grid_size = if bindings["Create Large Grid"].is_freshly_in(&combos) {
                    Some(15usize)
                } else if bindings["Create Small Grid"].is_freshly_in(&combos) {
                    Some(10usize)
                } else {
                    None
                };

                if let Some(grid_size) = maybe_grid_size {
                    const GRID_SPACING: N = 1000.0;
                    let half_grid_extent = grid_size as f32 * GRID_SPACING / 2.0;
                    for x in 0..grid_size {
                        plan_manager.on_stroke(
                            vec![
                                P2::new(
                                    (x as f32 + 0.5) * GRID_SPACING - half_grid_extent,
                                    -half_grid_extent
                                ),
                                P2::new(
                                    (x as f32 + 0.5) * GRID_SPACING - half_grid_extent,
                                    grid_size as f32 * GRID_SPACING - half_grid_extent
                                ),
                            ].into(),
                            StrokeState::Finished,
                            world,
                        );

                    }
                    for y in 0..grid_size {
                        plan_manager.on_stroke(
                            vec![
                                P2::new(
                                    -half_grid_extent,
                                    (y as f32 + 0.5) * GRID_SPACING - half_grid_extent
                                ),
                                P2::new(
                                    grid_size as f32 * GRID_SPACING - half_grid_extent,
                                    (y as f32 + 0.5) * GRID_SPACING - half_grid_extent
                                ),
                            ].into(),
                            StrokeState::Finished,
                            world,
                        );
                    }
                }

                if bindings["Create Rural Roads"].is_freshly_in(&combos) {
                    let mut rnd = thread_rng();
                    let mut start_point = P2::new(
                        rnd.gen_range(-2000.0, 2000.0),
                        rnd.gen_range(-2000.0, 2000.0),
                    );
                    let mut rough_angle = rnd.next_f32() * 2.0 * ::std::f32::consts::PI;
                    for i in 0..rnd.gen_range(15, 30) {
                        let is_major = i < 6;
                        plan_manager.set_n_lanes(
                            if is_major {
                                2
                            } else {
                                *rnd.choose(&[1, 2]).unwrap()
                            },
                            world,
                        );
                        rough_angle += rnd.gen_range(0.4, 0.6) * ::std::f32::consts::PI;
                        let mut rough_direction = V2::new(rough_angle.sin(), rough_angle.cos());
                        let mut points = Vec::new();
                        let length = if is_major {
                            rnd.gen_range(6000.0, 8000.0)
                        } else {
                            rnd.gen_range(300.0, 2000.0)
                        };
                        let mut point = start_point -
                            rnd.gen_range(0.0, 0.5) * length * rough_direction;
                        for p in 0..rnd.gen_range(10, 15) {
                            points.push(point);
                            point += length / 10.0 * rough_direction;
                            if p == 9 {
                                start_point = point;
                            }
                            if rnd.gen_weighted_bool(7) {
                                rough_angle += rnd.gen_range(-0.1, 0.1);
                                rough_direction = V2::new(rough_angle.sin(), rough_angle.cos());
                            }
                        }
                        plan_manager.on_stroke(points.into(), StrokeState::Finished, world);
                    }
                    plan_manager.set_n_lanes(2, world);
                }

                if bindings["Delete Selection"].is_freshly_in(&combos) {
                    plan_manager.change_intent(
                        Intent::RoadIntent(RoadIntent::DeleteSelection),
                        IntentProgress::Immediate,
                        world,
                    );
                }
            }
            Event3d::ButtonDown(::stagemaster::combo::Button::NumberKey(num)) => {
                if num == 0 {
                    plan_manager.toggle_both_sides(world);
                } else {
                    plan_manager.set_n_lanes(num as usize, world);
                }
            }
            _ => {}
        };
    }
}

mod kay_auto;
pub use self::kay_auto::*;
<commit_msg>Better village generation<commit_after>use compact::CVec;
use kay::World;
use planning::plan_manager::{PlanManagerID, PlanManager, Intent, IntentProgress};
use super::{RoadIntent, RoadSelections, SelectableStrokeRef};
use super::super::road_plan::RoadPlanDelta;
use super::super::materialized_roads::BuiltStrokes;
use stagemaster::combo::{Bindings, Combo2};
use stagemaster::user_interface::Event3d;
use descartes::{N, P2, V2, FiniteCurve, Norm};
use rand::{Rng, thread_rng};

use super::helper_interactables::{DeselecterID, AddableID, DraggableID, SelectableID,
                                  StrokeCanvasID, StrokeState};

#[derive(Compact, Clone)]
pub struct RoadInteraction {
    selectables: CVec<SelectableID>,
    addables: CVec<AddableID>,
    draggables: CVec<DraggableID>,
    pub stroke_canvas: StrokeCanvasID,
    deselecter: Option<DeselecterID>,
}

pub fn default_road_planning_bindings() -> Vec<(&'static str, Combo2)> {
    use stagemaster::combo::Button::*;

    vec![
        ("Create Rural Roads", Combo2::new(&[R], &[])),
        ("Create Small Grid", Combo2::new(&[G], &[])),
        ("Create Large Grid", Combo2::new(&[LShift, G], &[])),
        ("Delete Selection", Combo2::new(&[Back], &[Delete])),
    ]
}

use monet::{RendererID, EyeListener, Eye, Movement, EyeListenerID};
use stagemaster::UserInterfaceID;

impl RoadInteraction {
    pub fn init(
        world: &mut World,
        renderer_id: RendererID,
        user_interface: UserInterfaceID,
        plan_manager: PlanManagerID,
    ) -> RoadInteraction {
        renderer_id.add_eye_listener(0, plan_manager.into(), world);
        RoadInteraction {
            selectables: CVec::new(),
            addables: CVec::new(),
            draggables: CVec::new(),
            stroke_canvas: StrokeCanvasID::spawn(user_interface, plan_manager, world),
            deselecter: None,
        }
    }
}

impl EyeListener for PlanManager {
    fn eye_moved(&mut self, eye: Eye, _movement: Movement, _: &mut World) {
        if eye.position.z < 100.0 {
            self.settings.select_parallel = false;
            self.settings.select_opposite = false;
        } else if eye.position.z < 130.0 {
            self.settings.select_parallel = true;
            self.settings.select_opposite = false;
        } else {
            self.settings.select_parallel = true;
            self.settings.select_opposite = true;
        }
    }
}

impl PlanManager {
    pub fn on_stroke(&mut self, points: &CVec<P2>, state: StrokeState, _: &mut World) {
        let maybe_new_intent = match self.current.intent {
            Intent::RoadIntent(RoadIntent::ContinueRoad(ref continue_from,
                                                        _,
                                                        start_reference_point)) => {
                Some(Intent::RoadIntent(RoadIntent::ContinueRoad(
                    continue_from.clone(),
                    points.clone(),
                    start_reference_point,
                )))
            }
            Intent::None |
            Intent::RoadIntent(..) => {
                if points.len() >= 2 {
                    self.invalidate_interactables();
                    Some(Intent::RoadIntent(RoadIntent::NewRoad(points.clone())))
                } else {
                    None
                }
            }
        };
        if let Some(new_intent) = maybe_new_intent {
            self.current.intent = new_intent;
            match state {
                StrokeState::Preview => {
                    self.invalidate_preview();
                }
                StrokeState::Intermediate => {
                    self.commit_substep();
                }
                StrokeState::Finished => {
                    self.commit();
                }
            }

        }
    }

    pub fn set_n_lanes(&mut self, n_lanes: usize, _: &mut World) {
        self.settings.n_lanes_per_side = n_lanes;
        self.invalidate_preview();
    }

    pub fn toggle_both_sides(&mut self, _: &mut World) {
        self.settings.create_both_sides = !self.settings.create_both_sides;
        self.invalidate_preview();
    }
}

impl RoadInteraction {
    #[allow(too_many_arguments)]
    pub fn update_interactables(
        &mut self,
        world: &mut World,
        intent: Option<&RoadIntent>,
        road_delta: &RoadPlanDelta,
        selections: &RoadSelections,
        built_strokes_after_delta: &BuiltStrokes,
        user_interface: UserInterfaceID,
        plan_manager: PlanManagerID,
    ) {
        for selectable in self.selectables.drain() {
            selectable.clear(user_interface, world);
        }
        for draggable in self.selectables.drain() {
            draggable.clear(user_interface, world);
        }
        for addable in self.selectables.drain() {
            addable.clear(user_interface, world);
        }
        if let Some(deselecter) = self.deselecter.take() {
            deselecter.clear(user_interface, world);
        }

        self.deselecter = if selections.is_empty() {
            None
        } else {
            Some(DeselecterID::spawn(user_interface, plan_manager, world))
        };

        match intent {
            Some(&RoadIntent::ContinueRoad(..)) |
            Some(&RoadIntent::NewRoad(..)) |
            Some(&RoadIntent::ContinueRoadAround(..)) => {}
            _ => {
                for (i, stroke) in road_delta.new_strokes.iter().enumerate() {
                    self.selectables.push(SelectableID::spawn(
                        SelectableStrokeRef::New(i),
                        stroke.path().clone(),
                        user_interface,
                        plan_manager,
                        world,
                    ));
                }
                for (old_stroke_ref, stroke) in built_strokes_after_delta.mapping.pairs() {
                    self.selectables.push(SelectableID::spawn(
                        SelectableStrokeRef::Built(*old_stroke_ref),
                        stroke.path().clone(),
                        user_interface,
                        plan_manager,
                        world,
                    ));
                }
            }
        }
        for (&selection_ref, &(start, end)) in selections.pairs() {
            let stroke = selection_ref.get_stroke(road_delta, built_strokes_after_delta);
            if let Some(subsection) = stroke.path().subsection(start, end) {
                self.draggables.push(DraggableID::spawn(
                    selection_ref,
                    subsection.clone(),
                    user_interface,
                    plan_manager,
                    world,
                ));
                if let Some(next_lane_path) = subsection.shift_orthogonally(5.0) {
                    self.addables.push(AddableID::spawn(
                        next_lane_path,
                        user_interface,
                        plan_manager,
                        world,
                    ));
                }
            }
        }
    }

    pub fn on_step(&mut self, world: &mut World, intent: Option<&RoadIntent>) {
        let points = match intent {
            Some(&RoadIntent::ContinueRoad(_, ref points, _)) |
            Some(&RoadIntent::NewRoad(ref points)) => points.clone(),
            _ => CVec::new(),
        };

        self.stroke_canvas.set_points(points, world);
    }

    pub fn handle_event(
        &mut self,
        world: &mut World,
        plan_manager: PlanManagerID,
        event: Event3d,
        bindings: &Bindings,
    ) {
        match event {
            Event3d::Combos(combos) => {
                let maybe_grid_size = if bindings["Create Large Grid"].is_freshly_in(&combos) {
                    Some(15usize)
                } else if bindings["Create Small Grid"].is_freshly_in(&combos) {
                    Some(10usize)
                } else {
                    None
                };

                if let Some(grid_size) = maybe_grid_size {
                    const GRID_SPACING: N = 1000.0;
                    let half_grid_extent = grid_size as f32 * GRID_SPACING / 2.0;
                    for x in 0..grid_size {
                        plan_manager.on_stroke(
                            vec![
                                P2::new(
                                    (x as f32 + 0.5) * GRID_SPACING - half_grid_extent,
                                    -half_grid_extent
                                ),
                                P2::new(
                                    (x as f32 + 0.5) * GRID_SPACING - half_grid_extent,
                                    grid_size as f32 * GRID_SPACING - half_grid_extent
                                ),
                            ].into(),
                            StrokeState::Finished,
                            world,
                        );

                    }
                    for y in 0..grid_size {
                        plan_manager.on_stroke(
                            vec![
                                P2::new(
                                    -half_grid_extent,
                                    (y as f32 + 0.5) * GRID_SPACING - half_grid_extent
                                ),
                                P2::new(
                                    grid_size as f32 * GRID_SPACING - half_grid_extent,
                                    (y as f32 + 0.5) * GRID_SPACING - half_grid_extent
                                ),
                            ].into(),
                            StrokeState::Finished,
                            world,
                        );
                    }
                }

                if bindings["Create Rural Roads"].is_freshly_in(&combos) {
                    let mut rnd = thread_rng();

                    let mut start_points = vec![
                        P2::new(
                            rnd.gen_range(-2000.0, 2000.0),
                            rnd.gen_range(-2000.0, 2000.0)
                        ),
                    ];
                    let mut rough_angle = rnd.next_f32() * 2.0 * ::std::f32::consts::PI;
                    let mut length = rnd.gen_range(9000.0, 12_000.0);

                    for i in 0..6 {
                        let mut new_start_points = vec![];

                        for start_point in &start_points {
                            let length_here = length * rnd.gen_range(0.8, 1.2);
                            let mut rough_angle_here = rough_angle + rnd.gen_range(-0.1, 0.1);
                            let mut rough_direction_here =
                                V2::new(rough_angle_here.sin(), rough_angle_here.cos());
                            plan_manager.set_n_lanes(
                                if length_here < 600.0 { 1 } else { 2 },
                                world,
                            );

                            let mut point = *start_point -
                                rnd.gen_range(0.4, 0.6) * length_here * rough_direction_here;
                            let mut points = Vec::new();

                            for p in 0..6 {
                                points.push(point);
                                if (i == 0 && p == 3) || (i > 0 && (p == 1 || p == 4)) {
                                    new_start_points.push(point);
                                }
                                point += length_here / 4.0 * rough_direction_here;

                                // rough_angle_here += rnd.gen_range(-0.1, 0.1);
                                // rough_direction_here =
                                //     V2::new(rough_angle_here.sin(), rough_angle_here.cos());
                            }

                            plan_manager.on_stroke(points.into(), StrokeState::Finished, world);
                        }

                        length *= rnd.gen_range(0.4, 0.55);
                        rough_angle += rnd.gen_range(0.47, 0.53) * ::std::f32::consts::PI;
                        start_points = new_start_points;
                    }
                }

                if bindings["Delete Selection"].is_freshly_in(&combos) {
                    plan_manager.change_intent(
                        Intent::RoadIntent(RoadIntent::DeleteSelection),
                        IntentProgress::Immediate,
                        world,
                    );
                }
            }
            Event3d::ButtonDown(::stagemaster::combo::Button::NumberKey(num)) => {
                if num == 0 {
                    plan_manager.toggle_both_sides(world);
                } else {
                    plan_manager.set_n_lanes(num as usize, world);
                }
            }
            _ => {}
        };
    }
}

mod kay_auto;
pub use self::kay_auto::*;
<|endoftext|>


<commit_before>use crate::surface_utils::compare_surfaces::{compare_surfaces, BufferDiff, Diff};
use crate::surface_utils::shared_surface::{SharedImageSurface, SurfaceType};

use std::convert::TryFrom;
use std::env;
use std::fs::{self, File};
use std::io::BufReader;
use std::path::PathBuf;
use std::sync::Once;

fn tolerable_difference() -> u8 {
    static mut TOLERANCE: u8 = 2;

    static ONCE: Once = Once::new();
    ONCE.call_once(|| unsafe {
        if let Ok(str) = env::var("RSVG_TEST_TOLERANCE") {
            let value: usize = str
                .parse()
                .expect("Can not parse RSVG_TEST_TOLERANCE as a number");
            TOLERANCE =
                u8::try_from(value).expect("RSVG_TEST_TOLERANCE should be between 0 and 255");
        }
    });

    unsafe { TOLERANCE }
}

trait Deviation {
    fn distinguishable(&self) -> bool;
    fn inacceptable(&self) -> bool;
}

impl Deviation for Diff {
    fn distinguishable(&self) -> bool {
        self.max_diff > 2
    }

    fn inacceptable(&self) -> bool {
        self.max_diff > tolerable_difference()
    }
}

pub fn output_dir() -> PathBuf {
    let path = PathBuf::from(
        env::var_os("OUT_DIR")
            .expect(r#"OUT_DIR is not set, please set it or run under "cargo test""#),
    );

    fs::create_dir_all(&path).expect("could not create output directory for tests");

    path
}

// FIXME: proper errors?
fn load_png_as_argb(path: &PathBuf) -> Result<cairo::ImageSurface, ()> {
    let file = File::open(path).map_err(|_| ())?;

    let mut reference_file = BufReader::new(file);

    let png = cairo::ImageSurface::create_from_png(&mut reference_file).map_err(|_| ())?;
    let argb =
        cairo::ImageSurface::create(cairo::Format::ARgb32, png.get_width(), png.get_height())
            .map_err(|_| ())?;

    {
        // convert to ARGB; the PNG may come as Rgb24
        let cr = cairo::Context::new(&argb);
        cr.set_source_surface(&png, 0.0, 0.0);
        cr.paint();
    }

    Ok(argb)
}

pub fn compare_to_file(
    output_surf: &SharedImageSurface,
    output_base_name: &str,
    reference_path: &PathBuf,
) {
    let png = load_png_as_argb(reference_path).unwrap();
    let reference_surf = SharedImageSurface::wrap(png, SurfaceType::SRgb).unwrap();

    compare_to_surface(output_surf, &reference_surf, output_base_name);
}

pub fn compare_to_surface(
    output_surf: &SharedImageSurface,
    reference_surf: &SharedImageSurface,
    output_base_name: &str,
) {
    let output_path = output_dir().join(&format!("{}-out.png", output_base_name));

    println!("output: {}", output_path.to_string_lossy());

    let mut output_file = File::create(output_path).unwrap();
    output_surf
        .clone()
        .into_image_surface()
        .unwrap()
        .write_to_png(&mut output_file)
        .unwrap();

    let diff = compare_surfaces(output_surf, reference_surf).unwrap();
    evaluate_diff(&diff, output_base_name);
}

fn evaluate_diff(diff: &BufferDiff, output_base_name: &str) {
    match diff {
        BufferDiff::DifferentSizes => unreachable!("surfaces should be of the same size"),

        BufferDiff::Diff(diff) => {
            if diff.distinguishable() {
                println!(
                    "{}: {} pixels changed with maximum difference of {}",
                    output_base_name, diff.num_pixels_changed, diff.max_diff,
                );
                save_diff(diff, output_base_name);

                if diff.inacceptable() {
                    panic!("surfaces are too different");
                }
            }
        }
    }

    fn save_diff(diff: &Diff, output_base_name: &str) {
        let surf = diff.surface.clone().into_image_surface().unwrap();
        let diff_path = output_dir().join(&format!("{}-diff.png", output_base_name));
        println!("diff: {}", diff_path.to_string_lossy());

        let mut output_file = File::create(diff_path).unwrap();
        surf.write_to_png(&mut output_file).unwrap();
    }
}
<commit_msg>Add a little documentation for the test_utils<commit_after>//! Utilities for the test suite.
//!
//! This module has utility functions that are used in the test suite
//! to compare rendered surfaces to reference images.

use crate::surface_utils::compare_surfaces::{compare_surfaces, BufferDiff, Diff};
use crate::surface_utils::shared_surface::{SharedImageSurface, SurfaceType};

use std::convert::TryFrom;
use std::env;
use std::fs::{self, File};
use std::io::BufReader;
use std::path::PathBuf;
use std::sync::Once;

fn tolerable_difference() -> u8 {
    static mut TOLERANCE: u8 = 2;

    static ONCE: Once = Once::new();
    ONCE.call_once(|| unsafe {
        if let Ok(str) = env::var("RSVG_TEST_TOLERANCE") {
            let value: usize = str
                .parse()
                .expect("Can not parse RSVG_TEST_TOLERANCE as a number");
            TOLERANCE =
                u8::try_from(value).expect("RSVG_TEST_TOLERANCE should be between 0 and 255");
        }
    });

    unsafe { TOLERANCE }
}

trait Deviation {
    fn distinguishable(&self) -> bool;
    fn inacceptable(&self) -> bool;
}

impl Deviation for Diff {
    fn distinguishable(&self) -> bool {
        self.max_diff > 2
    }

    fn inacceptable(&self) -> bool {
        self.max_diff > tolerable_difference()
    }
}

/// Creates a directory from the `OUT_DIR` environment variable and returns its path.
///
/// # Panics
///
/// Will panic if the `OUT_DIR` environment variable is not set.  Normally this is set
/// by the continuous integration scripts or the build scripts that run the test suite.
pub fn output_dir() -> PathBuf {
    let path = PathBuf::from(
        env::var_os("OUT_DIR")
            .expect(r#"OUT_DIR is not set, please set it to a directory where the test suite can write its output"#),
    );

    fs::create_dir_all(&path).expect("could not create output directory for tests");

    path
}

// FIXME: proper errors?
fn load_png_as_argb(path: &PathBuf) -> Result<cairo::ImageSurface, ()> {
    let file = File::open(path).map_err(|_| ())?;

    let mut reference_file = BufReader::new(file);

    let png = cairo::ImageSurface::create_from_png(&mut reference_file).map_err(|_| ())?;
    let argb =
        cairo::ImageSurface::create(cairo::Format::ARgb32, png.get_width(), png.get_height())
            .map_err(|_| ())?;

    {
        // convert to ARGB; the PNG may come as Rgb24
        let cr = cairo::Context::new(&argb);
        cr.set_source_surface(&png, 0.0, 0.0);
        cr.paint();
    }

    Ok(argb)
}

/// Compares `output_surf` to the reference image from `reference_path`.
///
/// Loads the image stored at `reference_path` and uses `compare_to_surface` to
/// do the comparison.  See that function for details.
///
/// # Panics
///
/// See `compare_to_surface` for information; this function compares the images and panics in the
/// same way as that function upon encountering differences.
pub fn compare_to_file(
    output_surf: &SharedImageSurface,
    output_base_name: &str,
    reference_path: &PathBuf,
) {
    let png = load_png_as_argb(reference_path).unwrap();
    let reference_surf = SharedImageSurface::wrap(png, SurfaceType::SRgb).unwrap();

    compare_to_surface(output_surf, &reference_surf, output_base_name);
}

/// Compares two surfaces and panics if they are too different.
///
/// The `output_base_name` is used to write test results if the
/// surfaces are different.  If this is `foo`, this will write
/// `foo-out.png` with the `output_surf` and `foo-diff.png` with a
/// visual diff between `output_surf` and `reference_surf`.
///
/// # Panics
///
/// Will panic if the surfaces are too different to be acceptable.
pub fn compare_to_surface(
    output_surf: &SharedImageSurface,
    reference_surf: &SharedImageSurface,
    output_base_name: &str,
) {
    let output_path = output_dir().join(&format!("{}-out.png", output_base_name));

    println!("output: {}", output_path.to_string_lossy());

    let mut output_file = File::create(output_path).unwrap();
    output_surf
        .clone()
        .into_image_surface()
        .unwrap()
        .write_to_png(&mut output_file)
        .unwrap();

    let diff = compare_surfaces(output_surf, reference_surf).unwrap();
    evaluate_diff(&diff, output_base_name);
}

fn evaluate_diff(diff: &BufferDiff, output_base_name: &str) {
    match diff {
        BufferDiff::DifferentSizes => unreachable!("surfaces should be of the same size"),

        BufferDiff::Diff(diff) => {
            if diff.distinguishable() {
                println!(
                    "{}: {} pixels changed with maximum difference of {}",
                    output_base_name, diff.num_pixels_changed, diff.max_diff,
                );
                save_diff(diff, output_base_name);

                if diff.inacceptable() {
                    panic!("surfaces are too different");
                }
            }
        }
    }

    fn save_diff(diff: &Diff, output_base_name: &str) {
        let surf = diff.surface.clone().into_image_surface().unwrap();
        let diff_path = output_dir().join(&format!("{}-diff.png", output_base_name));
        println!("diff: {}", diff_path.to_string_lossy());

        let mut output_file = File::create(diff_path).unwrap();
        surf.write_to_png(&mut output_file).unwrap();
    }
}
<|endoftext|>