<commit_before>//!
//! The script interpreter runs scripts directly from their parse tree. This is
//! a fairly slow way to run scripts but comparatively simple to implement.
//!

use std::result::Result;
use serde_json::*;

use gossyp_base::*;
use gossyp_base::basic::*;

use super::script::Script;
use super::evaluate_statement::evaluate_statement;
use super::bind_statement::bind_statement;

///
/// A tool representing a script that will be interepreted
///
pub struct InterpretedScriptTool {
    statements: Script
}

///
/// Script evaluation error
///
#[derive(Serialize, Deserialize)]
pub enum ScriptEvaluationError {
    /// Tried to evaluate an expression type that's not implemented yet
    ExpressionNotImplemented,

    /// Tried to evaluate a statement type that's not implemented yet
    StatementNotImplemented,

    /// Tried to look up a tool and it couldn't be found
    ToolNameNotFound,

    /// Found an expression that can't be treated as a tool where a tool name was expected
    ExpressionDoesNotEvaluateToTool,

    /// Expressions used as keys in a map must evaluate to a string
    MapKeysMustEvaluateToAString,

    /// In index expression like foo[bar], foo must be either an array, a string or a map
    IndexMustApplyToAnArrayOrAMap,

    /// When indexing an array or a string, the index must be a number
    ArrayIndexMustBeANumber,

    /// When indexing a map, the index must be a string
    MapIndexMustBeAString,

    /// When indexing an array, the index must be in the array bounds
    IndexOutOfBounds,

    /// Object value is not present in a map
    ObjectValueNotPresent,

    /// In a field access (a.b), the '.b' part must be an identifier
    FieldMustBeIdentifier
}

impl InterpretedScriptTool {
    ///
    /// Creates a tool that can evaluate a script
    ///
    pub fn new_script_eval_tool() -> Box<Tool> {
        Box::new(make_dynamic_tool(|script: Vec<Script>, environment: &Environment| {
            let script_tool = InterpretedScriptTool::from_statements(script);

            script_tool.invoke_json(Value::Null, environment)
        }))
    }

    ///
    /// Creates a new interpreted script tool from a set of statements
    ///
    pub fn from_statements(statements: Vec<Script>) -> InterpretedScriptTool {
        InterpretedScriptTool { statements: Script::Sequence(statements) }
    }
}

impl Tool for InterpretedScriptTool {
    fn invoke_json(&self, _input: Value, environment: &Environment) -> Result<Value, Value> {
        // Make the environment that this script will run in
        let mut script_environment = ScriptExecutionEnvironment::new(environment);

        // Bind the values contained within the script
        let bound_script = bind_statement(&self.statements, &mut script_environment)?;

        // Evaluate them
        evaluate_statement(&bound_script, &mut script_environment)
    }
}

///
/// Represents an execution environment for a running script
///
pub struct ScriptExecutionEnvironment<'a> {
    /// Current values of the variables in this environment
    variable_values: Vec<Value>,

    /// The environment where tools are drawn from
    parent_environment: &'a Environment
}

impl<'a> ScriptExecutionEnvironment<'a> {
    ///
    /// Creates a new script execution environment
    ///
    pub fn new(parent_environment: &'a Environment) -> ScriptExecutionEnvironment<'a> {
        ScriptExecutionEnvironment { variable_values: vec![], parent_environment: parent_environment }
    }

    ///
    /// Fetches a tool from this environment
    ///
    #[inline]
    pub fn get_json_tool(&self, tool: &str) -> Result<Box<Tool>, RetrieveToolError> {
        self.parent_environment.get_json_tool(tool)
    }

    ///
    /// Invokes a JSON tool in this environment
    ///
    #[inline]
    pub fn invoke_tool(&self, tool: &Box<Tool>, input: Value) -> Result<Value, Value> {
        tool.invoke_json(input, self.parent_environment)
    }
}
<commit_msg>Be more precise about the things we depend on<commit_after>//!
//! The script interpreter runs scripts directly from their parse tree. This is
//! a fairly slow way to run scripts but comparatively simple to implement.
//!

use std::result::Result;
use serde_json::*;

use gossyp_base::{Tool, Environment, RetrieveToolError};
use gossyp_base::basic::{make_dynamic_tool};

use super::script::Script;
use super::evaluate_statement::evaluate_statement;
use super::bind_statement::bind_statement;

///
/// A tool representing a script that will be interepreted
///
pub struct InterpretedScriptTool {
    statements: Script
}

///
/// Script evaluation error
///
#[derive(Serialize, Deserialize)]
pub enum ScriptEvaluationError {
    /// Tried to evaluate an expression type that's not implemented yet
    ExpressionNotImplemented,

    /// Tried to evaluate a statement type that's not implemented yet
    StatementNotImplemented,

    /// Tried to look up a tool and it couldn't be found
    ToolNameNotFound,

    /// Found an expression that can't be treated as a tool where a tool name was expected
    ExpressionDoesNotEvaluateToTool,

    /// Expressions used as keys in a map must evaluate to a string
    MapKeysMustEvaluateToAString,

    /// In index expression like foo[bar], foo must be either an array, a string or a map
    IndexMustApplyToAnArrayOrAMap,

    /// When indexing an array or a string, the index must be a number
    ArrayIndexMustBeANumber,

    /// When indexing a map, the index must be a string
    MapIndexMustBeAString,

    /// When indexing an array, the index must be in the array bounds
    IndexOutOfBounds,

    /// Object value is not present in a map
    ObjectValueNotPresent,

    /// In a field access (a.b), the '.b' part must be an identifier
    FieldMustBeIdentifier
}

impl InterpretedScriptTool {
    ///
    /// Creates a tool that can evaluate a script
    ///
    pub fn new_script_eval_tool() -> Box<Tool> {
        Box::new(make_dynamic_tool(|script: Vec<Script>, environment: &Environment| {
            let script_tool = InterpretedScriptTool::from_statements(script);

            script_tool.invoke_json(Value::Null, environment)
        }))
    }

    ///
    /// Creates a new interpreted script tool from a set of statements
    ///
    pub fn from_statements(statements: Vec<Script>) -> InterpretedScriptTool {
        InterpretedScriptTool { statements: Script::Sequence(statements) }
    }
}

impl Tool for InterpretedScriptTool {
    fn invoke_json(&self, _input: Value, environment: &Environment) -> Result<Value, Value> {
        // Make the environment that this script will run in
        let mut script_environment = ScriptExecutionEnvironment::new(environment);

        // Bind the values contained within the script
        let bound_script = bind_statement(&self.statements, &mut script_environment)?;

        // Evaluate them
        evaluate_statement(&bound_script, &mut script_environment)
    }
}

///
/// Represents an execution environment for a running script
///
pub struct ScriptExecutionEnvironment<'a> {
    /// Current values of the variables in this environment
    variable_values: Vec<Value>,

    /// The environment where tools are drawn from
    parent_environment: &'a Environment
}

impl<'a> ScriptExecutionEnvironment<'a> {
    ///
    /// Creates a new script execution environment
    ///
    pub fn new(parent_environment: &'a Environment) -> ScriptExecutionEnvironment<'a> {
        ScriptExecutionEnvironment { variable_values: vec![], parent_environment: parent_environment }
    }

    ///
    /// Fetches a tool from this environment
    ///
    #[inline]
    pub fn get_json_tool(&self, tool: &str) -> Result<Box<Tool>, RetrieveToolError> {
        self.parent_environment.get_json_tool(tool)
    }

    ///
    /// Invokes a JSON tool in this environment
    ///
    #[inline]
    pub fn invoke_tool(&self, tool: &Box<Tool>, input: Value) -> Result<Value, Value> {
        tool.invoke_json(input, self.parent_environment)
    }
}
<|endoftext|>


<commit_before>use std::time::{SystemTime, UNIX_EPOCH, Duration, Instant};
use tokio::timer::Interval;
use tokio::prelude::*;
use std::str::FromStr;
use std::sync::Arc;

use blockchain::*;
use constants;
use crypto::*;
use asset::*;
use tx::*;

pub struct Producer {
    chain: Arc<Blockchain>,
    minter: KeyPair,
    staker: PublicKey
}

impl Producer {

    pub fn new(chain: Arc<Blockchain>,
                minter: KeyPair,
                staker: PublicKey) -> Producer {
        Producer {
            chain,
            minter,
            staker
        }
    }

    pub fn start_timer(self) {
        let dur = Duration::from_millis(constants::BLOCK_PROD_TIME);
        let at = Instant::now() + dur;
        ::tokio::spawn(Interval::new(at, dur).for_each(move |_| {
            self.produce();
            Ok(())
        }).map_err(|err| {
            error!("Timer error in producer: {:?}", err);
        }));
    }

    fn produce(&self) {
        let timestamp = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs() as u32;
        let transactions = vec![
            TxVariant::RewardTx(RewardTx {
                base: Tx {
                    tx_type: TxType::REWARD,
                    fee: Asset::from_str("0 GOLD").unwrap(),
                    timestamp,
                    signature_pairs: Vec::new()
                },
                to: self.staker.clone(),
                rewards: vec![
                    Asset::from_str("1 GOLD").unwrap(),
                    Asset::from_str("100 SILVER").unwrap()
                ]
            })
        ];

        let head = self.chain.get_chain_head();
        let block = head.new_child(transactions).sign(&self.minter);

        let height = block.height;
        let tx_len = block.transactions.len();

        self.chain.insert_block(block).unwrap();
        info!("Produced block at height {} with {} txs", height, tx_len);
    }
}
<commit_msg>Distinguish singular and plural when logging block producer transactions<commit_after>use std::time::{SystemTime, UNIX_EPOCH, Duration, Instant};
use tokio::timer::Interval;
use tokio::prelude::*;
use std::str::FromStr;
use std::sync::Arc;

use blockchain::*;
use constants;
use crypto::*;
use asset::*;
use tx::*;

pub struct Producer {
    chain: Arc<Blockchain>,
    minter: KeyPair,
    staker: PublicKey
}

impl Producer {

    pub fn new(chain: Arc<Blockchain>,
                minter: KeyPair,
                staker: PublicKey) -> Producer {
        Producer {
            chain,
            minter,
            staker
        }
    }

    pub fn start_timer(self) {
        let dur = Duration::from_millis(constants::BLOCK_PROD_TIME);
        let at = Instant::now() + dur;
        ::tokio::spawn(Interval::new(at, dur).for_each(move |_| {
            self.produce();
            Ok(())
        }).map_err(|err| {
            error!("Timer error in producer: {:?}", err);
        }));
    }

    fn produce(&self) {
        let timestamp = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs() as u32;
        let transactions = vec![
            TxVariant::RewardTx(RewardTx {
                base: Tx {
                    tx_type: TxType::REWARD,
                    fee: Asset::from_str("0 GOLD").unwrap(),
                    timestamp,
                    signature_pairs: Vec::new()
                },
                to: self.staker.clone(),
                rewards: vec![
                    Asset::from_str("1 GOLD").unwrap(),
                    Asset::from_str("100 SILVER").unwrap()
                ]
            })
        ];

        let head = self.chain.get_chain_head();
        let block = head.new_child(transactions).sign(&self.minter);

        let height = block.height;
        let tx_len = block.transactions.len();

        self.chain.insert_block(block).unwrap();
        let txs = if tx_len == 1 { "tx" } else { "txs" };
        info!("Produced block at height {} with {} {}", height, tx_len, txs);
    }
}
<|endoftext|>


<commit_before>
extern crate libc;
extern crate time;
use std::{ptr};
use std::mem::{transmute};

#[cfg(target_os = "win32")]
mod win {
	pub struct Handle;
	pub struct SecurityAttributes;

	extern "system" {
		pub fn CreateFileW(filename: *u16, access: u32, share: u32, security: *SecurityAttributes, creation: u32, flags: u32, template: *Handle) -> *Handle;
		pub fn CreateFileMappingW(file: *Handle, security: *SecurityAttributes, protect: u32, sizehigh: u32, sizelow: u32, name: *u16) -> *Handle;
		pub fn MapViewOfFile(map: *Handle, access: u32, offsethigh: u32, offsetlow: u32, size: uint) -> *u8;
		pub fn GetLastError() -> u32;
	}

	pub static GENERIC_READ: u32 = 0x80000000;
	pub static FILE_SHARE_READ: u32 = 0x00000001;
	pub static OPEN_EXISTING: u32 = 3;
	pub static FILE_FLAG_RANDOM_ACCESS: u32 = 0x10000000;
	pub static PAGE_READONLY: u32 = 0x02;
	pub static SECTION_MAP_READ: u32 = 0x0004;
	pub static FILE_MAP_READ: u32 = SECTION_MAP_READ;
	pub static INVALID_HANDLE_VALUE: uint = -1;
}

#[cfg(target_os = "linux")]
mod posix {
	//Hi
}

struct File {
	data: *u8,
	header: *Header,
	nodetable: *NodeData,
	stringtable: *u64
}
impl File {
	#[cfg(target_os = "win32")]
	fn open(name: &Path) -> Option<File> {
		unsafe {
			let sname = name.as_str().unwrap().to_utf16();
			let handle = win::CreateFileW(sname.as_ptr(), win::GENERIC_READ, win::FILE_SHARE_READ, ptr::null(), win::OPEN_EXISTING, win::FILE_FLAG_RANDOM_ACCESS, ptr::null());
			if handle.to_uint() == win::INVALID_HANDLE_VALUE {
				return None;
			}
			let map = win::CreateFileMappingW(handle, ptr::null(), win::PAGE_READONLY, 0, 0, ptr::null());
			if map.is_null() {
				return None;
			}
			let data = win::MapViewOfFile(map, win::FILE_MAP_READ, 0, 0, 0);
			if data.is_null() {
				return None;
			}
			let header: *Header = transmute(data);
			if (*header).magic != 0x34474B50 {
				return None;
			}
			let file = File {
				data: data,
				header: header,
				nodetable: transmute(data.offset((*header).nodeoffset.to_int().unwrap())),
				stringtable: transmute(data.offset((*header).stringoffset.to_int().unwrap()))
			};
			return Some(file);
		}
	}
	#[cfg(target_os = "linux")]
	fn open(name: &Path) -> Option<File> {
		unsafe {
			//Someone fill this in please
		}
	}
	fn get_header(&self) -> &Header {
		unsafe {
			transmute(self.header)
		}
	}
	fn root<'a>(&'a self) -> Node<'a> {
		unsafe {
			Node {data: &*self.nodetable, file: self}
		}
	}
	fn get_str<'a>(&'a self, index: u32) -> Option<&'a str> {
		unsafe {
			let ptr = self.data.offset((*self.stringtable.offset(index.to_int().unwrap())).to_int().unwrap());
			let size: *u16 = transmute(ptr);
			std::slice::raw::buf_as_slice(ptr.offset(2), (*size).to_uint().unwrap(), |buf: &[u8]| -> Option<&'a str> {
				let bytes: &'a [u8] = transmute(buf);
				std::str::from_utf8(bytes)
			})
		}
	}
}
#[packed]
struct Header {
	magic: u32,
	nodecount: u32,
	nodeoffset: u64,
	stringcount: u32,
	stringoffset: u64,
	bitmapcount: u32,
	bitmapoffset: u64,
	audiocount: u32,
	audiooffset: u64
}
#[packed]
struct String {
	length: u16
}
struct Node<'a> {
	data: &'a NodeData,
	file: &'a File
}
impl<'a> Node<'a> {
	fn iter(&self) -> NodeIterator<'a> {
		unsafe {
			NodeIterator {data: self.file.nodetable.offset(self.data.children as int), count: self.data.count, file: self.file}
		}
	}
	fn name(&self) -> Option<&'a str> {
		self.file.get_str(self.data.name)
	}
	fn empty(&self) -> bool {
		self.data.count == 0
	}
}
struct NodeIterator<'a> {
	data: *NodeData,
	count: u16,
	file: &'a File
}
impl<'a> Iterator<Node<'a>> for NodeIterator<'a> {
	fn next(&mut self) -> Option<Node<'a>> {
		match self.count {
			0 => None,
			_ => {
				self.count -= 1;
				let node = Node {data: unsafe {&*self.data}, file: self.file};
				self.data = unsafe {self.data.offset(1)};
				Some(node)
			}
		}
	}
}
#[packed]
struct NodeData {
	name: u32,
	children: u32,
	count: u16,
	dtype: u16,
	data: u64
}
#[packed]
struct NodeInteger {
	value: i64
}
#[packed]
struct NodeFloat {
	value: f64
}
#[packed]
struct NodeString {
	index: u32,
	unused: u32
}
#[packed]
struct NodeVector {
	x: i32,
	y: i32
}
#[packed]
struct NodeBitmap {
	index: u32,
	width:  u16,
	height: u16
}
#[packed]
struct NodeAudio {
	index: u32,
	length: u32
}

fn recurse(node: Node) -> int {
	node.iter().fold(1, |a, b| a + recurse(b))
}

fn test(name: &str, func: |Node| -> int, node: Node) {
	for i in range(0, 10) {
		let begin = time::precise_time_ns();
		let answer = recurse(node);
		let end = time::precise_time_ns();
		println!("{}\t{}\t{}", name, (end - begin) / 1000, answer);
	}
}

fn main() {
	unsafe { ::std::rt::stack::record_sp_limit(0); }
	let file = File::open(&Path::new("Data.nx")).unwrap();
	test("Re", recurse, file.root());
}

<commit_msg>Do some cleanup and format code using rustc<commit_after>extern crate libc;
extern crate time;
use std::{ptr};
use std::mem::{transmute};
#[cfg(target_os = "win32")]
mod win {
    pub struct Handle;
    pub struct SecurityAttributes;
    extern "system" {
        pub fn CreateFileW(filename: *u16, access: u32, share: u32,
                           security: *SecurityAttributes, creation: u32,
                           flags: u32, template: *Handle) -> *Handle;
        pub fn CreateFileMappingW(file: *Handle,
                                  security: *SecurityAttributes, protect: u32,
                                  sizehigh: u32, sizelow: u32, name: *u16) ->
         *Handle;
        pub fn MapViewOfFile(map: *Handle, access: u32, offsethigh: u32,
                             offsetlow: u32, size: uint) -> *u8;
        pub fn GetLastError() -> u32;
    }
    pub static GENERIC_READ: u32 = 0x80000000;
    pub static FILE_SHARE_READ: u32 = 0x00000001;
    pub static OPEN_EXISTING: u32 = 3;
    pub static FILE_FLAG_RANDOM_ACCESS: u32 = 0x10000000;
    pub static PAGE_READONLY: u32 = 0x02;
    pub static SECTION_MAP_READ: u32 = 0x0004;
    pub static FILE_MAP_READ: u32 = SECTION_MAP_READ;
    pub static INVALID_HANDLE_VALUE: uint = -1;
}
#[cfg(target_os = "linux")]
mod posix {
    //Hi
}
struct File {
    data: *u8,
    header: *Header,
    nodetable: *NodeData,
    stringtable: *u64,
}
impl File {
    #[cfg(target_os = "win32")]
    fn open(name: &Path) -> Option<File> {
        unsafe {
            let sname = name.as_str().unwrap().to_utf16();
            let handle =
                win::CreateFileW(sname.as_ptr(), win::GENERIC_READ,
                                 win::FILE_SHARE_READ, ptr::null(),
                                 win::OPEN_EXISTING,
                                 win::FILE_FLAG_RANDOM_ACCESS, ptr::null());
            if handle.to_uint() == win::INVALID_HANDLE_VALUE { return None; }
            let map =
                win::CreateFileMappingW(handle, ptr::null(),
                                        win::PAGE_READONLY, 0, 0,
                                        ptr::null());
            if map.is_null() { return None; }
            let data = win::MapViewOfFile(map, win::FILE_MAP_READ, 0, 0, 0);
            if data.is_null() { return None; }
            let header: *Header = transmute(data);
            if (*header).magic != 0x34474B50 { return None; }
            let file =
                File{data: data,
                     header: header,
                     nodetable:
                         transmute(data.offset((*header).nodeoffset as int)),
                     stringtable:
                         transmute(data.offset((*header).stringoffset as
                                                   int)),};
            Some(file)
        }
    }
    #[cfg(target_os = "linux")]
    fn open(name: &Path) -> Option<File> {
        unsafe {
            //Someone fill this in please
        }
    }
    fn get_header(&self) -> &Header { unsafe { transmute(self.header) } }
    fn root<'a>(&'a self) -> Node<'a> {
        unsafe { Node{data: &*self.nodetable, file: self,} }
    }
    fn get_str<'a>(&'a self, index: u32) -> Option<&'a str> {
        unsafe {
            let off = *self.stringtable.offset(index as int);
            let ptr = self.data.offset(off as int);
            let size: *u16 = transmute(ptr);
            let func = |buf: &[u8]| -> Option<&'a str> {
                let bytes: &'a [u8] = transmute(buf);
                std::str::from_utf8(bytes) };
            std::slice::raw::buf_as_slice(ptr.offset(2), (*size) as uint,
                                          func)
        }
    }
}
#[packed]
struct Header {
    magic: u32,
    nodecount: u32,
    nodeoffset: u64,
    stringcount: u32,
    stringoffset: u64,
    bitmapcount: u32,
    bitmapoffset: u64,
    audiocount: u32,
    audiooffset: u64,
}
#[packed]
struct String {
    length: u16,
}
struct Node<'a> {
    data: &'a NodeData,
    file: &'a File,
}
impl <'a> Node<'a> {
    fn iter(&self) -> NodeIterator<'a> {
        unsafe {
            let data = self.file.nodetable.offset(self.data.children as int);
            NodeIterator{data: data, count: self.data.count, file: self.file,}
        }
    }
    fn name(&self) -> Option<&'a str> { self.file.get_str(self.data.name) }
    fn empty(&self) -> bool { self.data.count == 0 }
}
struct NodeIterator<'a> {
    data: *NodeData,
    count: u16,
    file: &'a File,
}
impl <'a> Iterator<Node<'a>> for NodeIterator<'a> {
    fn next(&mut self) -> Option<Node<'a>> {
        match self.count {
            0 => None,
            _ => {
                unsafe {
                    self.count -= 1;
                    let node = Node{data: &*self.data, file: self.file,};
                    self.data = self.data.offset(1);
                    Some(node)
                }
            }
        }
    }
}
#[packed]
struct NodeData {
    name: u32,
    children: u32,
    count: u16,
    dtype: u16,
    data: u64,
}
#[packed]
struct NodeInteger {
    value: i64,
}
#[packed]
struct NodeFloat {
    value: f64,
}
#[packed]
struct NodeString {
    index: u32,
    unused: u32,
}
#[packed]
struct NodeVector {
    x: i32,
    y: i32,
}
#[packed]
struct NodeBitmap {
    index: u32,
    width: u16,
    height: u16,
}
#[packed]
struct NodeAudio {
    index: u32,
    length: u32,
}
fn recurse(node: Node) -> int { node.iter().fold(1, |a, b| a + recurse(b)) }
fn test(name: &str, func: |Node| -> int, node: Node) {
    for i in range(0, 10) {
        let begin = time::precise_time_ns();
        let answer = recurse(node);
        let end = time::precise_time_ns();
        println!("{}\t{}\t{}" , name , ( end - begin ) / 1000 , answer);
    }
}
fn main() {
    unsafe { ::std::rt::stack::record_sp_limit(0); }
    let file = File::open(&Path::new("Data.nx")).unwrap();
    test("Re", recurse, file.root());
}
<|endoftext|>


<commit_before><commit_msg>Fix pointed out<commit_after><|endoftext|>


<commit_before>// Copyright 2013-2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Interfaces to the operating system provided random number
//! generators.

pub use self::imp::OsRng;

#[cfg(all(unix, not(target_os = "ios")))]
mod imp {
    extern crate libc;

    use self::OsRngInner::*;

    use io::{IoResult, File};
    use path::Path;
    use rand::Rng;
    use rand::reader::ReaderRng;
    use result::Result::Ok;
    use slice::SliceExt;
    use mem;
    use os::errno;

    // NOTE: for old macros; remove after the next snapshot
    #[cfg(stage0)] use result::Result::Err;

    #[cfg(all(target_os = "linux",
              any(target_arch = "x86_64",
                  target_arch = "x86",
                  target_arch = "arm",
                  target_arch = "aarch64")))]
    fn getrandom(buf: &mut [u8]) -> libc::c_long {
        extern "C" {
            fn syscall(number: libc::c_long, ...) -> libc::c_long;
        }

        #[cfg(target_arch = "x86_64")]
        const NR_GETRANDOM: libc::c_long = 318;
        #[cfg(target_arch = "x86")]
        const NR_GETRANDOM: libc::c_long = 355;
        #[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
        const NR_GETRANDOM: libc::c_long = 384;

        unsafe {
            syscall(NR_GETRANDOM, buf.as_mut_ptr(), buf.len(), 0u)
        }
    }

    #[cfg(not(all(target_os = "linux",
                  any(target_arch = "x86_64",
                      target_arch = "x86",
                      target_arch = "arm",
                      target_arch = "aarch64"))))]
    fn getrandom(_buf: &mut [u8]) -> libc::c_long { -1 }

    fn getrandom_fill_bytes(v: &mut [u8]) {
        let mut read = 0;
        let len = v.len();
        while read < len {
            let result = getrandom(v.slice_from_mut(read));
            if result == -1 {
                let err = errno() as libc::c_int;
                if err == libc::EINTR {
                    continue;
                } else {
                    panic!("unexpected getrandom error: {}", err);
                }
            } else {
                read += result as uint;
            }
        }
    }

    fn getrandom_next_u32() -> u32 {
        let mut buf: [u8; 4] = [0u8; 4];
        getrandom_fill_bytes(&mut buf);
        unsafe { mem::transmute::<[u8; 4], u32>(buf) }
    }

    fn getrandom_next_u64() -> u64 {
        let mut buf: [u8; 8] = [0u8; 8];
        getrandom_fill_bytes(&mut buf);
        unsafe { mem::transmute::<[u8; 8], u64>(buf) }
    }

    #[cfg(all(target_os = "linux",
              any(target_arch = "x86_64",
                  target_arch = "x86",
                  target_arch = "arm",
                  target_arch = "aarch64")))]
    fn is_getrandom_available() -> bool {
        use sync::atomic::{AtomicBool, ATOMIC_BOOL_INIT, Ordering};

        static GETRANDOM_CHECKED: AtomicBool = ATOMIC_BOOL_INIT;
        static GETRANDOM_AVAILABLE: AtomicBool = ATOMIC_BOOL_INIT;

        if !GETRANDOM_CHECKED.load(Ordering::Relaxed) {
            let mut buf: [u8; 0] = [];
            let result = getrandom(&mut buf);
            let available = if result == -1 {
                let err = errno() as libc::c_int;
                err != libc::ENOSYS
            } else {
                true
            };
            GETRANDOM_AVAILABLE.store(available, Ordering::Relaxed);
            GETRANDOM_CHECKED.store(true, Ordering::Relaxed);
            available
        } else {
            GETRANDOM_AVAILABLE.load(Ordering::Relaxed)
        }
    }

    #[cfg(not(all(target_os = "linux",
                  any(target_arch = "x86_64",
                      target_arch = "x86",
                      target_arch = "arm",
                      target_arch = "aarch64"))))]
    fn is_getrandom_available() -> bool { false }

    /// A random number generator that retrieves randomness straight from
    /// the operating system. Platform sources:
    ///
    /// - Unix-like systems (Linux, Android, Mac OSX): read directly from
    ///   `/dev/urandom`, or from `getrandom(2)` system call if available.
    /// - Windows: calls `CryptGenRandom`, using the default cryptographic
    ///   service provider with the `PROV_RSA_FULL` type.
    /// - iOS: calls SecRandomCopyBytes as /dev/(u)random is sandboxed.
    ///
    /// This does not block.
    pub struct OsRng {
        inner: OsRngInner,
    }

    enum OsRngInner {
        OsGetrandomRng,
        OsReaderRng(ReaderRng<File>),
    }

    impl OsRng {
        /// Create a new `OsRng`.
        pub fn new() -> IoResult<OsRng> {
            if is_getrandom_available() {
                return Ok(OsRng { inner: OsGetrandomRng });
            }

            let reader = try!(File::open(&Path::new("/dev/urandom")));
            let reader_rng = ReaderRng::new(reader);

            Ok(OsRng { inner: OsReaderRng(reader_rng) })
        }
    }

    impl Rng for OsRng {
        fn next_u32(&mut self) -> u32 {
            match self.inner {
                OsGetrandomRng => getrandom_next_u32(),
                OsReaderRng(ref mut rng) => rng.next_u32(),
            }
        }
        fn next_u64(&mut self) -> u64 {
            match self.inner {
                OsGetrandomRng => getrandom_next_u64(),
                OsReaderRng(ref mut rng) => rng.next_u64(),
            }
        }
        fn fill_bytes(&mut self, v: &mut [u8]) {
            match self.inner {
                OsGetrandomRng => getrandom_fill_bytes(v),
                OsReaderRng(ref mut rng) => rng.fill_bytes(v)
            }
        }
    }
}

#[cfg(target_os = "ios")]
mod imp {
    extern crate libc;

    use io::{IoResult};
    use kinds::Sync;
    use mem;
    use os;
    use rand::Rng;
    use result::Result::{Ok};
    use self::libc::{c_int, size_t};
    use slice::SliceExt;

    /// A random number generator that retrieves randomness straight from
    /// the operating system. Platform sources:
    ///
    /// - Unix-like systems (Linux, Android, Mac OSX): read directly from
    ///   `/dev/urandom`, or from `getrandom(2)` system call if available.
    /// - Windows: calls `CryptGenRandom`, using the default cryptographic
    ///   service provider with the `PROV_RSA_FULL` type.
    /// - iOS: calls SecRandomCopyBytes as /dev/(u)random is sandboxed.
    ///
    /// This does not block.
    #[allow(missing_copy_implementations)]
    pub struct OsRng {
        // dummy field to ensure that this struct cannot be constructed outside of this module
        _dummy: (),
    }

    #[repr(C)]
    struct SecRandom;

    unsafe impl Sync for *const SecRandom {}

    #[allow(non_upper_case_globals)]
    static kSecRandomDefault: *const SecRandom = 0 as *const SecRandom;

    #[link(name = "Security", kind = "framework")]
    extern "C" {
        fn SecRandomCopyBytes(rnd: *const SecRandom,
                              count: size_t, bytes: *mut u8) -> c_int;
    }

    impl OsRng {
        /// Create a new `OsRng`.
        pub fn new() -> IoResult<OsRng> {
            Ok(OsRng { _dummy: () })
        }
    }

    impl Rng for OsRng {
        fn next_u32(&mut self) -> u32 {
            let mut v = [0u8; 4];
            self.fill_bytes(&mut v);
            unsafe { mem::transmute(v) }
        }
        fn next_u64(&mut self) -> u64 {
            let mut v = [0u8; 8];
            self.fill_bytes(&mut v);
            unsafe { mem::transmute(v) }
        }
        fn fill_bytes(&mut self, v: &mut [u8]) {
            let ret = unsafe {
                SecRandomCopyBytes(kSecRandomDefault, v.len() as size_t, v.as_mut_ptr())
            };
            if ret == -1 {
                panic!("couldn't generate random bytes: {}", os::last_os_error());
            }
        }
    }
}

#[cfg(windows)]
mod imp {
    extern crate libc;

    use io::{IoResult, IoError};
    use mem;
    use ops::Drop;
    use os;
    use rand::Rng;
    use result::Result::{Ok, Err};
    use self::libc::{DWORD, BYTE, LPCSTR, BOOL};
    use self::libc::types::os::arch::extra::{LONG_PTR};
    use slice::SliceExt;

    type HCRYPTPROV = LONG_PTR;

    /// A random number generator that retrieves randomness straight from
    /// the operating system. Platform sources:
    ///
    /// - Unix-like systems (Linux, Android, Mac OSX): read directly from
    ///   `/dev/urandom`, or from `getrandom(2)` system call if available.
    /// - Windows: calls `CryptGenRandom`, using the default cryptographic
    ///   service provider with the `PROV_RSA_FULL` type.
    /// - iOS: calls SecRandomCopyBytes as /dev/(u)random is sandboxed.
    ///
    /// This does not block.
    pub struct OsRng {
        hcryptprov: HCRYPTPROV
    }

    static PROV_RSA_FULL: DWORD = 1;
    static CRYPT_SILENT: DWORD = 64;
    static CRYPT_VERIFYCONTEXT: DWORD = 0xF0000000;

    #[allow(non_snake_case)]
    extern "system" {
        fn CryptAcquireContextA(phProv: *mut HCRYPTPROV,
                                pszContainer: LPCSTR,
                                pszProvider: LPCSTR,
                                dwProvType: DWORD,
                                dwFlags: DWORD) -> BOOL;
        fn CryptGenRandom(hProv: HCRYPTPROV,
                          dwLen: DWORD,
                          pbBuffer: *mut BYTE) -> BOOL;
        fn CryptReleaseContext(hProv: HCRYPTPROV, dwFlags: DWORD) -> BOOL;
    }

    impl OsRng {
        /// Create a new `OsRng`.
        pub fn new() -> IoResult<OsRng> {
            let mut hcp = 0;
            let ret = unsafe {
                CryptAcquireContextA(&mut hcp, 0 as LPCSTR, 0 as LPCSTR,
                                     PROV_RSA_FULL,
                                     CRYPT_VERIFYCONTEXT | CRYPT_SILENT)
            };

            if ret == 0 {
                Err(IoError::last_error())
            } else {
                Ok(OsRng { hcryptprov: hcp })
            }
        }
    }

    impl Rng for OsRng {
        fn next_u32(&mut self) -> u32 {
            let mut v = [0u8; 4];
            self.fill_bytes(&mut v);
            unsafe { mem::transmute(v) }
        }
        fn next_u64(&mut self) -> u64 {
            let mut v = [0u8; 8];
            self.fill_bytes(&mut v);
            unsafe { mem::transmute(v) }
        }
        fn fill_bytes(&mut self, v: &mut [u8]) {
            let ret = unsafe {
                CryptGenRandom(self.hcryptprov, v.len() as DWORD,
                               v.as_mut_ptr())
            };
            if ret == 0 {
                panic!("couldn't generate random bytes: {}", os::last_os_error());
            }
        }
    }

    impl Drop for OsRng {
        fn drop(&mut self) {
            let ret = unsafe {
                CryptReleaseContext(self.hcryptprov, 0)
            };
            if ret == 0 {
                panic!("couldn't release context: {}", os::last_os_error());
            }
        }
    }
}

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

    use sync::mpsc::channel;
    use rand::Rng;
    use super::OsRng;
    use thread::Thread;

    #[test]
    fn test_os_rng() {
        let mut r = OsRng::new().unwrap();

        r.next_u32();
        r.next_u64();

        let mut v = [0u8; 1000];
        r.fill_bytes(&mut v);
    }

    #[test]
    fn test_os_rng_tasks() {

        let mut txs = vec!();
        for _ in range(0u, 20) {
            let (tx, rx) = channel();
            txs.push(tx);

            Thread::spawn(move|| {
                // wait until all the tasks are ready to go.
                rx.recv().unwrap();

                // deschedule to attempt to interleave things as much
                // as possible (XXX: is this a good test?)
                let mut r = OsRng::new().unwrap();
                Thread::yield_now();
                let mut v = [0u8; 1000];

                for _ in range(0u, 100) {
                    r.next_u32();
                    Thread::yield_now();
                    r.next_u64();
                    Thread::yield_now();
                    r.fill_bytes(&mut v);
                    Thread::yield_now();
                }
            }).detach();
        }

        // start all the tasks
        for tx in txs.iter() {
            tx.send(()).unwrap();
        }
    }
}
<commit_msg>Change `std::kinds` to `std::markers`; flatten `std::kinds::marker`<commit_after>// Copyright 2013-2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Interfaces to the operating system provided random number
//! generators.

pub use self::imp::OsRng;

#[cfg(all(unix, not(target_os = "ios")))]
mod imp {
    extern crate libc;

    use self::OsRngInner::*;

    use io::{IoResult, File};
    use path::Path;
    use rand::Rng;
    use rand::reader::ReaderRng;
    use result::Result::Ok;
    use slice::SliceExt;
    use mem;
    use os::errno;

    // NOTE: for old macros; remove after the next snapshot
    #[cfg(stage0)] use result::Result::Err;

    #[cfg(all(target_os = "linux",
              any(target_arch = "x86_64",
                  target_arch = "x86",
                  target_arch = "arm",
                  target_arch = "aarch64")))]
    fn getrandom(buf: &mut [u8]) -> libc::c_long {
        extern "C" {
            fn syscall(number: libc::c_long, ...) -> libc::c_long;
        }

        #[cfg(target_arch = "x86_64")]
        const NR_GETRANDOM: libc::c_long = 318;
        #[cfg(target_arch = "x86")]
        const NR_GETRANDOM: libc::c_long = 355;
        #[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
        const NR_GETRANDOM: libc::c_long = 384;

        unsafe {
            syscall(NR_GETRANDOM, buf.as_mut_ptr(), buf.len(), 0u)
        }
    }

    #[cfg(not(all(target_os = "linux",
                  any(target_arch = "x86_64",
                      target_arch = "x86",
                      target_arch = "arm",
                      target_arch = "aarch64"))))]
    fn getrandom(_buf: &mut [u8]) -> libc::c_long { -1 }

    fn getrandom_fill_bytes(v: &mut [u8]) {
        let mut read = 0;
        let len = v.len();
        while read < len {
            let result = getrandom(v.slice_from_mut(read));
            if result == -1 {
                let err = errno() as libc::c_int;
                if err == libc::EINTR {
                    continue;
                } else {
                    panic!("unexpected getrandom error: {}", err);
                }
            } else {
                read += result as uint;
            }
        }
    }

    fn getrandom_next_u32() -> u32 {
        let mut buf: [u8; 4] = [0u8; 4];
        getrandom_fill_bytes(&mut buf);
        unsafe { mem::transmute::<[u8; 4], u32>(buf) }
    }

    fn getrandom_next_u64() -> u64 {
        let mut buf: [u8; 8] = [0u8; 8];
        getrandom_fill_bytes(&mut buf);
        unsafe { mem::transmute::<[u8; 8], u64>(buf) }
    }

    #[cfg(all(target_os = "linux",
              any(target_arch = "x86_64",
                  target_arch = "x86",
                  target_arch = "arm",
                  target_arch = "aarch64")))]
    fn is_getrandom_available() -> bool {
        use sync::atomic::{AtomicBool, ATOMIC_BOOL_INIT, Ordering};

        static GETRANDOM_CHECKED: AtomicBool = ATOMIC_BOOL_INIT;
        static GETRANDOM_AVAILABLE: AtomicBool = ATOMIC_BOOL_INIT;

        if !GETRANDOM_CHECKED.load(Ordering::Relaxed) {
            let mut buf: [u8; 0] = [];
            let result = getrandom(&mut buf);
            let available = if result == -1 {
                let err = errno() as libc::c_int;
                err != libc::ENOSYS
            } else {
                true
            };
            GETRANDOM_AVAILABLE.store(available, Ordering::Relaxed);
            GETRANDOM_CHECKED.store(true, Ordering::Relaxed);
            available
        } else {
            GETRANDOM_AVAILABLE.load(Ordering::Relaxed)
        }
    }

    #[cfg(not(all(target_os = "linux",
                  any(target_arch = "x86_64",
                      target_arch = "x86",
                      target_arch = "arm",
                      target_arch = "aarch64"))))]
    fn is_getrandom_available() -> bool { false }

    /// A random number generator that retrieves randomness straight from
    /// the operating system. Platform sources:
    ///
    /// - Unix-like systems (Linux, Android, Mac OSX): read directly from
    ///   `/dev/urandom`, or from `getrandom(2)` system call if available.
    /// - Windows: calls `CryptGenRandom`, using the default cryptographic
    ///   service provider with the `PROV_RSA_FULL` type.
    /// - iOS: calls SecRandomCopyBytes as /dev/(u)random is sandboxed.
    ///
    /// This does not block.
    pub struct OsRng {
        inner: OsRngInner,
    }

    enum OsRngInner {
        OsGetrandomRng,
        OsReaderRng(ReaderRng<File>),
    }

    impl OsRng {
        /// Create a new `OsRng`.
        pub fn new() -> IoResult<OsRng> {
            if is_getrandom_available() {
                return Ok(OsRng { inner: OsGetrandomRng });
            }

            let reader = try!(File::open(&Path::new("/dev/urandom")));
            let reader_rng = ReaderRng::new(reader);

            Ok(OsRng { inner: OsReaderRng(reader_rng) })
        }
    }

    impl Rng for OsRng {
        fn next_u32(&mut self) -> u32 {
            match self.inner {
                OsGetrandomRng => getrandom_next_u32(),
                OsReaderRng(ref mut rng) => rng.next_u32(),
            }
        }
        fn next_u64(&mut self) -> u64 {
            match self.inner {
                OsGetrandomRng => getrandom_next_u64(),
                OsReaderRng(ref mut rng) => rng.next_u64(),
            }
        }
        fn fill_bytes(&mut self, v: &mut [u8]) {
            match self.inner {
                OsGetrandomRng => getrandom_fill_bytes(v),
                OsReaderRng(ref mut rng) => rng.fill_bytes(v)
            }
        }
    }
}

#[cfg(target_os = "ios")]
mod imp {
    extern crate libc;

    use io::{IoResult};
    use markers::Sync;
    use mem;
    use os;
    use rand::Rng;
    use result::Result::{Ok};
    use self::libc::{c_int, size_t};
    use slice::SliceExt;

    /// A random number generator that retrieves randomness straight from
    /// the operating system. Platform sources:
    ///
    /// - Unix-like systems (Linux, Android, Mac OSX): read directly from
    ///   `/dev/urandom`, or from `getrandom(2)` system call if available.
    /// - Windows: calls `CryptGenRandom`, using the default cryptographic
    ///   service provider with the `PROV_RSA_FULL` type.
    /// - iOS: calls SecRandomCopyBytes as /dev/(u)random is sandboxed.
    ///
    /// This does not block.
    #[allow(missing_copy_implementations)]
    pub struct OsRng {
        // dummy field to ensure that this struct cannot be constructed outside of this module
        _dummy: (),
    }

    #[repr(C)]
    struct SecRandom;

    unsafe impl Sync for *const SecRandom {}

    #[allow(non_upper_case_globals)]
    static kSecRandomDefault: *const SecRandom = 0 as *const SecRandom;

    #[link(name = "Security", kind = "framework")]
    extern "C" {
        fn SecRandomCopyBytes(rnd: *const SecRandom,
                              count: size_t, bytes: *mut u8) -> c_int;
    }

    impl OsRng {
        /// Create a new `OsRng`.
        pub fn new() -> IoResult<OsRng> {
            Ok(OsRng { _dummy: () })
        }
    }

    impl Rng for OsRng {
        fn next_u32(&mut self) -> u32 {
            let mut v = [0u8; 4];
            self.fill_bytes(&mut v);
            unsafe { mem::transmute(v) }
        }
        fn next_u64(&mut self) -> u64 {
            let mut v = [0u8; 8];
            self.fill_bytes(&mut v);
            unsafe { mem::transmute(v) }
        }
        fn fill_bytes(&mut self, v: &mut [u8]) {
            let ret = unsafe {
                SecRandomCopyBytes(kSecRandomDefault, v.len() as size_t, v.as_mut_ptr())
            };
            if ret == -1 {
                panic!("couldn't generate random bytes: {}", os::last_os_error());
            }
        }
    }
}

#[cfg(windows)]
mod imp {
    extern crate libc;

    use io::{IoResult, IoError};
    use mem;
    use ops::Drop;
    use os;
    use rand::Rng;
    use result::Result::{Ok, Err};
    use self::libc::{DWORD, BYTE, LPCSTR, BOOL};
    use self::libc::types::os::arch::extra::{LONG_PTR};
    use slice::SliceExt;

    type HCRYPTPROV = LONG_PTR;

    /// A random number generator that retrieves randomness straight from
    /// the operating system. Platform sources:
    ///
    /// - Unix-like systems (Linux, Android, Mac OSX): read directly from
    ///   `/dev/urandom`, or from `getrandom(2)` system call if available.
    /// - Windows: calls `CryptGenRandom`, using the default cryptographic
    ///   service provider with the `PROV_RSA_FULL` type.
    /// - iOS: calls SecRandomCopyBytes as /dev/(u)random is sandboxed.
    ///
    /// This does not block.
    pub struct OsRng {
        hcryptprov: HCRYPTPROV
    }

    static PROV_RSA_FULL: DWORD = 1;
    static CRYPT_SILENT: DWORD = 64;
    static CRYPT_VERIFYCONTEXT: DWORD = 0xF0000000;

    #[allow(non_snake_case)]
    extern "system" {
        fn CryptAcquireContextA(phProv: *mut HCRYPTPROV,
                                pszContainer: LPCSTR,
                                pszProvider: LPCSTR,
                                dwProvType: DWORD,
                                dwFlags: DWORD) -> BOOL;
        fn CryptGenRandom(hProv: HCRYPTPROV,
                          dwLen: DWORD,
                          pbBuffer: *mut BYTE) -> BOOL;
        fn CryptReleaseContext(hProv: HCRYPTPROV, dwFlags: DWORD) -> BOOL;
    }

    impl OsRng {
        /// Create a new `OsRng`.
        pub fn new() -> IoResult<OsRng> {
            let mut hcp = 0;
            let ret = unsafe {
                CryptAcquireContextA(&mut hcp, 0 as LPCSTR, 0 as LPCSTR,
                                     PROV_RSA_FULL,
                                     CRYPT_VERIFYCONTEXT | CRYPT_SILENT)
            };

            if ret == 0 {
                Err(IoError::last_error())
            } else {
                Ok(OsRng { hcryptprov: hcp })
            }
        }
    }

    impl Rng for OsRng {
        fn next_u32(&mut self) -> u32 {
            let mut v = [0u8; 4];
            self.fill_bytes(&mut v);
            unsafe { mem::transmute(v) }
        }
        fn next_u64(&mut self) -> u64 {
            let mut v = [0u8; 8];
            self.fill_bytes(&mut v);
            unsafe { mem::transmute(v) }
        }
        fn fill_bytes(&mut self, v: &mut [u8]) {
            let ret = unsafe {
                CryptGenRandom(self.hcryptprov, v.len() as DWORD,
                               v.as_mut_ptr())
            };
            if ret == 0 {
                panic!("couldn't generate random bytes: {}", os::last_os_error());
            }
        }
    }

    impl Drop for OsRng {
        fn drop(&mut self) {
            let ret = unsafe {
                CryptReleaseContext(self.hcryptprov, 0)
            };
            if ret == 0 {
                panic!("couldn't release context: {}", os::last_os_error());
            }
        }
    }
}

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

    use sync::mpsc::channel;
    use rand::Rng;
    use super::OsRng;
    use thread::Thread;

    #[test]
    fn test_os_rng() {
        let mut r = OsRng::new().unwrap();

        r.next_u32();
        r.next_u64();

        let mut v = [0u8; 1000];
        r.fill_bytes(&mut v);
    }

    #[test]
    fn test_os_rng_tasks() {

        let mut txs = vec!();
        for _ in range(0u, 20) {
            let (tx, rx) = channel();
            txs.push(tx);

            Thread::spawn(move|| {
                // wait until all the tasks are ready to go.
                rx.recv().unwrap();

                // deschedule to attempt to interleave things as much
                // as possible (XXX: is this a good test?)
                let mut r = OsRng::new().unwrap();
                Thread::yield_now();
                let mut v = [0u8; 1000];

                for _ in range(0u, 100) {
                    r.next_u32();
                    Thread::yield_now();
                    r.next_u64();
                    Thread::yield_now();
                    r.fill_bytes(&mut v);
                    Thread::yield_now();
                }
            }).detach();
        }

        // start all the tasks
        for tx in txs.iter() {
            tx.send(()).unwrap();
        }
    }
}
<|endoftext|>


<commit_before>// $ cargo bench --features full --bench rust
//
// Syn only, useful for profiling:
// $ RUSTFLAGS='--cfg syn_only' cargo build --release --features full --bench rust

#![cfg_attr(not(syn_only), feature(rustc_private))]
#![recursion_limit = "1024"]

#[macro_use]
#[path = "../tests/macros/mod.rs"]
mod macros;

#[path = "../tests/common/mod.rs"]
mod common;
#[path = "../tests/repo/mod.rs"]
mod repo;

use std::fs;
use std::time::{Duration, Instant};

#[cfg(not(syn_only))]
mod tokenstream_parse {
    use proc_macro2::TokenStream;
    use std::str::FromStr;

    pub fn bench(content: &str) -> Result<(), ()> {
        TokenStream::from_str(content).map(drop).map_err(drop)
    }
}

mod syn_parse {
    pub fn bench(content: &str) -> Result<(), ()> {
        syn::parse_file(content).map(drop).map_err(drop)
    }
}

#[cfg(not(syn_only))]
mod libsyntax_parse {
    extern crate rustc_data_structures;
    extern crate rustc_parse;
    extern crate rustc_span;
    extern crate syntax;

    use rustc_data_structures::sync::Lrc;
    use rustc_span::FileName;
    use syntax::edition::Edition;
    use syntax::errors::{emitter::Emitter, Diagnostic, Handler};
    use syntax::sess::ParseSess;
    use syntax::source_map::{FilePathMapping, SourceMap};

    pub fn bench(content: &str) -> Result<(), ()> {
        struct SilentEmitter;

        impl Emitter for SilentEmitter {
            fn emit_diagnostic(&mut self, _diag: &Diagnostic) {}
            fn source_map(&self) -> Option<&Lrc<SourceMap>> {
                None
            }
        }

        syntax::with_globals(Edition::Edition2018, || {
            let cm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
            let emitter = Box::new(SilentEmitter);
            let handler = Handler::with_emitter(false, None, emitter);
            let sess = ParseSess::with_span_handler(handler, cm);
            if let Err(mut diagnostic) = rustc_parse::parse_crate_from_source_str(
                FileName::Custom("bench".to_owned()),
                content.to_owned(),
                &sess,
            ) {
                diagnostic.cancel();
                return Err(());
            };
            Ok(())
        })
    }
}

#[cfg(not(syn_only))]
mod read_from_disk {
    pub fn bench(content: &str) -> Result<(), ()> {
        let _ = content;
        Ok(())
    }
}

fn exec(mut codepath: impl FnMut(&str) -> Result<(), ()>) -> Duration {
    let begin = Instant::now();
    let mut success = 0;
    let mut total = 0;

    walkdir::WalkDir::new("tests/rust/src")
        .into_iter()
        .filter_entry(repo::base_dir_filter)
        .for_each(|entry| {
            let entry = entry.unwrap();
            let path = entry.path();
            if path.is_dir() {
                return;
            }
            let content = fs::read_to_string(path).unwrap();
            let ok = codepath(&content).is_ok();
            success += ok as usize;
            total += 1;
            if !ok {
                eprintln!("FAIL {}", path.display());
            }
        });

    assert_eq!(success, total);
    begin.elapsed()
}

fn main() {
    repo::clone_rust();

    macro_rules! testcases {
        ($($(#[$cfg:meta])* $name:path,)*) => {
            vec![
                $(
                    $(#[$cfg])*
                    (stringify!($name), $name as fn(&str) -> Result<(), ()>),
                )*
            ]
        };
    }

    #[cfg(not(syn_only))]
    {
        let mut lines = 0;
        let mut files = 0;
        exec(|content| {
            lines += content.lines().count();
            files += 1;
            Ok(())
        });
        eprintln!("\n{} lines in {} files", lines, files);
    }

    for (name, f) in testcases!(
        #[cfg(not(syn_only))]
        read_from_disk::bench,
        #[cfg(not(syn_only))]
        tokenstream_parse::bench,
        syn_parse::bench,
        #[cfg(not(syn_only))]
        libsyntax_parse::bench,
    ) {
        eprint!("{:20}", format!("{}:", name));
        let elapsed = exec(f);
        eprintln!(
            "elapsed={}.{:03}s",
            elapsed.as_secs(),
            elapsed.subsec_millis(),
        );
    }
    eprintln!();
}
<commit_msg>Update rust benchmark to rustc_ast<commit_after>// $ cargo bench --features full --bench rust
//
// Syn only, useful for profiling:
// $ RUSTFLAGS='--cfg syn_only' cargo build --release --features full --bench rust

#![cfg_attr(not(syn_only), feature(rustc_private))]
#![recursion_limit = "1024"]

#[macro_use]
#[path = "../tests/macros/mod.rs"]
mod macros;

#[path = "../tests/common/mod.rs"]
mod common;
#[path = "../tests/repo/mod.rs"]
mod repo;

use std::fs;
use std::time::{Duration, Instant};

#[cfg(not(syn_only))]
mod tokenstream_parse {
    use proc_macro2::TokenStream;
    use std::str::FromStr;

    pub fn bench(content: &str) -> Result<(), ()> {
        TokenStream::from_str(content).map(drop).map_err(drop)
    }
}

mod syn_parse {
    pub fn bench(content: &str) -> Result<(), ()> {
        syn::parse_file(content).map(drop).map_err(drop)
    }
}

#[cfg(not(syn_only))]
mod libsyntax_parse {
    extern crate rustc_ast;
    extern crate rustc_data_structures;
    extern crate rustc_errors;
    extern crate rustc_parse;
    extern crate rustc_session;
    extern crate rustc_span;

    use rustc_data_structures::sync::Lrc;
    use rustc_errors::{emitter::Emitter, Diagnostic, Handler};
    use rustc_session::parse::ParseSess;
    use rustc_span::source_map::{FilePathMapping, SourceMap};
    use rustc_span::{edition::Edition, FileName};

    pub fn bench(content: &str) -> Result<(), ()> {
        struct SilentEmitter;

        impl Emitter for SilentEmitter {
            fn emit_diagnostic(&mut self, _diag: &Diagnostic) {}
            fn source_map(&self) -> Option<&Lrc<SourceMap>> {
                None
            }
        }

        rustc_ast::with_globals(Edition::Edition2018, || {
            let cm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
            let emitter = Box::new(SilentEmitter);
            let handler = Handler::with_emitter(false, None, emitter);
            let sess = ParseSess::with_span_handler(handler, cm);
            if let Err(mut diagnostic) = rustc_parse::parse_crate_from_source_str(
                FileName::Custom("bench".to_owned()),
                content.to_owned(),
                &sess,
            ) {
                diagnostic.cancel();
                return Err(());
            };
            Ok(())
        })
    }
}

#[cfg(not(syn_only))]
mod read_from_disk {
    pub fn bench(content: &str) -> Result<(), ()> {
        let _ = content;
        Ok(())
    }
}

fn exec(mut codepath: impl FnMut(&str) -> Result<(), ()>) -> Duration {
    let begin = Instant::now();
    let mut success = 0;
    let mut total = 0;

    walkdir::WalkDir::new("tests/rust/src")
        .into_iter()
        .filter_entry(repo::base_dir_filter)
        .for_each(|entry| {
            let entry = entry.unwrap();
            let path = entry.path();
            if path.is_dir() {
                return;
            }
            let content = fs::read_to_string(path).unwrap();
            let ok = codepath(&content).is_ok();
            success += ok as usize;
            total += 1;
            if !ok {
                eprintln!("FAIL {}", path.display());
            }
        });

    assert_eq!(success, total);
    begin.elapsed()
}

fn main() {
    repo::clone_rust();

    macro_rules! testcases {
        ($($(#[$cfg:meta])* $name:path,)*) => {
            vec![
                $(
                    $(#[$cfg])*
                    (stringify!($name), $name as fn(&str) -> Result<(), ()>),
                )*
            ]
        };
    }

    #[cfg(not(syn_only))]
    {
        let mut lines = 0;
        let mut files = 0;
        exec(|content| {
            lines += content.lines().count();
            files += 1;
            Ok(())
        });
        eprintln!("\n{} lines in {} files", lines, files);
    }

    for (name, f) in testcases!(
        #[cfg(not(syn_only))]
        read_from_disk::bench,
        #[cfg(not(syn_only))]
        tokenstream_parse::bench,
        syn_parse::bench,
        #[cfg(not(syn_only))]
        libsyntax_parse::bench,
    ) {
        eprint!("{:20}", format!("{}:", name));
        let elapsed = exec(f);
        eprintln!(
            "elapsed={}.{:03}s",
            elapsed.as_secs(),
            elapsed.subsec_millis(),
        );
    }
    eprintln!();
}
<|endoftext|>


<commit_before>//! Support mapping based Cuboid shape.

use std::f64;
use num::{One, Zero};

use na::{self, Real, Unit};
use bounding_volume::PolyhedralCone;
use shape::{ConvexPolyface, FeatureId, SupportMap};
use math::{Isometry, Point, Vector};

/// Shape of a box.
#[derive(PartialEq, Debug, Clone)]
pub struct Cuboid<V> {
    half_extents: V,
}

impl<V: Vector> Cuboid<V> {
    /// Creates a new box from its half-extents. Half-extents are the box half-width along each
    /// axis. Each half-extent must be greater than 0.04.
    #[inline]
    pub fn new(half_extents: V) -> Cuboid<V> {
        for i in 0..na::dimension::<V>() {
            assert!(half_extents[i] >= V::Real::zero());
        }

        Cuboid {
            half_extents: half_extents,
        }
    }
}

impl<V> Cuboid<V> {
    /// The half-extents of this box. Half-extents are the box half-width along each axis.
    #[inline]
    pub fn half_extents(&self) -> &V {
        &self.half_extents
    }
}

// FIXME: this should be a public method of Cuboid. However we can't do this because
// of the type-parameter P.
fn face<P, M>(half_extents: &P::Vector, m: &M, mut i: usize, out: &mut ConvexPolyface<P>)
where
    P: Point,
    M: Isometry<P>,
{
    let dim = na::dimension::<P::Vector>();
    let i1;
    let sign;

    if i < dim {
        i1 = i;
        sign = P::Real::one();
    } else {
        i1 = i - dim;
        sign = -P::Real::one();
    }

    if dim == 2 {
        let i2 = (i1 + 1) % 2;

        let mut vertex = *half_extents;
        vertex[i1] *= sign;
        vertex[i2] *= if i1 == 0 { -sign } else { sign };

        let p1 = P::from_coordinates(vertex);
        vertex[i2] = -vertex[i2];
        let p2 = P::from_coordinates(vertex);

        let mut vertex_id1 = if sign < na::zero() { 1 << i1 } else { 0 };
        let mut vertex_id2 = vertex_id1;
        if p1[i2] < na::zero() {
            vertex_id1 |= 1 << i2;
        } else {
            vertex_id2 |= 1 << i2;
        }
        out.push(m.transform_point(&p1), FeatureId::Vertex(vertex_id1));
        out.push(m.transform_point(&p2), FeatureId::Vertex(vertex_id2));

        let mut normal: P::Vector = na::zero();
        normal[i1] = sign;
        out.set_normal(Unit::new_unchecked(m.transform_vector(&normal)));

        if sign > na::zero() {
            out.set_feature_id(FeatureId::Edge(i1));
        } else {
            out.set_feature_id(FeatureId::Edge(i1 + 2));
        }
    } else {
        let i2 = (i1 + 1) % 3;
        let i3 = (i1 + 2) % 3;
        let (edge_i2, edge_i3) = if sign > na::zero() {
            (i2, i3)
        } else {
            (i3, i2)
        };
        let mut vertex = *half_extents;
        vertex[i1] *= sign;

        let (sbit, msbit) = if sign < na::zero() { (1, 0) } else { (0, 1) };
        let mut vertex_id = sbit << i1;
        out.push(
            m.transform_point(&P::from_coordinates(vertex)),
            FeatureId::Vertex(vertex_id),
        );
        out.push_edge_feature_id(FeatureId::Edge(edge_i2 | (vertex_id << 2)));

        vertex[i2] = -sign * half_extents[i2];
        vertex[i3] = sign * half_extents[i3];
        vertex_id |= msbit << i2 | sbit << i3;
        out.push(
            m.transform_point(&P::from_coordinates(vertex)),
            FeatureId::Vertex(vertex_id),
        );
        out.push_edge_feature_id(FeatureId::Edge(edge_i3 | (vertex_id << 2)));

        vertex[i2] = -half_extents[i2];
        vertex[i3] = -half_extents[i3];
        vertex_id |= 1 << i2 | 1 << i3;
        out.push(
            m.transform_point(&P::from_coordinates(vertex)),
            FeatureId::Vertex(vertex_id),
        );
        out.push_edge_feature_id(FeatureId::Edge(edge_i2 | (vertex_id << 2)));

        vertex[i2] = sign * half_extents[i2];
        vertex[i3] = -sign * half_extents[i3];
        vertex_id = sbit << i1 | sbit << i2 | msbit << i3;
        out.push(
            m.transform_point(&P::from_coordinates(vertex)),
            FeatureId::Vertex(vertex_id),
        );
        out.push_edge_feature_id(FeatureId::Edge(edge_i3 | (vertex_id << 2)));

        let mut normal: P::Vector = na::zero();
        normal[i1] = sign;
        out.set_normal(Unit::new_unchecked(m.transform_vector(&normal)));
        if sign > na::zero() {
            out.set_feature_id(FeatureId::Face(i1));
        } else {
            out.set_feature_id(FeatureId::Face(i1 + 3));
        }
        out.recompute_edge_normals_3d();
    }
}

impl<P: Point, M: Isometry<P>> SupportMap<P, M> for Cuboid<P::Vector> {
    #[inline]
    fn support_point(&self, m: &M, dir: &P::Vector) -> P {
        let local_dir = m.inverse_rotate_vector(dir);

        let mut res = *self.half_extents();

        for i in 0usize..na::dimension::<P::Vector>() {
            if local_dir[i] < P::Real::zero() {
                res[i] = -res[i];
            }
        }

        m.transform_point(&P::from_coordinates(res))
    }

    fn support_face_toward(&self, m: &M, dir: &Unit<P::Vector>, out: &mut ConvexPolyface<P>) {
        let local_dir = m.inverse_rotate_vector(dir);

        let mut iamax = 0;
        let mut amax = local_dir[0].abs();
        let dim = na::dimension::<P::Vector>();

        // FIXME: we should use nalgebra's iamax method.
        for i in 1..dim {
            let candidate = local_dir[i].abs();
            if candidate > amax {
                amax = candidate;
                iamax = i;
            }
        }

        if local_dir[iamax] > na::zero() {
            face(&self.half_extents, m, iamax, out);
        } else {
            face(&self.half_extents, m, iamax + dim, out);
        }
    }

    fn support_feature_toward(
        &self,
        m: &M,
        dir: &Unit<P::Vector>,
        angle: P::Real,
        out: &mut ConvexPolyface<P>,
    ) {
        let local_dir = m.inverse_rotate_vector(dir);
        let (sang, cang) = angle.sin_cos();
        let dim = na::dimension::<P::Vector>();
        let mut support_point = self.half_extents;

        out.clear();
        match dim {
            2 => {
                let mut support_point_id = 0;
                for i1 in 0..2 {
                    let sign = local_dir[i1].signum();
                    if sign * local_dir[i1] >= cang {
                        if sign > na::zero() {
                            face(&self.half_extents, m, i1, out);
                        } else {
                            face(&self.half_extents, m, i1 + 2, out)
                        }
                        return;
                    } else {
                        if sign < na::zero() {
                            support_point_id |= 1 << i1;
                        }
                        support_point[i1] *= sign;
                    }
                }

                // We are not on a face, return the support vertex.
                out.push(
                    m.transform_point(&P::from_coordinates(support_point)),
                    FeatureId::Vertex(support_point_id),
                );
                out.set_feature_id(FeatureId::Vertex(support_point_id));
            }
            3 => {
                let mut support_point_id = 0;

                // Check faces.
                for i1 in 0..3 {
                    let sign = local_dir[i1].signum();
                    if sign * local_dir[i1] >= cang {
                        if sign > na::zero() {
                            face(&self.half_extents, m, i1, out);
                        } else {
                            face(&self.half_extents, m, i1 + 3, out)
                        }
                        return;
                    } else {
                        if sign < na::zero() {
                            support_point[i1] *= sign;
                            support_point_id |= 1 << i1;
                        }
                    }
                }

                // Check edges.
                for i in 0..3 {
                    let sign = local_dir[i].signum();

                    // sign * local_dir[i] <= cos(pi / 2 - angle)
                    if sign * local_dir[i] <= sang {
                        support_point[i] = -self.half_extents[i];
                        let p1 = P::from_coordinates(support_point);
                        support_point[i] = self.half_extents[i];
                        let p2 = P::from_coordinates(support_point);
                        out.push(
                            m.transform_point(&p1),
                            FeatureId::Vertex(support_point_id | (1 << i)),
                        );
                        out.push(
                            m.transform_point(&p2),
                            FeatureId::Vertex(support_point_id & !(1 << i)),
                        );

                        let edge_id = FeatureId::Edge(i | (support_point_id << 2));
                        out.push_edge_feature_id(edge_id);
                        out.set_feature_id(edge_id);
                        return;
                    }
                }

                // We are not on a face or edge, return the support vertex.
                out.push(
                    m.transform_point(&P::from_coordinates(support_point)),
                    FeatureId::Vertex(support_point_id),
                );
                out.set_feature_id(FeatureId::Vertex(support_point_id));
            }
            _ => {
                out.push(self.support_point_toward(m, dir), FeatureId::Unknown);
                out.set_feature_id(FeatureId::Unknown);
            }
        }
    }

    fn normal_cone(&self, local_pt: &P, feature: FeatureId) -> PolyhedralCone<P::Vector> {
        // let mut result = PolyhedralCone::new();
        // let mut dir = na::zero();

        unimplemented!()
        // match na::dimension::<P::Vector>() {
        //     2 => match feature {
        //         FeatureId::Edge(id) => {
        //             if id < 2 {
        //                 dir[id] = na::one();
        //             } else {
        //                 dir[id] = -na::one();
        //             }
        //             result.push(Unit::new_unchecked(dir));
        //         }
        //         FeatureId::Vertex(id) => {
        //             if id & 0b01 != 0 {
        //                 local_dir[0] = -local_dir[0]
        //             }
        //             if id & 0b10 != 0 {
        //                 local_dir[1] = -local_dir[1]
        //             }

        //             local_dir[0] >= -stol && local_dir[1] >= -stol
        //         }
        //         _ => false,
        //     },
        //     3 => match feature {
        //         FeatureId::Face(id) => {
        //             if id < 3 {
        //                 local_dir[id] >= ctol
        //             } else {
        //                 -local_dir[id - 3] >= ctol
        //             }
        //         }
        //         FeatureId::Edge(id) => {
        //             let edge = id & 0b011;
        //             let face1 = (edge + 1) % 3;
        //             let face2 = (edge + 2) % 3;
        //             let signs = id >> 2;

        //             if signs & (1 << face1) != 0 {
        //                 local_dir[face1] = -local_dir[face1]
        //             }
        //             if signs & (1 << face2) != 0 {
        //                 local_dir[face2] = -local_dir[face2]
        //             }

        //             local_dir[face1] > -stol && local_dir[face2] > -stol
        //                 && local_dir[edge].abs() <= stol
        //         }
        //         FeatureId::Vertex(id) => {
        //             for i in 0..3 {
        //                 if id & (1 << i) != 0 {
        //                     if local_dir[i] > stol {
        //                         return false;
        //                     }
        //                 } else if local_dir[i] < -stol {
        //                     return false;
        //                 }
        //             }
        //             true
        //         }
        //         _ => false,
        //     },
        //     _ => false,
        // }
    }

    fn is_direction_in_normal_cone(
        &self,
        m: &M,
        feature: FeatureId,
        point: &P,
        dir: &Unit<P::Vector>,
    ) -> bool {
        let tolerence: P::Real = na::convert(f64::consts::PI / 180.0 * 1.0);
        let (stol, ctol) = tolerence.sin_cos();
        let mut local_dir = m.inverse_rotate_vector(dir);

        match na::dimension::<P::Vector>() {
            2 => match feature {
                FeatureId::Edge(id) => {
                    if id < 2 {
                        local_dir[id] >= ctol
                    } else {
                        -local_dir[id - 2] >= ctol
                    }
                }
                FeatureId::Vertex(id) => {
                    if id & 0b01 != 0 {
                        local_dir[0] = -local_dir[0]
                    }
                    if id & 0b10 != 0 {
                        local_dir[1] = -local_dir[1]
                    }

                    local_dir[0] >= -stol && local_dir[1] >= -stol
                }
                _ => false,
            },
            3 => match feature {
                FeatureId::Face(id) => {
                    if id < 3 {
                        local_dir[id] >= ctol
                    } else {
                        -local_dir[id - 3] >= ctol
                    }
                }
                FeatureId::Edge(id) => {
                    let edge = id & 0b011;
                    let face1 = (edge + 1) % 3;
                    let face2 = (edge + 2) % 3;
                    let signs = id >> 2;

                    if signs & (1 << face1) != 0 {
                        local_dir[face1] = -local_dir[face1]
                    }
                    if signs & (1 << face2) != 0 {
                        local_dir[face2] = -local_dir[face2]
                    }

                    local_dir[face1] > -stol && local_dir[face2] > -stol
                        && local_dir[edge].abs() <= stol
                }
                FeatureId::Vertex(id) => {
                    for i in 0..3 {
                        if id & (1 << i) != 0 {
                            if local_dir[i] > stol {
                                return false;
                            }
                        } else if local_dir[i] < -stol {
                            return false;
                        }
                    }
                    true
                }
                _ => false,
            },
            _ => false,
        }
    }
}
<commit_msg>Add normal cone generation for 2D cuboid.<commit_after>//! Support mapping based Cuboid shape.

use std::f64;
use num::{One, Zero};

use na::{self, Real, Unit};
use bounding_volume::PolyhedralCone;
use shape::{ConvexPolyface, FeatureId, SupportMap};
use math::{Isometry, Point, Vector};

/// Shape of a box.
#[derive(PartialEq, Debug, Clone)]
pub struct Cuboid<V> {
    half_extents: V,
}

impl<V: Vector> Cuboid<V> {
    /// Creates a new box from its half-extents. Half-extents are the box half-width along each
    /// axis. Each half-extent must be greater than 0.04.
    #[inline]
    pub fn new(half_extents: V) -> Cuboid<V> {
        for i in 0..na::dimension::<V>() {
            assert!(half_extents[i] >= V::Real::zero());
        }

        Cuboid {
            half_extents: half_extents,
        }
    }
}

impl<V> Cuboid<V> {
    /// The half-extents of this box. Half-extents are the box half-width along each axis.
    #[inline]
    pub fn half_extents(&self) -> &V {
        &self.half_extents
    }
}

// FIXME: this should be a public method of Cuboid. However we can't do this because
// of the type-parameter P.
fn face<P, M>(half_extents: &P::Vector, m: &M, mut i: usize, out: &mut ConvexPolyface<P>)
where
    P: Point,
    M: Isometry<P>,
{
    let dim = na::dimension::<P::Vector>();
    let i1;
    let sign;

    if i < dim {
        i1 = i;
        sign = P::Real::one();
    } else {
        i1 = i - dim;
        sign = -P::Real::one();
    }

    if dim == 2 {
        let i2 = (i1 + 1) % 2;

        let mut vertex = *half_extents;
        vertex[i1] *= sign;
        vertex[i2] *= if i1 == 0 { -sign } else { sign };

        let p1 = P::from_coordinates(vertex);
        vertex[i2] = -vertex[i2];
        let p2 = P::from_coordinates(vertex);

        let mut vertex_id1 = if sign < na::zero() { 1 << i1 } else { 0 };
        let mut vertex_id2 = vertex_id1;
        if p1[i2] < na::zero() {
            vertex_id1 |= 1 << i2;
        } else {
            vertex_id2 |= 1 << i2;
        }
        out.push(m.transform_point(&p1), FeatureId::Vertex(vertex_id1));
        out.push(m.transform_point(&p2), FeatureId::Vertex(vertex_id2));

        let mut normal: P::Vector = na::zero();
        normal[i1] = sign;
        out.set_normal(Unit::new_unchecked(m.transform_vector(&normal)));

        if sign > na::zero() {
            out.set_feature_id(FeatureId::Edge(i1));
        } else {
            out.set_feature_id(FeatureId::Edge(i1 + 2));
        }
    } else {
        let i2 = (i1 + 1) % 3;
        let i3 = (i1 + 2) % 3;
        let (edge_i2, edge_i3) = if sign > na::zero() {
            (i2, i3)
        } else {
            (i3, i2)
        };
        let mut vertex = *half_extents;
        vertex[i1] *= sign;

        let (sbit, msbit) = if sign < na::zero() { (1, 0) } else { (0, 1) };
        let mut vertex_id = sbit << i1;
        out.push(
            m.transform_point(&P::from_coordinates(vertex)),
            FeatureId::Vertex(vertex_id),
        );
        out.push_edge_feature_id(FeatureId::Edge(edge_i2 | (vertex_id << 2)));

        vertex[i2] = -sign * half_extents[i2];
        vertex[i3] = sign * half_extents[i3];
        vertex_id |= msbit << i2 | sbit << i3;
        out.push(
            m.transform_point(&P::from_coordinates(vertex)),
            FeatureId::Vertex(vertex_id),
        );
        out.push_edge_feature_id(FeatureId::Edge(edge_i3 | (vertex_id << 2)));

        vertex[i2] = -half_extents[i2];
        vertex[i3] = -half_extents[i3];
        vertex_id |= 1 << i2 | 1 << i3;
        out.push(
            m.transform_point(&P::from_coordinates(vertex)),
            FeatureId::Vertex(vertex_id),
        );
        out.push_edge_feature_id(FeatureId::Edge(edge_i2 | (vertex_id << 2)));

        vertex[i2] = sign * half_extents[i2];
        vertex[i3] = -sign * half_extents[i3];
        vertex_id = sbit << i1 | sbit << i2 | msbit << i3;
        out.push(
            m.transform_point(&P::from_coordinates(vertex)),
            FeatureId::Vertex(vertex_id),
        );
        out.push_edge_feature_id(FeatureId::Edge(edge_i3 | (vertex_id << 2)));

        let mut normal: P::Vector = na::zero();
        normal[i1] = sign;
        out.set_normal(Unit::new_unchecked(m.transform_vector(&normal)));
        if sign > na::zero() {
            out.set_feature_id(FeatureId::Face(i1));
        } else {
            out.set_feature_id(FeatureId::Face(i1 + 3));
        }
        out.recompute_edge_normals_3d();
    }
}

impl<P: Point, M: Isometry<P>> SupportMap<P, M> for Cuboid<P::Vector> {
    #[inline]
    fn support_point(&self, m: &M, dir: &P::Vector) -> P {
        let local_dir = m.inverse_rotate_vector(dir);

        let mut res = *self.half_extents();

        for i in 0usize..na::dimension::<P::Vector>() {
            if local_dir[i] < P::Real::zero() {
                res[i] = -res[i];
            }
        }

        m.transform_point(&P::from_coordinates(res))
    }

    fn support_face_toward(&self, m: &M, dir: &Unit<P::Vector>, out: &mut ConvexPolyface<P>) {
        let local_dir = m.inverse_rotate_vector(dir);

        let mut iamax = 0;
        let mut amax = local_dir[0].abs();
        let dim = na::dimension::<P::Vector>();

        // FIXME: we should use nalgebra's iamax method.
        for i in 1..dim {
            let candidate = local_dir[i].abs();
            if candidate > amax {
                amax = candidate;
                iamax = i;
            }
        }

        if local_dir[iamax] > na::zero() {
            face(&self.half_extents, m, iamax, out);
        } else {
            face(&self.half_extents, m, iamax + dim, out);
        }
    }

    fn support_feature_toward(
        &self,
        m: &M,
        dir: &Unit<P::Vector>,
        angle: P::Real,
        out: &mut ConvexPolyface<P>,
    ) {
        let local_dir = m.inverse_rotate_vector(dir);
        let (sang, cang) = angle.sin_cos();
        let dim = na::dimension::<P::Vector>();
        let mut support_point = self.half_extents;

        out.clear();
        match dim {
            2 => {
                let mut support_point_id = 0;
                for i1 in 0..2 {
                    let sign = local_dir[i1].signum();
                    if sign * local_dir[i1] >= cang {
                        if sign > na::zero() {
                            face(&self.half_extents, m, i1, out);
                        } else {
                            face(&self.half_extents, m, i1 + 2, out)
                        }
                        return;
                    } else {
                        if sign < na::zero() {
                            support_point_id |= 1 << i1;
                        }
                        support_point[i1] *= sign;
                    }
                }

                // We are not on a face, return the support vertex.
                out.push(
                    m.transform_point(&P::from_coordinates(support_point)),
                    FeatureId::Vertex(support_point_id),
                );
                out.set_feature_id(FeatureId::Vertex(support_point_id));
            }
            3 => {
                let mut support_point_id = 0;

                // Check faces.
                for i1 in 0..3 {
                    let sign = local_dir[i1].signum();
                    if sign * local_dir[i1] >= cang {
                        if sign > na::zero() {
                            face(&self.half_extents, m, i1, out);
                        } else {
                            face(&self.half_extents, m, i1 + 3, out)
                        }
                        return;
                    } else {
                        if sign < na::zero() {
                            support_point[i1] *= sign;
                            support_point_id |= 1 << i1;
                        }
                    }
                }

                // Check edges.
                for i in 0..3 {
                    let sign = local_dir[i].signum();

                    // sign * local_dir[i] <= cos(pi / 2 - angle)
                    if sign * local_dir[i] <= sang {
                        support_point[i] = -self.half_extents[i];
                        let p1 = P::from_coordinates(support_point);
                        support_point[i] = self.half_extents[i];
                        let p2 = P::from_coordinates(support_point);
                        out.push(
                            m.transform_point(&p1),
                            FeatureId::Vertex(support_point_id | (1 << i)),
                        );
                        out.push(
                            m.transform_point(&p2),
                            FeatureId::Vertex(support_point_id & !(1 << i)),
                        );

                        let edge_id = FeatureId::Edge(i | (support_point_id << 2));
                        out.push_edge_feature_id(edge_id);
                        out.set_feature_id(edge_id);
                        return;
                    }
                }

                // We are not on a face or edge, return the support vertex.
                out.push(
                    m.transform_point(&P::from_coordinates(support_point)),
                    FeatureId::Vertex(support_point_id),
                );
                out.set_feature_id(FeatureId::Vertex(support_point_id));
            }
            _ => {
                out.push(self.support_point_toward(m, dir), FeatureId::Unknown);
                out.set_feature_id(FeatureId::Unknown);
            }
        }
    }

    fn normal_cone(&self, _: &P, feature: FeatureId) -> PolyhedralCone<P::Vector> {
        let mut result = PolyhedralCone::new();

        match na::dimension::<P::Vector>() {
            2 => match feature {
                FeatureId::Edge(id) => {
                    let mut dir: P::Vector = na::zero();

                    if id < 2 {
                        dir[id] = P::Real::one();
                    } else {
                        dir[id - 2] = -P::Real::one();
                    }
                    result.add_generator(Unit::new_unchecked(dir));
                }
                FeatureId::Vertex(id) => {
                    let mut dir1: P::Vector = na::zero();
                    let mut dir2: P::Vector = na::zero();

                    match id {
                        0b00 => {
                            dir1[0] = P::Real::one();
                            dir2[1] = P::Real::one();
                        }
                        0b01 => {
                            dir1[1] = P::Real::one();
                            dir2[0] = -P::Real::one();
                        }
                        0b10 => {
                            dir1[0] = -P::Real::one();
                            dir2[1] = -P::Real::one();
                        }
                        0b11 => {
                            dir1[1] = -P::Real::one();
                            dir2[0] = P::Real::one();
                        }
                        _ => unreachable!(),
                    }

                    result.add_generator(Unit::new_unchecked(dir1));
                    result.add_generator(Unit::new_unchecked(dir2));
                }
                _ => {}
            },
            _ => unimplemented!(),
        }
        //     3 => match feature {
        //         FeatureId::Face(id) => {
        //             if id < 3 {
        //                 local_dir[id] >= ctol
        //             } else {
        //                 -local_dir[id - 3] >= ctol
        //             }
        //         }
        //         FeatureId::Edge(id) => {
        //             let edge = id & 0b011;
        //             let face1 = (edge + 1) % 3;
        //             let face2 = (edge + 2) % 3;
        //             let signs = id >> 2;

        //             if signs & (1 << face1) != 0 {
        //                 local_dir[face1] = -local_dir[face1]
        //             }
        //             if signs & (1 << face2) != 0 {
        //                 local_dir[face2] = -local_dir[face2]
        //             }

        //             local_dir[face1] > -stol && local_dir[face2] > -stol
        //                 && local_dir[edge].abs() <= stol
        //         }
        //         FeatureId::Vertex(id) => {
        //             for i in 0..3 {
        //                 if id & (1 << i) != 0 {
        //                     if local_dir[i] > stol {
        //                         return false;
        //                     }
        //                 } else if local_dir[i] < -stol {
        //                     return false;
        //                 }
        //             }
        //             true
        //         }
        //         _ => false,
        //     },
        //     _ => false,
        // }

        result
    }

    fn is_direction_in_normal_cone(
        &self,
        m: &M,
        feature: FeatureId,
        point: &P,
        dir: &Unit<P::Vector>,
    ) -> bool {
        let tolerence: P::Real = na::convert(f64::consts::PI / 180.0 * 1.0);
        let (stol, ctol) = tolerence.sin_cos();
        let mut local_dir = m.inverse_rotate_vector(dir);

        match na::dimension::<P::Vector>() {
            2 => match feature {
                FeatureId::Edge(id) => {
                    if id < 2 {
                        local_dir[id] >= ctol
                    } else {
                        -local_dir[id - 2] >= ctol
                    }
                }
                FeatureId::Vertex(id) => {
                    if id & 0b01 != 0 {
                        local_dir[0] = -local_dir[0]
                    }
                    if id & 0b10 != 0 {
                        local_dir[1] = -local_dir[1]
                    }

                    local_dir[0] >= -stol && local_dir[1] >= -stol
                }
                _ => false,
            },
            3 => match feature {
                FeatureId::Face(id) => {
                    if id < 3 {
                        local_dir[id] >= ctol
                    } else {
                        -local_dir[id - 3] >= ctol
                    }
                }
                FeatureId::Edge(id) => {
                    let edge = id & 0b011;
                    let face1 = (edge + 1) % 3;
                    let face2 = (edge + 2) % 3;
                    let signs = id >> 2;

                    if signs & (1 << face1) != 0 {
                        local_dir[face1] = -local_dir[face1]
                    }
                    if signs & (1 << face2) != 0 {
                        local_dir[face2] = -local_dir[face2]
                    }

                    local_dir[face1] > -stol && local_dir[face2] > -stol
                        && local_dir[edge].abs() <= stol
                }
                FeatureId::Vertex(id) => {
                    for i in 0..3 {
                        if id & (1 << i) != 0 {
                            if local_dir[i] > stol {
                                return false;
                            }
                        } else if local_dir[i] < -stol {
                            return false;
                        }
                    }
                    true
                }
                _ => false,
            },
            _ => false,
        }
    }
}
<|endoftext|>