<commit_before>extern crate trie;
extern crate block;
extern crate hexutil;
extern crate bigint;
extern crate sputnikvm;
extern crate sputnikvm_stateful;
#[macro_use] extern crate lazy_static;

use hexutil::*;
use block::TransactionAction;
use bigint::{Address, U256, Gas};
use sputnikvm::{AccountChange, HeaderParams, SeqTransactionVM, VM, Storage, MainnetEIP160Patch, ValidTransaction};
use trie::MemoryDatabase;
use sputnikvm_stateful::{MemoryStateful, LiteralAccount};
use std::thread;
use std::collections::{HashMap, HashSet};
use std::sync::Arc;
use std::rc::Rc;
use std::ops::Deref;
use std::str::FromStr;

#[derive(Debug, Clone)]
/// Represents an account. This is usually returned by the EVM.
pub enum SendableAccountChange {
    /// A full account. The client is expected to replace its own account state with this.
    Full {
        /// Account nonce.
        nonce: U256,
        /// Account address.
        address: Address,
        /// Account balance.
        balance: U256,
        /// Change storage with given indexes and values.
        changing_storage: Storage,
        /// Code associated with this account.
        code: Vec<u8>,
    },
    /// Only balance is changed, and it is increasing for this address.
    IncreaseBalance(Address, U256),
    /// Only balance is changed, and it is decreasing for this address.
    DecreaseBalance(Address, U256),
    /// Create or delete a (new) account.
    Create {
        /// Account nonce.
        nonce: U256,
        /// Account address.
        address: Address,
        /// Account balance.
        balance: U256,
        /// All storage values of this account, with given indexes and values.
        storage: Storage,
        /// Code associated with this account.
        code: Vec<u8>,
        /// Whether, at this point, the account is considered
        /// existing. The client should delete this address if this is
        /// set to `false`.
        exists: bool,
    },
}

impl SendableAccountChange {
    /// Address of this account.
    pub fn address(&self) -> Address {
        match self {
            &SendableAccountChange::Full {
                address,
                ..
            } => address,
            &SendableAccountChange::IncreaseBalance(address, _) => address,
            &SendableAccountChange::DecreaseBalance(address, _) => address,
            &SendableAccountChange::Create {
                address,
                ..
            } => address,
        }
    }
}

impl From<AccountChange> for SendableAccountChange {
    fn from(change: AccountChange) -> Self {
        match change {
            AccountChange::Full { nonce, address, balance, changing_storage, code } => {
                SendableAccountChange::Full {
                    nonce, address, balance, changing_storage,
                    code: code.deref().clone()
                }
            },
            AccountChange::IncreaseBalance(address, balance) =>
                SendableAccountChange::IncreaseBalance(address, balance),
            AccountChange::DecreaseBalance(address, balance) =>
                SendableAccountChange::DecreaseBalance(address, balance),
            AccountChange::Create { nonce, address, balance, storage, code, exists } => {
                SendableAccountChange::Create {
                    nonce, address, balance, storage, exists,
                    code: code.deref().clone()
                }
            },
        }
    }
}

impl Into<AccountChange> for SendableAccountChange {
    fn into(self) -> AccountChange {
        match self {
            SendableAccountChange::Full { nonce, address, balance, changing_storage, code } => {
                AccountChange::Full {
                    nonce, address, balance, changing_storage,
                    code: Rc::new(code),
                }
            },
            SendableAccountChange::IncreaseBalance(address, balance) =>
                AccountChange::IncreaseBalance(address, balance),
            SendableAccountChange::DecreaseBalance(address, balance) =>
                AccountChange::DecreaseBalance(address, balance),
            SendableAccountChange::Create { nonce, address, balance, storage, code, exists } => {
                AccountChange::Create {
                    nonce, address, balance, storage, exists,
                    code: Rc::new(code),
                }
            },
        }
    }
}

pub struct SendableValidTransaction {
    pub caller: Option<Address>,
    pub gas_price: Gas,
    pub gas_limit: Gas,
    pub action: TransactionAction,
    pub value: U256,
    pub input: Vec<u8>,
    pub nonce: U256,
}

impl From<ValidTransaction> for SendableValidTransaction {
    fn from(transaction: ValidTransaction) -> SendableValidTransaction {
        match transaction {
            ValidTransaction { caller, gas_price, gas_limit, action, value, input, nonce } => {
                SendableValidTransaction {
                    caller, gas_price, gas_limit, action, value, nonce,
                    input: input.deref().clone(),
                }
            }
        }
    }
}

impl Into<ValidTransaction> for SendableValidTransaction {
    fn into(self) -> ValidTransaction {
        match self {
            SendableValidTransaction { caller, gas_price, gas_limit, action, value, input, nonce } => {
                ValidTransaction {
                    caller, gas_price, gas_limit, action, value, nonce,
                    input: Rc::new(input),
                }
            }
        }
    }
}

fn is_modified(modified_addresses: &HashSet<Address>, accounts: &[SendableAccountChange]) -> bool {
    for account in accounts {
        if modified_addresses.contains(&account.address()) {
            return true;
        }
    }
    return false;
}

pub fn parallel_execute(
    stateful: MemoryStateful<'static>, transactions: &[ValidTransaction]
) -> MemoryStateful<'static> {
    let header = HeaderParams {
        beneficiary: Address::zero(),
        timestamp: 0,
        number: U256::zero(),
        difficulty: U256::zero(),
        gas_limit: Gas::max_value(),
    };

    let stateful = Arc::new(stateful);
    let mut threads = Vec::new();

    // Execute all transactions in parallel.
    for transaction in transactions {
        let transaction: SendableValidTransaction = transaction.clone().into();
        let header = header.clone();
        let stateful = stateful.clone();

        threads.push(thread::spawn(move || {
            let vm: SeqTransactionVM<MainnetEIP160Patch> = stateful.call(
                transaction.into(), header, &[]);
            let accounts: Vec<SendableAccountChange> = vm.accounts().map(|v| SendableAccountChange::from(v.clone())).collect();
            (accounts, vm.used_addresses())
        }));
    }

    // Join all results together.
    let mut thread_accounts = Vec::new();
    for thread in threads {
        let accounts = thread.join().unwrap();
        thread_accounts.push(accounts);
    }

    // Now we only have a single reference to stateful, unwrap it and
    // start the state transition.
    let mut stateful = match Arc::try_unwrap(stateful) {
        Ok(val) => val,
        Err(_) => panic!(),
    };
    let mut modified_addresses = HashSet::new();

    for (index, (accounts, used_addresses)) in thread_accounts.into_iter().enumerate() {
        let (accounts, used_addresses) = if is_modified(&modified_addresses, &accounts) {
            // Re-execute the transaction if conflict is detected.
            println!("Transaction index {}: conflict detected, re-execute.", index);
            let vm: SeqTransactionVM<MainnetEIP160Patch> = stateful.call(
                transactions[index].clone(), header.clone(), &[]);
            let accounts: Vec<AccountChange> = vm.accounts().map(|v| v.clone()).collect();
            (accounts, vm.used_addresses())
        } else {
            println!("Transaction index {}: parallel execution successful.", index);
            let accounts: Vec<AccountChange> = accounts.iter().map(|v| v.clone().into()).collect();
            (accounts, used_addresses)
        };

        stateful.transit(&accounts);
        modified_addresses.extend(used_addresses.into_iter());
    }

    stateful
}

lazy_static! {
    static ref DATABASE: MemoryDatabase = MemoryDatabase::default();
}

fn main() {
    let mut stateful = MemoryStateful::empty(&DATABASE);

    let addr1 = Address::from_str("0x0000000000000000000000000000000000001000").unwrap();
    let addr2 = Address::from_str("0x0000000000000000000000000000000000001001").unwrap();
    let addr3 = Address::from_str("0x0000000000000000000000000000000000001002").unwrap();

    // Input some initial accounts.
    stateful.sets(&[
        (addr1, LiteralAccount {
            nonce: U256::zero(),
            balance: U256::from_str("0x1000000000000000000").unwrap(),
            storage: HashMap::new(),
            code: read_hex("0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01600055").unwrap(),
        }),
        (addr2, LiteralAccount {
            nonce: U256::zero(),
            balance: U256::from_str("0x1000000000000000000").unwrap(),
            storage: HashMap::new(),
            code: read_hex("0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01600055").unwrap(),
        }),
        (addr3, LiteralAccount {
            nonce: U256::zero(),
            balance: U256::from_str("0x1000000000000000000").unwrap(),
            storage: HashMap::new(),
            code: read_hex("0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01600055").unwrap(),
        }),
    ]);

    // Execute several crafted transactions.
    let stateful = parallel_execute(stateful, &[
        ValidTransaction {
            caller: Some(addr1),
            action: TransactionAction::Call(addr1),
            gas_price: Gas::zero(),
            gas_limit: Gas::max_value(),
            value: U256::from_str("0x1000").unwrap(),
            input: Rc::new(Vec::new()),
            nonce: U256::zero(),
        },
        ValidTransaction {
            caller: Some(addr2),
            action: TransactionAction::Call(addr3),
            gas_price: Gas::zero(),
            gas_limit: Gas::max_value(),
            value: U256::from_str("0x1000").unwrap(),
            input: Rc::new(Vec::new()),
            nonce: U256::zero(),
        },
        ValidTransaction {
            caller: Some(addr3),
            action: TransactionAction::Call(addr2),
            gas_price: Gas::zero(),
            gas_limit: Gas::max_value(),
            value: U256::from_str("0x1000").unwrap(),
            input: Rc::new(Vec::new()),
            nonce: U256::zero(),
        },
    ]);

    println!("New state root: 0x{:x}", stateful.root());
}
<commit_msg>Fix parallel example test<commit_after>extern crate trie;
extern crate block;
extern crate hexutil;
extern crate bigint;
extern crate sputnikvm;
extern crate sputnikvm_stateful;
#[macro_use] extern crate lazy_static;

use hexutil::*;
use block::TransactionAction;
use bigint::{Address, U256, Gas};
use sputnikvm::{AccountChange, HeaderParams, SeqTransactionVM, VM, Storage, MainnetEIP160Patch, ValidTransaction};
use trie::MemoryDatabase;
use sputnikvm_stateful::{MemoryStateful, LiteralAccount};
use std::thread;
use std::collections::{HashMap, HashSet};
use std::sync::Arc;
use std::rc::Rc;
use std::ops::Deref;
use std::str::FromStr;

#[derive(Debug, Clone)]
/// Represents an account. This is usually returned by the EVM.
pub enum SendableAccountChange {
    /// A full account. The client is expected to replace its own account state with this.
    Full {
        /// Account nonce.
        nonce: U256,
        /// Account address.
        address: Address,
        /// Account balance.
        balance: U256,
        /// Change storage with given indexes and values.
        changing_storage: Storage,
        /// Code associated with this account.
        code: Vec<u8>,
    },
    /// Only balance is changed, and it is increasing for this address.
    IncreaseBalance(Address, U256),
    /// Only balance is changed, and it is decreasing for this address.
    DecreaseBalance(Address, U256),
    /// Create or delete a (new) account.
    Create {
        /// Account nonce.
        nonce: U256,
        /// Account address.
        address: Address,
        /// Account balance.
        balance: U256,
        /// All storage values of this account, with given indexes and values.
        storage: Storage,
        /// Code associated with this account.
        code: Vec<u8>,
    },
    Nonexist(Address),
}

impl SendableAccountChange {
    /// Address of this account.
    pub fn address(&self) -> Address {
        match self {
            &SendableAccountChange::Full {
                address,
                ..
            } => address,
            &SendableAccountChange::IncreaseBalance(address, _) => address,
            &SendableAccountChange::DecreaseBalance(address, _) => address,
            &SendableAccountChange::Create {
                address,
                ..
            } => address,
            &SendableAccountChange::Nonexist(address) => address,
        }
    }
}

impl From<AccountChange> for SendableAccountChange {
    fn from(change: AccountChange) -> Self {
        match change {
            AccountChange::Full { nonce, address, balance, changing_storage, code } => {
                SendableAccountChange::Full {
                    nonce, address, balance, changing_storage,
                    code: code.deref().clone()
                }
            },
            AccountChange::IncreaseBalance(address, balance) =>
                SendableAccountChange::IncreaseBalance(address, balance),
            AccountChange::DecreaseBalance(address, balance) =>
                SendableAccountChange::DecreaseBalance(address, balance),
            AccountChange::Create { nonce, address, balance, storage, code } => {
                SendableAccountChange::Create {
                    nonce, address, balance, storage,
                    code: code.deref().clone()
                }
            },
            AccountChange::Nonexist(address) => SendableAccountChange::Nonexist(address),
        }
    }
}

impl Into<AccountChange> for SendableAccountChange {
    fn into(self) -> AccountChange {
        match self {
            SendableAccountChange::Full { nonce, address, balance, changing_storage, code } => {
                AccountChange::Full {
                    nonce, address, balance, changing_storage,
                    code: Rc::new(code),
                }
            },
            SendableAccountChange::IncreaseBalance(address, balance) =>
                AccountChange::IncreaseBalance(address, balance),
            SendableAccountChange::DecreaseBalance(address, balance) =>
                AccountChange::DecreaseBalance(address, balance),
            SendableAccountChange::Create { nonce, address, balance, storage, code } => {
                AccountChange::Create {
                    nonce, address, balance, storage,
                    code: Rc::new(code),
                }
            },
            SendableAccountChange::Nonexist(address) => AccountChange::Nonexist(address),
        }
    }
}

pub struct SendableValidTransaction {
    pub caller: Option<Address>,
    pub gas_price: Gas,
    pub gas_limit: Gas,
    pub action: TransactionAction,
    pub value: U256,
    pub input: Vec<u8>,
    pub nonce: U256,
}

impl From<ValidTransaction> for SendableValidTransaction {
    fn from(transaction: ValidTransaction) -> SendableValidTransaction {
        match transaction {
            ValidTransaction { caller, gas_price, gas_limit, action, value, input, nonce } => {
                SendableValidTransaction {
                    caller, gas_price, gas_limit, action, value, nonce,
                    input: input.deref().clone(),
                }
            }
        }
    }
}

impl Into<ValidTransaction> for SendableValidTransaction {
    fn into(self) -> ValidTransaction {
        match self {
            SendableValidTransaction { caller, gas_price, gas_limit, action, value, input, nonce } => {
                ValidTransaction {
                    caller, gas_price, gas_limit, action, value, nonce,
                    input: Rc::new(input),
                }
            }
        }
    }
}

fn is_modified(modified_addresses: &HashSet<Address>, accounts: &[SendableAccountChange]) -> bool {
    for account in accounts {
        if modified_addresses.contains(&account.address()) {
            return true;
        }
    }
    return false;
}

pub fn parallel_execute(
    stateful: MemoryStateful<'static>, transactions: &[ValidTransaction]
) -> MemoryStateful<'static> {
    let header = HeaderParams {
        beneficiary: Address::zero(),
        timestamp: 0,
        number: U256::zero(),
        difficulty: U256::zero(),
        gas_limit: Gas::max_value(),
    };

    let stateful = Arc::new(stateful);
    let mut threads = Vec::new();

    // Execute all transactions in parallel.
    for transaction in transactions {
        let transaction: SendableValidTransaction = transaction.clone().into();
        let header = header.clone();
        let stateful = stateful.clone();

        threads.push(thread::spawn(move || {
            let vm: SeqTransactionVM<MainnetEIP160Patch> = stateful.call(
                transaction.into(), header, &[]);
            let accounts: Vec<SendableAccountChange> = vm.accounts().map(|v| SendableAccountChange::from(v.clone())).collect();
            (accounts, vm.used_addresses())
        }));
    }

    // Join all results together.
    let mut thread_accounts = Vec::new();
    for thread in threads {
        let accounts = thread.join().unwrap();
        thread_accounts.push(accounts);
    }

    // Now we only have a single reference to stateful, unwrap it and
    // start the state transition.
    let mut stateful = match Arc::try_unwrap(stateful) {
        Ok(val) => val,
        Err(_) => panic!(),
    };
    let mut modified_addresses = HashSet::new();

    for (index, (accounts, used_addresses)) in thread_accounts.into_iter().enumerate() {
        let (accounts, used_addresses) = if is_modified(&modified_addresses, &accounts) {
            // Re-execute the transaction if conflict is detected.
            println!("Transaction index {}: conflict detected, re-execute.", index);
            let vm: SeqTransactionVM<MainnetEIP160Patch> = stateful.call(
                transactions[index].clone(), header.clone(), &[]);
            let accounts: Vec<AccountChange> = vm.accounts().map(|v| v.clone()).collect();
            (accounts, vm.used_addresses())
        } else {
            println!("Transaction index {}: parallel execution successful.", index);
            let accounts: Vec<AccountChange> = accounts.iter().map(|v| v.clone().into()).collect();
            (accounts, used_addresses)
        };

        stateful.transit(&accounts);
        modified_addresses.extend(used_addresses.into_iter());
    }

    stateful
}

lazy_static! {
    static ref DATABASE: MemoryDatabase = MemoryDatabase::default();
}

fn main() {
    let mut stateful = MemoryStateful::empty(&DATABASE);

    let addr1 = Address::from_str("0x0000000000000000000000000000000000001000").unwrap();
    let addr2 = Address::from_str("0x0000000000000000000000000000000000001001").unwrap();
    let addr3 = Address::from_str("0x0000000000000000000000000000000000001002").unwrap();

    // Input some initial accounts.
    stateful.sets(&[
        (addr1, LiteralAccount {
            nonce: U256::zero(),
            balance: U256::from_str("0x1000000000000000000").unwrap(),
            storage: HashMap::new(),
            code: read_hex("0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01600055").unwrap(),
        }),
        (addr2, LiteralAccount {
            nonce: U256::zero(),
            balance: U256::from_str("0x1000000000000000000").unwrap(),
            storage: HashMap::new(),
            code: read_hex("0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01600055").unwrap(),
        }),
        (addr3, LiteralAccount {
            nonce: U256::zero(),
            balance: U256::from_str("0x1000000000000000000").unwrap(),
            storage: HashMap::new(),
            code: read_hex("0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01600055").unwrap(),
        }),
    ]);

    // Execute several crafted transactions.
    let stateful = parallel_execute(stateful, &[
        ValidTransaction {
            caller: Some(addr1),
            action: TransactionAction::Call(addr1),
            gas_price: Gas::zero(),
            gas_limit: Gas::max_value(),
            value: U256::from_str("0x1000").unwrap(),
            input: Rc::new(Vec::new()),
            nonce: U256::zero(),
        },
        ValidTransaction {
            caller: Some(addr2),
            action: TransactionAction::Call(addr3),
            gas_price: Gas::zero(),
            gas_limit: Gas::max_value(),
            value: U256::from_str("0x1000").unwrap(),
            input: Rc::new(Vec::new()),
            nonce: U256::zero(),
        },
        ValidTransaction {
            caller: Some(addr3),
            action: TransactionAction::Call(addr2),
            gas_price: Gas::zero(),
            gas_limit: Gas::max_value(),
            value: U256::from_str("0x1000").unwrap(),
            input: Rc::new(Vec::new()),
            nonce: U256::zero(),
        },
    ]);

    println!("New state root: 0x{:x}", stateful.root());
}
<|endoftext|>


<commit_before>use std::borrow::Cow::{Owned, Borrowed};

use pulldown_cmark as cmark;
use self::cmark::{Parser, Event, Tag, Options, OPTION_ENABLE_TABLES, OPTION_ENABLE_FOOTNOTES};
use slug::slugify;
use syntect::easy::HighlightLines;
use syntect::html::{start_coloured_html_snippet, styles_to_coloured_html, IncludeBackground};

use errors::Result;
use utils::site::resolve_internal_link;
use highlighting::{get_highlighter, THEME_SET};
use link_checker::check_url;

use table_of_contents::{TempHeader, Header, make_table_of_contents};
use context::RenderContext;

const CONTINUE_READING: &str = "

\n";

pub struct Rendered {
    pub body: String,
    pub summary_len: Option<usize>,
    pub toc: Vec
}

// We might have cases where the slug is already present in our list of anchor
// for example an article could have several titles named Example
// We add a counter after the slug if the slug is already present, which
// means we will have example, example-1, example-2 etc
fn find_anchor(anchors: &[String], name: String, level: u8) -> String {
    if level == 0 && !anchors.contains(&name) {
        return name.to_string();
    }

    let new_anchor = format!("{}-{}", name, level + 1);
    if !anchors.contains(&new_anchor) {
        return new_anchor;
    }

    find_anchor(anchors, name, level + 1)
}

fn is_colocated_asset_link(link: &str) -> bool {
    !link.contains("/")  // http://, ftp://, ../ etc
        && !link.starts_with("mailto:")
}

pub fn markdown_to_html(content: &str, context: &RenderContext) -> Result<Rendered> {
    // the rendered html
    let mut html = String::with_capacity(content.len());
    // Set while parsing
    let mut error = None;

    let mut highlighter: Option<HighlightLines> = None;
    // If we get text in header, we need to insert the id and a anchor
    let mut in_header = false;
    // pulldown_cmark can send several text events for a title if there are markdown
    // specific characters like `!` in them. We only want to insert the anchor the first time
    let mut header_created = false;
    let mut anchors: Vec<String> = vec![];

    let mut headers = vec![];
    // Defaults to a 0 level so not a real header
    // It should be an Option ideally but not worth the hassle to update
    let mut temp_header = TempHeader::default();

    let mut opts = Options::empty();
    let mut has_summary = false;
    opts.insert(OPTION_ENABLE_TABLES);
    opts.insert(OPTION_ENABLE_FOOTNOTES);

    {
        let parser = Parser::new_ext(content, opts).map(|event| {
            match event {
                Event::Text(text) => {
                    // Header first
                    if in_header {
                        if header_created {
                            temp_header.push(&text);
                            return Event::Html(Borrowed(""));
                        }
                        let id = find_anchor(&anchors, slugify(&text), 0);
                        anchors.push(id.clone());
                        // update the header and add it to the list
                        temp_header.permalink = format!("{}#{}", context.current_page_permalink, id);
                        temp_header.id = id;
                        // += as we might have some  or other things already there
                        temp_header.title += &text;
                        header_created = true;
                        return Event::Html(Borrowed(""));
                    }

                    // if we are in the middle of a code block
                    if let Some(ref mut highlighter) = highlighter {
                        let highlighted = &highlighter.highlight(&text);
                        let html = styles_to_coloured_html(highlighted, IncludeBackground::Yes);
                        return Event::Html(Owned(html));
                    }

                    // Business as usual
                    Event::Text(text)
                }
                Event::Start(Tag::CodeBlock(ref info)) => {
                    if !context.config.highlight_code {
                        return Event::Html(Borrowed("
"));
                    }

                    let theme = &THEME_SET.themes[&context.config.highlight_theme];
                    match get_highlighter(&theme, info, context.base_path, &context.config.extra_syntaxes) {
                        Ok(h) => highlighter = Some(h),
                        Err(err) => {
                            error = Some(format!("Could not load syntax: {}", err).into());
                            return Event::Html(Borrowed(""));
                        }
                    }
                    let snippet = start_coloured_html_snippet(theme);
                    Event::Html(Owned(snippet))
                }
                Event::End(Tag::CodeBlock(_)) => {
                    if !context.config.highlight_code {
                        return Event::Html(Borrowed("
\n"));
                    }
                    // reset highlight and close the code block
                    highlighter = None;
                    Event::Html(Borrowed("
"))
                }
                Event::Start(Tag::Image(src, title)) => {
                    if is_colocated_asset_link(&src) {
                        return Event::Start(
                            Tag::Image(
                                Owned(format!("{}{}", context.current_page_permalink, src)),
                                title,
                            )
                        );
                    }

                    Event::Start(Tag::Image(src, title))
                }
                Event::Start(Tag::Link(link, title)) => {
                    // A few situations here:
                    // - it could be a relative link (starting with `./`)
                    // - it could be a link to a co-located asset
                    // - it could be a normal link
                    // - any of those can be in a header or not: if it's in a header
                    //   we need to append to a string
                    let fixed_link = if link.starts_with("./") {
                        match resolve_internal_link(&link, context.permalinks) {
                            Ok(url) => url,
                            Err(_) => {
                                error = Some(format!("Relative link {} not found.", link).into());
                                return Event::Html(Borrowed(""));
                            }
                        }
                    } else if is_colocated_asset_link(&link) {
                        format!("{}{}", context.current_page_permalink, link)
                    } else {
                        if context.config.check_external_links && !link.starts_with('#') {
                            let res = check_url(&link);
                            if res.is_valid() {
                                link.to_string()
                            } else {
                                error = Some(
                                    format!("Link {} is not valid: {}", link, res.message()).into()
                                );
                                String::new()
                            }
                        } else {
                            link.to_string()
                        }
                    };

                    if in_header {
                        let html = if title.is_empty() {
                            format!("", fixed_link)
                        } else {
                            format!("", fixed_link, title)
                        };
                        temp_header.push(&html);
                        return Event::Html(Borrowed(""));
                    }

                    Event::Start(Tag::Link(Owned(fixed_link), title))
                }
                Event::End(Tag::Link(_, _)) => {
                    if in_header {
                        temp_header.push("");
                        return Event::Html(Borrowed(""));
                    }
                    event
                }
                Event::Start(Tag::Code) => {
                    if in_header {
                        temp_header.push("");
                        return Event::Html(Borrowed(""));
                    }
                    event
                }
                Event::End(Tag::Code) => {
                    if in_header {
                        temp_header.push("");
                        return Event::Html(Borrowed(""));
                    }
                    event
                }
                Event::Start(Tag::Header(num)) => {
                    in_header = true;
                    temp_header = TempHeader::new(num);
                    Event::Html(Borrowed(""))
                }
                Event::End(Tag::Header(_)) => {
                    // End of a header, reset all the things and return the stringified
                    // version of the header
                    in_header = false;
                    header_created = false;
                    let val = temp_header.to_string(context.tera, context.insert_anchor);
                    headers.push(temp_header.clone());
                    temp_header = TempHeader::default();
                    Event::Html(Owned(val))
                }
                Event::Html(ref markup) if markup.contains("") => {
                    has_summary = true;
                    Event::Html(Borrowed(CONTINUE_READING))
                }
                _ => event,
            }
        });

        cmark::html::push_html(&mut html, parser);
    }

    if let Some(e) = error {
        return Err(e)
    } else {
        html = html.replace("

", "").replace("

", "
");
        Ok(Rendered {
            summary_len: if has_summary { html.find(CONTINUE_READING) } else { None },
            body: html,
            toc: make_table_of_contents(&headers)
        })
    }
}
<commit_msg>Derive debug for markdown::Rendered<commit_after>use std::borrow::Cow::{Owned, Borrowed};

use pulldown_cmark as cmark;
use self::cmark::{Parser, Event, Tag, Options, OPTION_ENABLE_TABLES, OPTION_ENABLE_FOOTNOTES};
use slug::slugify;
use syntect::easy::HighlightLines;
use syntect::html::{start_coloured_html_snippet, styles_to_coloured_html, IncludeBackground};

use errors::Result;
use utils::site::resolve_internal_link;
use highlighting::{get_highlighter, THEME_SET};
use link_checker::check_url;

use table_of_contents::{TempHeader, Header, make_table_of_contents};
use context::RenderContext;

const CONTINUE_READING: &str = "

\n";

#[derive(Debug)]
pub struct Rendered {
    pub body: String,
    pub summary_len: Option<usize>,
    pub toc: Vec
}

// We might have cases where the slug is already present in our list of anchor
// for example an article could have several titles named Example
// We add a counter after the slug if the slug is already present, which
// means we will have example, example-1, example-2 etc
fn find_anchor(anchors: &[String], name: String, level: u8) -> String {
    if level == 0 && !anchors.contains(&name) {
        return name.to_string();
    }

    let new_anchor = format!("{}-{}", name, level + 1);
    if !anchors.contains(&new_anchor) {
        return new_anchor;
    }

    find_anchor(anchors, name, level + 1)
}

fn is_colocated_asset_link(link: &str) -> bool {
    !link.contains("/")  // http://, ftp://, ../ etc
        && !link.starts_with("mailto:")
}

pub fn markdown_to_html(content: &str, context: &RenderContext) -> Result<Rendered> {
    // the rendered html
    let mut html = String::with_capacity(content.len());
    // Set while parsing
    let mut error = None;

    let mut highlighter: Option<HighlightLines> = None;
    // If we get text in header, we need to insert the id and a anchor
    let mut in_header = false;
    // pulldown_cmark can send several text events for a title if there are markdown
    // specific characters like `!` in them. We only want to insert the anchor the first time
    let mut header_created = false;
    let mut anchors: Vec<String> = vec![];

    let mut headers = vec![];
    // Defaults to a 0 level so not a real header
    // It should be an Option ideally but not worth the hassle to update
    let mut temp_header = TempHeader::default();

    let mut opts = Options::empty();
    let mut has_summary = false;
    opts.insert(OPTION_ENABLE_TABLES);
    opts.insert(OPTION_ENABLE_FOOTNOTES);

    {
        let parser = Parser::new_ext(content, opts).map(|event| {
            match event {
                Event::Text(text) => {
                    // Header first
                    if in_header {
                        if header_created {
                            temp_header.push(&text);
                            return Event::Html(Borrowed(""));
                        }
                        let id = find_anchor(&anchors, slugify(&text), 0);
                        anchors.push(id.clone());
                        // update the header and add it to the list
                        temp_header.permalink = format!("{}#{}", context.current_page_permalink, id);
                        temp_header.id = id;
                        // += as we might have some  or other things already there
                        temp_header.title += &text;
                        header_created = true;
                        return Event::Html(Borrowed(""));
                    }

                    // if we are in the middle of a code block
                    if let Some(ref mut highlighter) = highlighter {
                        let highlighted = &highlighter.highlight(&text);
                        let html = styles_to_coloured_html(highlighted, IncludeBackground::Yes);
                        return Event::Html(Owned(html));
                    }

                    // Business as usual
                    Event::Text(text)
                }
                Event::Start(Tag::CodeBlock(ref info)) => {
                    if !context.config.highlight_code {
                        return Event::Html(Borrowed("
"));
                    }

                    let theme = &THEME_SET.themes[&context.config.highlight_theme];
                    match get_highlighter(&theme, info, context.base_path, &context.config.extra_syntaxes) {
                        Ok(h) => highlighter = Some(h),
                        Err(err) => {
                            error = Some(format!("Could not load syntax: {}", err).into());
                            return Event::Html(Borrowed(""));
                        }
                    }
                    let snippet = start_coloured_html_snippet(theme);
                    Event::Html(Owned(snippet))
                }
                Event::End(Tag::CodeBlock(_)) => {
                    if !context.config.highlight_code {
                        return Event::Html(Borrowed("
\n"));
                    }
                    // reset highlight and close the code block
                    highlighter = None;
                    Event::Html(Borrowed("
"))
                }
                Event::Start(Tag::Image(src, title)) => {
                    if is_colocated_asset_link(&src) {
                        return Event::Start(
                            Tag::Image(
                                Owned(format!("{}{}", context.current_page_permalink, src)),
                                title,
                            )
                        );
                    }

                    Event::Start(Tag::Image(src, title))
                }
                Event::Start(Tag::Link(link, title)) => {
                    // A few situations here:
                    // - it could be a relative link (starting with `./`)
                    // - it could be a link to a co-located asset
                    // - it could be a normal link
                    // - any of those can be in a header or not: if it's in a header
                    //   we need to append to a string
                    let fixed_link = if link.starts_with("./") {
                        match resolve_internal_link(&link, context.permalinks) {
                            Ok(url) => url,
                            Err(_) => {
                                error = Some(format!("Relative link {} not found.", link).into());
                                return Event::Html(Borrowed(""));
                            }
                        }
                    } else if is_colocated_asset_link(&link) {
                        format!("{}{}", context.current_page_permalink, link)
                    } else {
                        if context.config.check_external_links && !link.starts_with('#') {
                            let res = check_url(&link);
                            if res.is_valid() {
                                link.to_string()
                            } else {
                                error = Some(
                                    format!("Link {} is not valid: {}", link, res.message()).into()
                                );
                                String::new()
                            }
                        } else {
                            link.to_string()
                        }
                    };

                    if in_header {
                        let html = if title.is_empty() {
                            format!("", fixed_link)
                        } else {
                            format!("", fixed_link, title)
                        };
                        temp_header.push(&html);
                        return Event::Html(Borrowed(""));
                    }

                    Event::Start(Tag::Link(Owned(fixed_link), title))
                }
                Event::End(Tag::Link(_, _)) => {
                    if in_header {
                        temp_header.push("");
                        return Event::Html(Borrowed(""));
                    }
                    event
                }
                Event::Start(Tag::Code) => {
                    if in_header {
                        temp_header.push("");
                        return Event::Html(Borrowed(""));
                    }
                    event
                }
                Event::End(Tag::Code) => {
                    if in_header {
                        temp_header.push("");
                        return Event::Html(Borrowed(""));
                    }
                    event
                }
                Event::Start(Tag::Header(num)) => {
                    in_header = true;
                    temp_header = TempHeader::new(num);
                    Event::Html(Borrowed(""))
                }
                Event::End(Tag::Header(_)) => {
                    // End of a header, reset all the things and return the stringified
                    // version of the header
                    in_header = false;
                    header_created = false;
                    let val = temp_header.to_string(context.tera, context.insert_anchor);
                    headers.push(temp_header.clone());
                    temp_header = TempHeader::default();
                    Event::Html(Owned(val))
                }
                Event::Html(ref markup) if markup.contains("") => {
                    has_summary = true;
                    Event::Html(Borrowed(CONTINUE_READING))
                }
                _ => event,
            }
        });

        cmark::html::push_html(&mut html, parser);
    }

    if let Some(e) = error {
        return Err(e)
    } else {
        html = html.replace("

", "").replace("

", "
");
        Ok(Rendered {
            summary_len: if has_summary { html.find(CONTINUE_READING) } else { None },
            body: html,
            toc: make_table_of_contents(&headers)
        })
    }
}
<|endoftext|>


<commit_before>//! Implementation of the `Session` trait through various implementations
//! automatically choosing the best available interface.
//!
//! ## How to use it
//!
//! ### Initialization
//!
//! To initialize a session just call `AutoSession::new`. A new session will be opened, if the
//! any available interface is successful and will be closed once the `AutoSessionNotifier` is dropped.
//!
//! ### Usage of the session
//!
//! The session may be used to open devices manually through the `Session` interface
//! or be passed to other objects that need it to open devices themselves.
//! The `AutoSession` is clonable and may be passed to multiple devices easily.
//!
//! Examples for those are e.g. the `LibinputInputBackend` (its context might be initialized through a
//! `Session` via the `LibinputSessionInterface`) or the `UdevBackend`.
//!
//! ### Usage of the session notifier
//!
//! The notifier might be used to pause device access, when the session gets paused (e.g. by
//! switching the tty via `AutoSession::change_vt`) and to automatically enable it again,
//! when the session becomes active again.
//!
//! It is crutial to avoid errors during that state. Examples for object that might be registered
//! for notifications are the `Libinput` context, the `UdevBackend` or a `DrmDevice` (handled
//! automatically by the `UdevBackend`, if not done manually).
//! ```

use super::direct::{self, direct_session_bind, BoundDirectSession, DirectSession, DirectSessionNotifier};
#[cfg(feature = "backend_session_logind")]
use super::logind::{self, logind_session_bind, BoundLogindSession, LogindSession, LogindSessionNotifier};
use super::{AsErrno, AsSessionObserver, Session, SessionNotifier, SessionObserver};
use nix::fcntl::OFlag;
use std::cell::RefCell;
use std::io::Error as IoError;
use std::os::unix::io::RawFd;
use std::path::Path;
use std::rc::Rc;

use wayland_server::calloop::LoopHandle;

/// `Session` using the best available inteface
#[derive(Clone)]
pub enum AutoSession {
    /// Logind session
    #[cfg(feature = "backend_session_logind")]
    Logind(LogindSession),
    /// Direct / tty session
    Direct(Rc<RefCell<DirectSession>>),
}

/// `SessionNotifier` using the best available inteface
pub enum AutoSessionNotifier {
    /// Logind session nofifier
    #[cfg(feature = "backend_session_logind")]
    Logind(LogindSessionNotifier),
    /// Direct / tty session notifier
    Direct(DirectSessionNotifier),
}

/// Bound session that is driven by the `calloop::EventLoop`.
///
/// See `auto_session_bind` for details.
///
/// Dropping this object will close the session just like the `AutoSessionNotifier`.
pub enum BoundAutoSession {
    /// Bound logind session
    #[cfg(feature = "backend_session_logind")]
    Logind(BoundLogindSession),
    /// Bound direct / tty session
    Direct(BoundDirectSession),
}

/// Id's used by the `AutoSessionNotifier` internally.
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub struct AutoId(AutoIdInternal);
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
enum AutoIdInternal {
    #[cfg(feature = "backend_session_logind")]
    Logind(logind::Id),
    Direct(direct::Id),
}

impl AutoSession {
    /// Tries to create a new session via the best available interface.
    #[cfg(feature = "backend_session_logind")]
    pub fn new<L>(logger: L) -> Option<(AutoSession, AutoSessionNotifier)>
    where
        L: Into<Option<::slog::Logger>>,
    {
        let logger = ::slog_or_stdlog(logger)
            .new(o!("smithay_module" => "backend_session_auto", "session_type" => "auto"));

        info!(logger, "Trying to create logind session");
        match LogindSession::new(logger.clone()) {
            Ok((session, notifier)) => Some((
                AutoSession::Logind(session),
                AutoSessionNotifier::Logind(notifier),
            )),
            Err(err) => {
                warn!(logger, "Failed to create logind session: {}", err);
                info!(logger, "Falling back to create tty session");
                match DirectSession::new(None, logger.clone()) {
                    Ok((session, notifier)) => Some((
                        AutoSession::Direct(Rc::new(RefCell::new(session))),
                        AutoSessionNotifier::Direct(notifier),
                    )),
                    Err(err) => {
                        warn!(logger, "Failed to create direct session: {}", err);
                        error!(logger, "Could not create any session, possibilities exhausted");
                        None
                    }
                }
            }
        }
    }

    #[cfg(not(feature = "backend_session_logind"))]
    pub fn new<L>(logger: L) -> Option<(AutoSession, AutoSessionNotifier)>
    where
        L: Into<Option<::slog::Logger>>,
    {
        let logger = ::slog_or_stdlog(logger)
            .new(o!("smithay_module" => "backend_session_auto", "session_type" => "auto"));

        info!(logger, "Trying to create tty session");
        match DirectSession::new(None, logger.clone()) {
            Ok((session, notifier)) => Some((
                AutoSession::Direct(Rc::new(RefCell::new(session))),
                AutoSessionNotifier::Direct(notifier),
            )),
            Err(err) => {
                warn!(logger, "Failed to create direct session: {}", err);
                error!(logger, "Could not create any session, possibilities exhausted");
                None
            }
        }
    }
}

/// Bind an `AutoSessionNotifier` to an `EventLoop`.
///
/// Allows the `AutoSessionNotifier` to listen for incoming signals signalling the session state.
/// If you don't use this function `AutoSessionNotifier` will not correctly tell you the
/// session state and call it's `SessionObservers`.
pub fn auto_session_bind<Data: 'static>(
    notifier: AutoSessionNotifier,
    handle: &LoopHandle<Data>,
) -> ::std::result::Result<BoundAutoSession, IoError> {
    Ok(match notifier {
        #[cfg(feature = "backend_session_logind")]
        AutoSessionNotifier::Logind(logind) => BoundAutoSession::Logind(logind_session_bind(logind, handle).map_err(|(e, _)| e)?),
        AutoSessionNotifier::Direct(direct) => BoundAutoSession::Direct(direct_session_bind(direct, handle)?),
    })
}

impl Session for AutoSession {
    type Error = Error;

    fn open(&mut self, path: &Path, flags: OFlag) -> Result<RawFd> {
        match *self {
            #[cfg(feature = "backend_session_logind")]
            AutoSession::Logind(ref mut logind) => logind.open(path, flags).map_err(|e| e.into()),
            AutoSession::Direct(ref mut direct) => direct.open(path, flags).map_err(|e| e.into()),
        }
    }
    fn close(&mut self, fd: RawFd) -> Result<()> {
        match *self {
            #[cfg(feature = "backend_session_logind")]
            AutoSession::Logind(ref mut logind) => logind.close(fd).map_err(|e| e.into()),
            AutoSession::Direct(ref mut direct) => direct.close(fd).map_err(|e| e.into()),
        }
    }

    fn change_vt(&mut self, vt: i32) -> Result<()> {
        match *self {
            #[cfg(feature = "backend_session_logind")]
            AutoSession::Logind(ref mut logind) => logind.change_vt(vt).map_err(|e| e.into()),
            AutoSession::Direct(ref mut direct) => direct.change_vt(vt).map_err(|e| e.into()),
        }
    }

    fn is_active(&self) -> bool {
        match *self {
            #[cfg(feature = "backend_session_logind")]
            AutoSession::Logind(ref logind) => logind.is_active(),
            AutoSession::Direct(ref direct) => direct.is_active(),
        }
    }
    fn seat(&self) -> String {
        match *self {
            #[cfg(feature = "backend_session_logind")]
            AutoSession::Logind(ref logind) => logind.seat(),
            AutoSession::Direct(ref direct) => direct.seat(),
        }
    }
}

impl SessionNotifier for AutoSessionNotifier {
    type Id = AutoId;

    fn register<S: SessionObserver + 'static, A: AsSessionObserver<S>>(
        &mut self,
        signal: &mut A,
    ) -> Self::Id {
        match *self {
            #[cfg(feature = "backend_session_logind")]
            AutoSessionNotifier::Logind(ref mut logind) => {
                AutoId(AutoIdInternal::Logind(logind.register(signal)))
            }
            AutoSessionNotifier::Direct(ref mut direct) => {
                AutoId(AutoIdInternal::Direct(direct.register(signal)))
            }
        }
    }

    fn unregister(&mut self, signal: Self::Id) {
        #[allow(unreachable_patterns)]
        match (self, signal) {
            #[cfg(feature = "backend_session_logind")]
            (&mut AutoSessionNotifier::Logind(ref mut logind), AutoId(AutoIdInternal::Logind(signal))) => {
                logind.unregister(signal)
            }
            (&mut AutoSessionNotifier::Direct(ref mut direct), AutoId(AutoIdInternal::Direct(signal))) => {
                direct.unregister(signal)
            }
            // this pattern is needed when the logind backend is activated
            _ => unreachable!()
        }
    }

    fn is_active(&self) -> bool {
        match *self {
            #[cfg(feature = "backend_session_logind")]
            AutoSessionNotifier::Logind(ref logind) => logind.is_active(),
            AutoSessionNotifier::Direct(ref direct) => direct.is_active(),
        }
    }
    fn seat(&self) -> &str {
        match *self {
            #[cfg(feature = "backend_session_logind")]
            AutoSessionNotifier::Logind(ref logind) => logind.seat(),
            AutoSessionNotifier::Direct(ref direct) => direct.seat(),
        }
    }
}

impl BoundAutoSession {
    /// Unbind the session from the `EventLoop` again
    pub fn unbind(self) -> AutoSessionNotifier {
        match self {
            #[cfg(feature = "backend_session_logind")]
            BoundAutoSession::Logind(logind) => AutoSessionNotifier::Logind(logind.unbind()),
            BoundAutoSession::Direct(direct) => AutoSessionNotifier::Direct(direct.unbind()),
        }
    }
}

error_chain! {
    links {
        Logind(logind::Error, logind::ErrorKind) #[cfg(feature = "backend_session_logind")] #[doc = "Underlying logind session error"];
    }

    foreign_links {
        Direct(::nix::Error) #[doc = "Underlying direct tty session error"];
    }
}

impl AsErrno for Error {
    fn as_errno(&self) -> Option<i32> {
        //TODO figure this out, I don't see a way..
        None
    }
}
<commit_msg>cargo fmt<commit_after>//! Implementation of the `Session` trait through various implementations
//! automatically choosing the best available interface.
//!
//! ## How to use it
//!
//! ### Initialization
//!
//! To initialize a session just call `AutoSession::new`. A new session will be opened, if the
//! any available interface is successful and will be closed once the `AutoSessionNotifier` is dropped.
//!
//! ### Usage of the session
//!
//! The session may be used to open devices manually through the `Session` interface
//! or be passed to other objects that need it to open devices themselves.
//! The `AutoSession` is clonable and may be passed to multiple devices easily.
//!
//! Examples for those are e.g. the `LibinputInputBackend` (its context might be initialized through a
//! `Session` via the `LibinputSessionInterface`) or the `UdevBackend`.
//!
//! ### Usage of the session notifier
//!
//! The notifier might be used to pause device access, when the session gets paused (e.g. by
//! switching the tty via `AutoSession::change_vt`) and to automatically enable it again,
//! when the session becomes active again.
//!
//! It is crutial to avoid errors during that state. Examples for object that might be registered
//! for notifications are the `Libinput` context, the `UdevBackend` or a `DrmDevice` (handled
//! automatically by the `UdevBackend`, if not done manually).
//! ```

use super::direct::{self, direct_session_bind, BoundDirectSession, DirectSession, DirectSessionNotifier};
#[cfg(feature = "backend_session_logind")]
use super::logind::{self, logind_session_bind, BoundLogindSession, LogindSession, LogindSessionNotifier};
use super::{AsErrno, AsSessionObserver, Session, SessionNotifier, SessionObserver};
use nix::fcntl::OFlag;
use std::cell::RefCell;
use std::io::Error as IoError;
use std::os::unix::io::RawFd;
use std::path::Path;
use std::rc::Rc;

use wayland_server::calloop::LoopHandle;

/// `Session` using the best available inteface
#[derive(Clone)]
pub enum AutoSession {
    /// Logind session
    #[cfg(feature = "backend_session_logind")]
    Logind(LogindSession),
    /// Direct / tty session
    Direct(Rc<RefCell<DirectSession>>),
}

/// `SessionNotifier` using the best available inteface
pub enum AutoSessionNotifier {
    /// Logind session nofifier
    #[cfg(feature = "backend_session_logind")]
    Logind(LogindSessionNotifier),
    /// Direct / tty session notifier
    Direct(DirectSessionNotifier),
}

/// Bound session that is driven by the `calloop::EventLoop`.
///
/// See `auto_session_bind` for details.
///
/// Dropping this object will close the session just like the `AutoSessionNotifier`.
pub enum BoundAutoSession {
    /// Bound logind session
    #[cfg(feature = "backend_session_logind")]
    Logind(BoundLogindSession),
    /// Bound direct / tty session
    Direct(BoundDirectSession),
}

/// Id's used by the `AutoSessionNotifier` internally.
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub struct AutoId(AutoIdInternal);
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
enum AutoIdInternal {
    #[cfg(feature = "backend_session_logind")]
    Logind(logind::Id),
    Direct(direct::Id),
}

impl AutoSession {
    /// Tries to create a new session via the best available interface.
    #[cfg(feature = "backend_session_logind")]
    pub fn new<L>(logger: L) -> Option<(AutoSession, AutoSessionNotifier)>
    where
        L: Into<Option<::slog::Logger>>,
    {
        let logger = ::slog_or_stdlog(logger)
            .new(o!("smithay_module" => "backend_session_auto", "session_type" => "auto"));

        info!(logger, "Trying to create logind session");
        match LogindSession::new(logger.clone()) {
            Ok((session, notifier)) => Some((
                AutoSession::Logind(session),
                AutoSessionNotifier::Logind(notifier),
            )),
            Err(err) => {
                warn!(logger, "Failed to create logind session: {}", err);
                info!(logger, "Falling back to create tty session");
                match DirectSession::new(None, logger.clone()) {
                    Ok((session, notifier)) => Some((
                        AutoSession::Direct(Rc::new(RefCell::new(session))),
                        AutoSessionNotifier::Direct(notifier),
                    )),
                    Err(err) => {
                        warn!(logger, "Failed to create direct session: {}", err);
                        error!(logger, "Could not create any session, possibilities exhausted");
                        None
                    }
                }
            }
        }
    }

    #[cfg(not(feature = "backend_session_logind"))]
    pub fn new<L>(logger: L) -> Option<(AutoSession, AutoSessionNotifier)>
    where
        L: Into<Option<::slog::Logger>>,
    {
        let logger = ::slog_or_stdlog(logger)
            .new(o!("smithay_module" => "backend_session_auto", "session_type" => "auto"));

        info!(logger, "Trying to create tty session");
        match DirectSession::new(None, logger.clone()) {
            Ok((session, notifier)) => Some((
                AutoSession::Direct(Rc::new(RefCell::new(session))),
                AutoSessionNotifier::Direct(notifier),
            )),
            Err(err) => {
                warn!(logger, "Failed to create direct session: {}", err);
                error!(logger, "Could not create any session, possibilities exhausted");
                None
            }
        }
    }
}

/// Bind an `AutoSessionNotifier` to an `EventLoop`.
///
/// Allows the `AutoSessionNotifier` to listen for incoming signals signalling the session state.
/// If you don't use this function `AutoSessionNotifier` will not correctly tell you the
/// session state and call it's `SessionObservers`.
pub fn auto_session_bind<Data: 'static>(
    notifier: AutoSessionNotifier,
    handle: &LoopHandle<Data>,
) -> ::std::result::Result<BoundAutoSession, IoError> {
    Ok(match notifier {
        #[cfg(feature = "backend_session_logind")]
        AutoSessionNotifier::Logind(logind) => {
            BoundAutoSession::Logind(logind_session_bind(logind, handle).map_err(|(e, _)| e)?)
        }
        AutoSessionNotifier::Direct(direct) => BoundAutoSession::Direct(direct_session_bind(direct, handle)?),
    })
}

impl Session for AutoSession {
    type Error = Error;

    fn open(&mut self, path: &Path, flags: OFlag) -> Result<RawFd> {
        match *self {
            #[cfg(feature = "backend_session_logind")]
            AutoSession::Logind(ref mut logind) => logind.open(path, flags).map_err(|e| e.into()),
            AutoSession::Direct(ref mut direct) => direct.open(path, flags).map_err(|e| e.into()),
        }
    }
    fn close(&mut self, fd: RawFd) -> Result<()> {
        match *self {
            #[cfg(feature = "backend_session_logind")]
            AutoSession::Logind(ref mut logind) => logind.close(fd).map_err(|e| e.into()),
            AutoSession::Direct(ref mut direct) => direct.close(fd).map_err(|e| e.into()),
        }
    }

    fn change_vt(&mut self, vt: i32) -> Result<()> {
        match *self {
            #[cfg(feature = "backend_session_logind")]
            AutoSession::Logind(ref mut logind) => logind.change_vt(vt).map_err(|e| e.into()),
            AutoSession::Direct(ref mut direct) => direct.change_vt(vt).map_err(|e| e.into()),
        }
    }

    fn is_active(&self) -> bool {
        match *self {
            #[cfg(feature = "backend_session_logind")]
            AutoSession::Logind(ref logind) => logind.is_active(),
            AutoSession::Direct(ref direct) => direct.is_active(),
        }
    }
    fn seat(&self) -> String {
        match *self {
            #[cfg(feature = "backend_session_logind")]
            AutoSession::Logind(ref logind) => logind.seat(),
            AutoSession::Direct(ref direct) => direct.seat(),
        }
    }
}

impl SessionNotifier for AutoSessionNotifier {
    type Id = AutoId;

    fn register<S: SessionObserver + 'static, A: AsSessionObserver<S>>(
        &mut self,
        signal: &mut A,
    ) -> Self::Id {
        match *self {
            #[cfg(feature = "backend_session_logind")]
            AutoSessionNotifier::Logind(ref mut logind) => {
                AutoId(AutoIdInternal::Logind(logind.register(signal)))
            }
            AutoSessionNotifier::Direct(ref mut direct) => {
                AutoId(AutoIdInternal::Direct(direct.register(signal)))
            }
        }
    }

    fn unregister(&mut self, signal: Self::Id) {
        #[allow(unreachable_patterns)]
        match (self, signal) {
            #[cfg(feature = "backend_session_logind")]
            (&mut AutoSessionNotifier::Logind(ref mut logind), AutoId(AutoIdInternal::Logind(signal))) => {
                logind.unregister(signal)
            }
            (&mut AutoSessionNotifier::Direct(ref mut direct), AutoId(AutoIdInternal::Direct(signal))) => {
                direct.unregister(signal)
            }
            // this pattern is needed when the logind backend is activated
            _ => unreachable!(),
        }
    }

    fn is_active(&self) -> bool {
        match *self {
            #[cfg(feature = "backend_session_logind")]
            AutoSessionNotifier::Logind(ref logind) => logind.is_active(),
            AutoSessionNotifier::Direct(ref direct) => direct.is_active(),
        }
    }
    fn seat(&self) -> &str {
        match *self {
            #[cfg(feature = "backend_session_logind")]
            AutoSessionNotifier::Logind(ref logind) => logind.seat(),
            AutoSessionNotifier::Direct(ref direct) => direct.seat(),
        }
    }
}

impl BoundAutoSession {
    /// Unbind the session from the `EventLoop` again
    pub fn unbind(self) -> AutoSessionNotifier {
        match self {
            #[cfg(feature = "backend_session_logind")]
            BoundAutoSession::Logind(logind) => AutoSessionNotifier::Logind(logind.unbind()),
            BoundAutoSession::Direct(direct) => AutoSessionNotifier::Direct(direct.unbind()),
        }
    }
}

error_chain! {
    links {
        Logind(logind::Error, logind::ErrorKind) #[cfg(feature = "backend_session_logind")] #[doc = "Underlying logind session error"];
    }

    foreign_links {
        Direct(::nix::Error) #[doc = "Underlying direct tty session error"];
    }
}

impl AsErrno for Error {
    fn as_errno(&self) -> Option<i32> {
        //TODO figure this out, I don't see a way..
        None
    }
}
<|endoftext|>


<commit_before>// Copyright 2014 The Gfx-rs Developers.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

extern crate cgmath;
#[macro_use]
extern crate gfx;
extern crate gfx_core;
extern crate gfx_window_glutin;
extern crate glutin;
extern crate gfx_gl as gl;
extern crate gfx_device_gl;

use cgmath::{SquareMatrix, Matrix, Point3, Vector3, Matrix3, Matrix4};
use cgmath::{Transform, Vector4};
pub use gfx::format::{DepthStencil, Rgba8 as ColorFormat};
//use glfw::Context;
use gl::Gl;
use gl::types::*;
use std::mem;
use std::ptr;
use std::str;
use std::env;
use std::str::FromStr;
use std::iter::repeat;
use std::ffi::CString;
use std::time::{Duration, Instant};
use gfx_device_gl::{Resources as R, CommandBuffer as CB};
use gfx_core::Device;

gfx_defines!{
    vertex Vertex {
        pos: [f32; 3] = "a_Pos",
    }

    pipeline pipe {
        vbuf: gfx::VertexBuffer<Vertex> = (),
        transform: gfx::Global<[[f32; 4]; 4]> = "u_Transform",
        out_color: gfx::RenderTarget<ColorFormat> = "o_Color",
    }
}

static VERTEX_SRC: &'static [u8] = b"
    #version 150 core
    in vec3 a_Pos;
    uniform mat4 u_Transform;

    void main() {
        gl_Position = u_Transform * vec4(a_Pos, 1.0);
   }
";

static FRAGMENT_SRC: &'static [u8] = b"
    #version 150 core
    out vec4 o_Color;

    void main() {
        o_Color = vec4(1.0, 0.0, 0.0, 1.0);
    }
";

static VERTEX_DATA: &'static [Vertex] = &[
    Vertex { pos: [-1.0, 0.0, -1.0] },
    Vertex { pos: [ 1.0, 0.0, -1.0] },
    Vertex { pos: [-1.0, 0.0,  1.0] },
];

const CLEAR_COLOR: (f32, f32, f32, f32) = (0.3, 0.3, 0.3, 1.0);

//----------------------------------------

fn transform(x: i16, y: i16, proj_view: &Matrix4<f32>) -> Matrix4<f32> {
    let mut model = Matrix4::from(Matrix3::identity() * 0.05);
    model.w = Vector4::new(x as f32 * 0.10,
                           0f32,
                           y as f32 * 0.10,
                           1f32);
    proj_view * model
}

trait Renderer: Drop {
    fn render(&mut self, proj_view: &Matrix4<f32>);
    fn window(&mut self) -> &glutin::Window;
}

struct GFX {
    dimension: i16,
    window: glutin::Window,
    device:gfx_device_gl::Device,
    encoder: gfx::Encoder<R,CB>,
    data: pipe::Data<R>,
    pso: gfx::PipelineState<R, pipe::Meta>,
    slice: gfx::Slice<R>,
}

struct GL {
    dimension: i16,
    window: glutin::Window,
    gl:Gl,
    trans_uniform:GLint,
    vs:GLuint,
    fs:GLuint,
    program:GLuint,
    vbo:GLuint,
    vao:GLuint,
}

impl GFX {
    fn new(builder: glutin::WindowBuilder, dimension: i16) -> Self {
        use gfx::traits::FactoryExt;

        let (window, device, mut factory, main_color, _) =
            gfx_window_glutin::init::<ColorFormat, DepthStencil>(builder);
        let encoder: gfx::Encoder<_,_> = factory.create_command_buffer().into();

        let pso = factory.create_pipeline_simple(
            VERTEX_SRC, FRAGMENT_SRC,
            pipe::new()
        ).unwrap();

        let (vbuf, slice) = factory.create_vertex_buffer_with_slice(VERTEX_DATA,());

        let data = pipe::Data {
            vbuf: vbuf,
            transform: cgmath::Matrix4::identity().into(),
            out_color: main_color,
        };

        GFX {
            window: window,
            dimension: dimension,
            device: device,
            encoder: encoder,
            data: data,
            pso: pso,
            slice: slice,
        }
    }
}

fn duration_to_f64(dur: Duration) -> f64 {
   dur.as_secs() as f64 + dur.subsec_nanos() as f64 / 1000_000_000.0
}

impl Renderer for GFX {
    fn render(&mut self, proj_view: &Matrix4<f32>) {
        let start = Instant::now();
        self.encoder.clear(&self.data.out_color, [CLEAR_COLOR.0,
                                                  CLEAR_COLOR.1,
                                                  CLEAR_COLOR.2,
                                                  CLEAR_COLOR.3]);

        for x in (-self.dimension) ..self.dimension {
            for y in (-self.dimension) ..self.dimension {
                self.data.transform = transform(x, y, proj_view).into();
                self.encoder.draw(&self.slice, &self.pso, &self.data);
            }
        }

        let pre_submit = start.elapsed();
        self.encoder.flush(&mut self.device);
        let post_submit = start.elapsed();
        self.window.swap_buffers().unwrap();
        self.device.cleanup();
        let swap = start.elapsed();

        println!("total time:\t\t{0:4.2}ms", duration_to_f64(swap));
        println!("\tcreate list:\t{0:4.2}ms", duration_to_f64(pre_submit));
        println!("\tsubmit:\t\t{0:4.2}ms", duration_to_f64(post_submit - pre_submit));
        println!("\tgpu wait:\t{0:4.2}ms", duration_to_f64(swap - post_submit));
    }
    fn window(&mut self) -> &glutin::Window { &self.window }
}

impl Drop for GFX {
    fn drop(&mut self) {
    }
}

impl GL {
    fn new(builder: glutin::WindowBuilder, dimension: i16) -> Self {
        fn compile_shader (gl:&Gl, src: &[u8], ty: GLenum) -> GLuint {
            unsafe {
                let shader = gl.CreateShader(ty);
                // Attempt to compile the shader
                gl.ShaderSource(shader, 1,
                                &(src.as_ptr() as *const i8),
                                &(src.len() as GLint));
                gl.CompileShader(shader);

                // Get the compile status
                let mut status = gl::FALSE as GLint;
                gl.GetShaderiv(shader, gl::COMPILE_STATUS, &mut status);

                // Fail on error
                if status != (gl::TRUE as GLint) {
                    let mut len: GLint = 0;
                    gl.GetShaderiv(shader, gl::INFO_LOG_LENGTH, &mut len);

                    // allocate a buffer of size (len - 1) to skip the trailing null character
                    let mut buf: Vec<u8> = repeat(0u8).take((len as usize).saturating_sub(1)).collect();
                    gl.GetShaderInfoLog(shader, len, ptr::null_mut(), buf.as_mut_ptr() as *mut GLchar);
                    panic!("{}", str::from_utf8(&buf).ok().expect("ShaderInfoLog not valid utf8"));
                }
                shader
            }
        };

        let window = builder.build().unwrap();
        unsafe { window.make_current().unwrap() };
        let gl = Gl::load_with(|s| window.get_proc_address(s) as *const _);

        // Create GLSL shaders
        let vs = compile_shader(&gl, VERTEX_SRC, gl::VERTEX_SHADER);
        let fs = compile_shader(&gl, FRAGMENT_SRC, gl::FRAGMENT_SHADER);

        // Link program
        let program;
        unsafe {
            program = gl.CreateProgram();
            gl.AttachShader(program, vs);
            gl.AttachShader(program, fs);
            gl.LinkProgram(program);
            // Get the link status
            let mut status = gl::FALSE as GLint;
            gl.GetProgramiv(program, gl::LINK_STATUS, &mut status);

            // Fail on error
            if status != (gl::TRUE as GLint) {
                let mut len: GLint = 0;
                gl.GetProgramiv(program, gl::INFO_LOG_LENGTH, &mut len);

                // allocate a buffer of size (len - 1) to skip the trailing null character
                let mut buf: Vec<u8> = repeat(0u8).take((len as usize).saturating_sub(1)).collect();
                gl.GetProgramInfoLog(program, len, ptr::null_mut(), buf.as_mut_ptr() as *mut GLchar);
                panic!("{}", str::from_utf8(&buf).ok().expect("ProgramInfoLog not valid utf8"));
            }
        }

        let mut vao = 0;
        let mut vbo = 0;

        let trans_uniform;
        unsafe {
            // Create Vertex Array Object
            gl.GenVertexArrays(1, &mut vao);
            gl.BindVertexArray(vao);

            // Create a Vertex Buffer Object and copy the vertex data to it
            gl.GenBuffers(1, &mut vbo);
            gl.BindBuffer(gl::ARRAY_BUFFER, vbo);

            gl.BufferData(gl::ARRAY_BUFFER,
                          (VERTEX_DATA.len() * mem::size_of::<Vertex>()) as GLsizeiptr,
                          mem::transmute(&VERTEX_DATA[0]),
                          gl::STATIC_DRAW);

            // Use shader program
            gl.UseProgram(program);
            let o_color = CString::new("o_Color").unwrap();
            gl.BindFragDataLocation(program, 0, o_color.as_bytes_with_nul().as_ptr() as *const i8);

            // Specify the layout of the vertex data
            let a_pos = CString::new("a_Pos").unwrap();
            gl.BindFragDataLocation(program, 0, a_pos.as_bytes_with_nul().as_ptr() as *const i8);

            let pos_attr = gl.GetAttribLocation(program, a_pos.as_ptr());
            gl.EnableVertexAttribArray(pos_attr as GLuint);
            gl.VertexAttribPointer(pos_attr as GLuint, 3, gl::FLOAT,
                                   gl::FALSE as GLboolean, 0, ptr::null());

            let u_transform = CString::new("u_Transform").unwrap();
            trans_uniform = gl.GetUniformLocation(program, u_transform.as_bytes_with_nul().as_ptr() as *const i8)
        };

        GL {
            window: window,
            dimension: dimension,
            gl: gl,
            vs: vs,
            fs: fs,
            program: program,
            vbo: vbo,
            vao: vao,
            trans_uniform: trans_uniform,
        }
    }
}

fn duration_to_ms(dur: Duration) -> f64 {
    (dur.as_secs() * 1000) as f64 + dur.subsec_nanos() as f64 / 1000_000.0
}

impl Renderer for GL {
    fn render(&mut self, proj_view: &Matrix4<f32>) {
        let start = Instant::now();

        // Clear the screen to black
        unsafe {
            self.gl.ClearColor(CLEAR_COLOR.0, CLEAR_COLOR.1, CLEAR_COLOR.2, CLEAR_COLOR.3);
            self.gl.Clear(gl::COLOR_BUFFER_BIT);
        }

        for x in (-self.dimension) ..self.dimension {
            for y in (-self.dimension) ..self.dimension {
                let mat:Matrix4<f32> = transform(x, y, proj_view).into();

                unsafe {
                    self.gl.UniformMatrix4fv(self.trans_uniform,
                                             1,
                                             gl::FALSE,
                                             mat.as_ptr());
                    self.gl.DrawArrays(gl::TRIANGLES, 0, 3);
                }

            }
        }

        let submit = start.elapsed();

        // Swap buffers
        self.window.swap_buffers().unwrap();
        let swap = start.elapsed();

        println!("total time:\t\t{0:4.2}ms", duration_to_ms(swap));
        println!("\tsubmit:\t\t{0:4.2}ms", duration_to_ms(submit));
        println!("\tgpu wait:\t{0:4.2}ms", duration_to_ms(swap - submit));
    }
    fn window(&mut self) -> &glutin::Window { &self.window }
}

impl Drop for GL {
    fn drop(&mut self) {
        unsafe {
            self.gl.DeleteProgram(self.program);
            self.gl.DeleteShader(self.fs);
            self.gl.DeleteShader(self.vs);
            self.gl.DeleteBuffers(1, &self.vbo);
            self.gl.DeleteVertexArrays(1, &self.vao);
        }
    }
}

fn main() {
    let ref mut args = env::args();
    let args_count = env::args().count();
    if args_count == 1 {
        println!("gfx-perf [gl|gfx] <size>");
        return;
    }

    let mode = args.nth(1).unwrap();
    let count: i32 = if args_count == 3 {
        FromStr::from_str(&args.next().unwrap()).ok()
    } else {
        None
    }.unwrap_or(10000);

    let count = ((count as f64).sqrt() / 2.) as i16;

    let builder = glutin::WindowBuilder::new()
        .with_title("Performance example".to_string())
        .with_dimensions(800, 600)
        .with_vsync();

    let mut r: Box<Renderer>;
    match mode.as_ref() {
        "gfx" => r = Box::new(GFX::new(builder, count)),
        "gl" => r = Box::new(GL::new(builder, count)),
        x => {
            panic!("{} is not a known mode", x)
        }
    }

    let proj_view = {
        let view = Matrix4::look_at(
            Point3::new(0f32, 5.0, -5.0),
            Point3::new(0f32, 0.0, 0.0),
            Vector3::unit_z(),
        );

        let proj = {
            let aspect = {
                let (w, h) = r.window().get_inner_size().unwrap();
                w as f32 / h as f32
            };
            cgmath::perspective(cgmath::deg(45.0f32), aspect, 1.0, 10.0)
        };
        proj * view
    };

    println!("count is {}", count*count*4);

    'main: loop {
        for event in r.window().poll_events() {
            match event {
                glutin::Event::KeyboardInput(_, _, Some(glutin::VirtualKeyCode::Escape)) |
                glutin::Event::Closed => break 'main,
                _ => {},
            }
        }
        r.render(&proj_view);
    }
}
<commit_msg>Display correct time in ms<commit_after>// Copyright 2014 The Gfx-rs Developers.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

extern crate cgmath;
#[macro_use]
extern crate gfx;
extern crate gfx_core;
extern crate gfx_window_glutin;
extern crate glutin;
extern crate gfx_gl as gl;
extern crate gfx_device_gl;

use cgmath::{SquareMatrix, Matrix, Point3, Vector3, Matrix3, Matrix4};
use cgmath::{Transform, Vector4};
pub use gfx::format::{DepthStencil, Rgba8 as ColorFormat};
//use glfw::Context;
use gl::Gl;
use gl::types::*;
use std::mem;
use std::ptr;
use std::str;
use std::env;
use std::str::FromStr;
use std::iter::repeat;
use std::ffi::CString;
use std::time::{Duration, Instant};
use gfx_device_gl::{Resources as R, CommandBuffer as CB};
use gfx_core::Device;

gfx_defines!{
    vertex Vertex {
        pos: [f32; 3] = "a_Pos",
    }

    pipeline pipe {
        vbuf: gfx::VertexBuffer<Vertex> = (),
        transform: gfx::Global<[[f32; 4]; 4]> = "u_Transform",
        out_color: gfx::RenderTarget<ColorFormat> = "o_Color",
    }
}

static VERTEX_SRC: &'static [u8] = b"
    #version 150 core
    in vec3 a_Pos;
    uniform mat4 u_Transform;

    void main() {
        gl_Position = u_Transform * vec4(a_Pos, 1.0);
   }
";

static FRAGMENT_SRC: &'static [u8] = b"
    #version 150 core
    out vec4 o_Color;

    void main() {
        o_Color = vec4(1.0, 0.0, 0.0, 1.0);
    }
";

static VERTEX_DATA: &'static [Vertex] = &[
    Vertex { pos: [-1.0, 0.0, -1.0] },
    Vertex { pos: [ 1.0, 0.0, -1.0] },
    Vertex { pos: [-1.0, 0.0,  1.0] },
];

const CLEAR_COLOR: (f32, f32, f32, f32) = (0.3, 0.3, 0.3, 1.0);

//----------------------------------------

fn transform(x: i16, y: i16, proj_view: &Matrix4<f32>) -> Matrix4<f32> {
    let mut model = Matrix4::from(Matrix3::identity() * 0.05);
    model.w = Vector4::new(x as f32 * 0.10,
                           0f32,
                           y as f32 * 0.10,
                           1f32);
    proj_view * model
}

trait Renderer: Drop {
    fn render(&mut self, proj_view: &Matrix4<f32>);
    fn window(&mut self) -> &glutin::Window;
}

struct GFX {
    dimension: i16,
    window: glutin::Window,
    device:gfx_device_gl::Device,
    encoder: gfx::Encoder<R,CB>,
    data: pipe::Data<R>,
    pso: gfx::PipelineState<R, pipe::Meta>,
    slice: gfx::Slice<R>,
}

struct GL {
    dimension: i16,
    window: glutin::Window,
    gl:Gl,
    trans_uniform:GLint,
    vs:GLuint,
    fs:GLuint,
    program:GLuint,
    vbo:GLuint,
    vao:GLuint,
}

impl GFX {
    fn new(builder: glutin::WindowBuilder, dimension: i16) -> Self {
        use gfx::traits::FactoryExt;

        let (window, device, mut factory, main_color, _) =
            gfx_window_glutin::init::<ColorFormat, DepthStencil>(builder);
        let encoder: gfx::Encoder<_,_> = factory.create_command_buffer().into();

        let pso = factory.create_pipeline_simple(
            VERTEX_SRC, FRAGMENT_SRC,
            pipe::new()
        ).unwrap();

        let (vbuf, slice) = factory.create_vertex_buffer_with_slice(VERTEX_DATA,());

        let data = pipe::Data {
            vbuf: vbuf,
            transform: cgmath::Matrix4::identity().into(),
            out_color: main_color,
        };

        GFX {
            window: window,
            dimension: dimension,
            device: device,
            encoder: encoder,
            data: data,
            pso: pso,
            slice: slice,
        }
    }
}

fn duration_to_f64(dur: Duration) -> f64 {
   dur.as_secs() as f64 + dur.subsec_nanos() as f64 / 1000_000_000.0
}

impl Renderer for GFX {
    fn render(&mut self, proj_view: &Matrix4<f32>) {
        let start = Instant::now();
        self.encoder.clear(&self.data.out_color, [CLEAR_COLOR.0,
                                                  CLEAR_COLOR.1,
                                                  CLEAR_COLOR.2,
                                                  CLEAR_COLOR.3]);

        for x in (-self.dimension) ..self.dimension {
            for y in (-self.dimension) ..self.dimension {
                self.data.transform = transform(x, y, proj_view).into();
                self.encoder.draw(&self.slice, &self.pso, &self.data);
            }
        }

        let pre_submit = start.elapsed();
        self.encoder.flush(&mut self.device);
        let post_submit = start.elapsed();
        self.window.swap_buffers().unwrap();
        self.device.cleanup();
        let swap = start.elapsed();

        println!("total time:\t\t{0:4.2}ms", duration_to_ms(swap));
        println!("\tcreate list:\t{0:4.2}ms", duration_to_ms(pre_submit));
        println!("\tsubmit:\t\t{0:4.2}ms", duration_to_ms(post_submit - pre_submit));
        println!("\tgpu wait:\t{0:4.2}ms", duration_to_ms(swap - post_submit));
    }
    fn window(&mut self) -> &glutin::Window { &self.window }
}

impl Drop for GFX {
    fn drop(&mut self) {
    }
}

impl GL {
    fn new(builder: glutin::WindowBuilder, dimension: i16) -> Self {
        fn compile_shader (gl:&Gl, src: &[u8], ty: GLenum) -> GLuint {
            unsafe {
                let shader = gl.CreateShader(ty);
                // Attempt to compile the shader
                gl.ShaderSource(shader, 1,
                                &(src.as_ptr() as *const i8),
                                &(src.len() as GLint));
                gl.CompileShader(shader);

                // Get the compile status
                let mut status = gl::FALSE as GLint;
                gl.GetShaderiv(shader, gl::COMPILE_STATUS, &mut status);

                // Fail on error
                if status != (gl::TRUE as GLint) {
                    let mut len: GLint = 0;
                    gl.GetShaderiv(shader, gl::INFO_LOG_LENGTH, &mut len);

                    // allocate a buffer of size (len - 1) to skip the trailing null character
                    let mut buf: Vec<u8> = repeat(0u8).take((len as usize).saturating_sub(1)).collect();
                    gl.GetShaderInfoLog(shader, len, ptr::null_mut(), buf.as_mut_ptr() as *mut GLchar);
                    panic!("{}", str::from_utf8(&buf).ok().expect("ShaderInfoLog not valid utf8"));
                }
                shader
            }
        };

        let window = builder.build().unwrap();
        unsafe { window.make_current().unwrap() };
        let gl = Gl::load_with(|s| window.get_proc_address(s) as *const _);

        // Create GLSL shaders
        let vs = compile_shader(&gl, VERTEX_SRC, gl::VERTEX_SHADER);
        let fs = compile_shader(&gl, FRAGMENT_SRC, gl::FRAGMENT_SHADER);

        // Link program
        let program;
        unsafe {
            program = gl.CreateProgram();
            gl.AttachShader(program, vs);
            gl.AttachShader(program, fs);
            gl.LinkProgram(program);
            // Get the link status
            let mut status = gl::FALSE as GLint;
            gl.GetProgramiv(program, gl::LINK_STATUS, &mut status);

            // Fail on error
            if status != (gl::TRUE as GLint) {
                let mut len: GLint = 0;
                gl.GetProgramiv(program, gl::INFO_LOG_LENGTH, &mut len);

                // allocate a buffer of size (len - 1) to skip the trailing null character
                let mut buf: Vec<u8> = repeat(0u8).take((len as usize).saturating_sub(1)).collect();
                gl.GetProgramInfoLog(program, len, ptr::null_mut(), buf.as_mut_ptr() as *mut GLchar);
                panic!("{}", str::from_utf8(&buf).ok().expect("ProgramInfoLog not valid utf8"));
            }
        }

        let mut vao = 0;
        let mut vbo = 0;

        let trans_uniform;
        unsafe {
            // Create Vertex Array Object
            gl.GenVertexArrays(1, &mut vao);
            gl.BindVertexArray(vao);

            // Create a Vertex Buffer Object and copy the vertex data to it
            gl.GenBuffers(1, &mut vbo);
            gl.BindBuffer(gl::ARRAY_BUFFER, vbo);

            gl.BufferData(gl::ARRAY_BUFFER,
                          (VERTEX_DATA.len() * mem::size_of::<Vertex>()) as GLsizeiptr,
                          mem::transmute(&VERTEX_DATA[0]),
                          gl::STATIC_DRAW);

            // Use shader program
            gl.UseProgram(program);
            let o_color = CString::new("o_Color").unwrap();
            gl.BindFragDataLocation(program, 0, o_color.as_bytes_with_nul().as_ptr() as *const i8);

            // Specify the layout of the vertex data
            let a_pos = CString::new("a_Pos").unwrap();
            gl.BindFragDataLocation(program, 0, a_pos.as_bytes_with_nul().as_ptr() as *const i8);

            let pos_attr = gl.GetAttribLocation(program, a_pos.as_ptr());
            gl.EnableVertexAttribArray(pos_attr as GLuint);
            gl.VertexAttribPointer(pos_attr as GLuint, 3, gl::FLOAT,
                                   gl::FALSE as GLboolean, 0, ptr::null());

            let u_transform = CString::new("u_Transform").unwrap();
            trans_uniform = gl.GetUniformLocation(program, u_transform.as_bytes_with_nul().as_ptr() as *const i8)
        };

        GL {
            window: window,
            dimension: dimension,
            gl: gl,
            vs: vs,
            fs: fs,
            program: program,
            vbo: vbo,
            vao: vao,
            trans_uniform: trans_uniform,
        }
    }
}

fn duration_to_ms(dur: Duration) -> f64 {
    (dur.as_secs() * 1000) as f64 + dur.subsec_nanos() as f64 / 1000_000.0
}

impl Renderer for GL {
    fn render(&mut self, proj_view: &Matrix4<f32>) {
        let start = Instant::now();

        // Clear the screen to black
        unsafe {
            self.gl.ClearColor(CLEAR_COLOR.0, CLEAR_COLOR.1, CLEAR_COLOR.2, CLEAR_COLOR.3);
            self.gl.Clear(gl::COLOR_BUFFER_BIT);
        }

        for x in (-self.dimension) ..self.dimension {
            for y in (-self.dimension) ..self.dimension {
                let mat:Matrix4<f32> = transform(x, y, proj_view).into();

                unsafe {
                    self.gl.UniformMatrix4fv(self.trans_uniform,
                                             1,
                                             gl::FALSE,
                                             mat.as_ptr());
                    self.gl.DrawArrays(gl::TRIANGLES, 0, 3);
                }

            }
        }

        let submit = start.elapsed();

        // Swap buffers
        self.window.swap_buffers().unwrap();
        let swap = start.elapsed();

        println!("total time:\t\t{0:4.2}ms", duration_to_ms(swap));
        println!("\tsubmit:\t\t{0:4.2}ms", duration_to_ms(submit));
        println!("\tgpu wait:\t{0:4.2}ms", duration_to_ms(swap - submit));
    }
    fn window(&mut self) -> &glutin::Window { &self.window }
}

impl Drop for GL {
    fn drop(&mut self) {
        unsafe {
            self.gl.DeleteProgram(self.program);
            self.gl.DeleteShader(self.fs);
            self.gl.DeleteShader(self.vs);
            self.gl.DeleteBuffers(1, &self.vbo);
            self.gl.DeleteVertexArrays(1, &self.vao);
        }
    }
}

fn main() {
    let ref mut args = env::args();
    let args_count = env::args().count();
    if args_count == 1 {
        println!("gfx-perf [gl|gfx] <size>");
        return;
    }

    let mode = args.nth(1).unwrap();
    let count: i32 = if args_count == 3 {
        FromStr::from_str(&args.next().unwrap()).ok()
    } else {
        None
    }.unwrap_or(10000);

    let count = ((count as f64).sqrt() / 2.) as i16;

    let builder = glutin::WindowBuilder::new()
        .with_title("Performance example".to_string())
        .with_dimensions(800, 600)
        .with_vsync();

    let mut r: Box<Renderer>;
    match mode.as_ref() {
        "gfx" => r = Box::new(GFX::new(builder, count)),
        "gl" => r = Box::new(GL::new(builder, count)),
        x => {
            panic!("{} is not a known mode", x)
        }
    }

    let proj_view = {
        let view = Matrix4::look_at(
            Point3::new(0f32, 5.0, -5.0),
            Point3::new(0f32, 0.0, 0.0),
            Vector3::unit_z(),
        );

        let proj = {
            let aspect = {
                let (w, h) = r.window().get_inner_size().unwrap();
                w as f32 / h as f32
            };
            cgmath::perspective(cgmath::deg(45.0f32), aspect, 1.0, 10.0)
        };
        proj * view
    };

    println!("count is {}", count*count*4);

    'main: loop {
        for event in r.window().poll_events() {
            match event {
                glutin::Event::KeyboardInput(_, _, Some(glutin::VirtualKeyCode::Escape)) |
                glutin::Event::Closed => break 'main,
                _ => {},
            }
        }
        r.render(&proj_view);
    }
}
<|endoftext|>


<commit_before>use std::ops::Index;
use std::cmp::Ordering;
use super::ST;

pub struct OrderedArrayST<K, V> {
    // Maintain an ordered array of key-value pairs.
    pairs: Vec<(K,V)>,
}

impl<K: Ord, V> OrderedArrayST<K, V> {
    fn rank(&self, key: &K) -> usize {
        let mut lo = 0;
        let mut hi = self.pairs.len() - 1;

        while lo <= hi {
            let mid = lo + (hi - lo)/2;
            if mid == 0 {
                break;
            }
            match key.cmp(&self.pairs[mid].0) {
                Ordering::Less => {
                    hi = mid - 1;
                },
                Ordering::Greater => {
                    lo = mid + 1;
                },
                _ => {
                    return mid;
                }
            }
        }
        lo
    }

    fn insertion_sort_by_key(&mut self) {
        let n = self.pairs.len();
        for i in 0 .. n {
            for j in (0 .. i).rev() {
                if self.pairs[j + 1].0 < self.pairs[j].0 {
                    self.pairs.swap(j+1, j);
                } else {
                    break;
                }
            }
        }
    }
}

impl<K: Ord, V> ST<K,V> for OrderedArrayST<K, V> {
    fn new() -> Self {
        OrderedArrayST {
            pairs: Vec::new(),
        }
    }

    // FIXME: bad implementation
    fn put(&mut self, key: K, val: V) {
        if self.get(&key).is_some() {
            self.delete(&key);
        }
        self.pairs.push((key, val));
        self.insertion_sort_by_key();
    }

    fn get(&self, key: &K) -> Option<&V> {
        if self.is_empty() {
            return None;
        }
        let i = self.rank(key);
        if i < self.pairs.len() && &self.pairs[i].0 == key {
            Some(&self.pairs[i].1)
        } else {
            None
        }
    }

    fn delete(&mut self, key: &K) {
        let i = self.rank(key);
        if i < self.pairs.len() && &self.pairs[i].0 == key {
            self.pairs.remove(i);
        }
    }

    /// is the table empty?
    fn is_empty(&self) -> bool {
        self.pairs.len() == 0
    }

    fn size(&self) -> usize {
        self.pairs.len()
    }
}

impl<K: Ord, V> Index<K> for OrderedArrayST<K, V> {
    type Output = V;

    fn index<'a>(&'a self, index: K) -> &'a Self::Output {
        self.get(&index).unwrap()
    }
}

#[test]
fn test_ordered_array_symbol_table() {
    let mut st: OrderedArrayST<char,usize> = ST::new();
    assert!(st.is_empty());
    for (i, c) in "SEARCHEXAMPLE".chars().enumerate() {
        st.put(c, i);
    }
    assert_eq!(st.get(&'X'), Some(&7));
    assert_eq!(st.get(&'E'), Some(&12));
    assert_eq!(st['E'], 12);
    assert_eq!(st.size(), 10);
    // st['Z'] = 233;
    assert_eq!(st.is_empty(), false);
}
<commit_msg>symbol_tables/ordered_array_st: fix slow implementation of OrderedArrayST<commit_after>use std::ops::Index;
use std::cmp::Ordering;
use super::ST;

pub struct OrderedArrayST<K, V> {
    // Maintain an ordered array of key-value pairs.
    pairs: Vec<(K,V)>,
}

impl<K: Ord, V> OrderedArrayST<K, V> {
    fn rank(&self, key: &K) -> usize {
        let mut lo = 0;
        let mut hi = self.pairs.len() - 1;

        while lo <= hi {
            let mid = lo + (hi - lo)/2;
            if mid == 0 {
                break;
            }
            match key.cmp(&self.pairs[mid].0) {
                Ordering::Less => {
                    hi = mid - 1;
                },
                Ordering::Greater => {
                    lo = mid + 1;
                },
                _ => {
                    return mid;
                }
            }
        }
        lo
    }

    fn insertion_sort_by_key(&mut self) {
        let n = self.pairs.len();
        for i in 0 .. n {
            for j in (0 .. i).rev() {
                if self.pairs[j + 1].0 < self.pairs[j].0 {
                    self.pairs.swap(j+1, j);
                } else {
                    break;
                }
            }
        }
    }

    fn get_mut(&mut self, key: &K) -> Option<&mut V> {
        if self.is_empty() {
            return None;
        }
        let i = self.rank(key);
        if i < self.pairs.len() && &self.pairs[i].0 == key {
            Some(&mut self.pairs[i].1)
        } else {
            None
        }
    }
}

impl<K: Ord, V> ST<K,V> for OrderedArrayST<K, V> {
    fn new() -> Self {
        OrderedArrayST {
            pairs: Vec::new(),
        }
    }

    fn put(&mut self, key: K, val: V) {
        if let Some(v) = self.get_mut(&key) {
            *v = val;
            return;
        }
        self.pairs.push((key, val));
        self.insertion_sort_by_key();
    }

    fn get(&self, key: &K) -> Option<&V> {
        if self.is_empty() {
            return None;
        }
        let i = self.rank(key);
        if i < self.pairs.len() && &self.pairs[i].0 == key {
            Some(&self.pairs[i].1)
        } else {
            None
        }
    }

    fn delete(&mut self, key: &K) {
        let i = self.rank(key);
        if i < self.pairs.len() && &self.pairs[i].0 == key {
            self.pairs.remove(i);
        }
    }

    /// is the table empty?
    fn is_empty(&self) -> bool {
        self.pairs.len() == 0
    }

    fn size(&self) -> usize {
        self.pairs.len()
    }
}

impl<K: Ord, V> Index<K> for OrderedArrayST<K, V> {
    type Output = V;

    fn index<'a>(&'a self, index: K) -> &'a Self::Output {
        self.get(&index).unwrap()
    }
}

#[test]
fn test_ordered_array_symbol_table() {
    let mut st: OrderedArrayST<char,usize> = ST::new();
    assert!(st.is_empty());
    for (i, c) in "SEARCHEXAMPLE".chars().enumerate() {
        st.put(c, i);
    }
    assert_eq!(st.get(&'X'), Some(&7));
    assert_eq!(st.get(&'E'), Some(&12));
    assert_eq!(st['E'], 12);
    assert_eq!(st.size(), 10);
    // st['Z'] = 233;
    assert_eq!(st.is_empty(), false);
}
<|endoftext|>


<commit_before>// Copyright 2012 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.

// Based on threadring.erlang by Jira Isa

use std::os;

fn start(n_tasks: int, token: int) {
    let mut (p, ch1) = stream();
    ch1.send(token);
    //  XXX could not get this to work with a range closure
    let mut i = 2;
    while i <= n_tasks {
        let (next_p, ch) = stream();
        let imm_i = i;
        let imm_p = p;
        do spawn {
            roundtrip(imm_i, n_tasks, &imm_p, &ch);
        };
        p = next_p;
        i += 1;
    }
    let imm_p = p;
    let imm_ch = ch1;
    do spawn {
        roundtrip(1, n_tasks, &imm_p, &imm_ch);
    }
}

fn roundtrip(id: int, n_tasks: int, p: &Port<int>, ch: &Chan<int>) {
    while (true) {
        match p.recv() {
          1 => {
            println(fmt!("%d\n", id));
            return;
          }
          token => {
            debug!("thread: %d   got token: %d", id, token);
            ch.send(token - 1);
            if token <= n_tasks {
                return;
            }
          }
        }
    }
}

fn main() {
    let args = if os::getenv(~"RUST_BENCH").is_some() {
        ~[~"", ~"2000000", ~"503"]
    }
    else {
        os::args()
    };
    let token = if args.len() > 1u {
        FromStr::from_str(args[1]).get()
    }
    else {
        1000
    };
    let n_tasks = if args.len() > 2u {
        FromStr::from_str(args[2]).get()
    }
    else {
        503
    };
    start(n_tasks, token);

}
<commit_msg>Fix threadring<commit_after>// Copyright 2012 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.

// Based on threadring.erlang by Jira Isa

use std::os;

fn start(n_tasks: int, token: int) {
    let (p, ch1) = stream();
    let mut p = p;
    let mut ch1 = ch1;
    ch1.send(token);
    //  XXX could not get this to work with a range closure
    let mut i = 2;
    while i <= n_tasks {
        let (next_p, ch) = stream();
        let imm_i = i;
        let imm_p = p;
        do spawn {
            roundtrip(imm_i, n_tasks, &imm_p, &ch);
        };
        p = next_p;
        i += 1;
    }
    let imm_p = p;
    let imm_ch = ch1;
    do spawn {
        roundtrip(1, n_tasks, &imm_p, &imm_ch);
    }
}

fn roundtrip(id: int, n_tasks: int, p: &Port<int>, ch: &Chan<int>) {
    while (true) {
        match p.recv() {
          1 => {
            println(fmt!("%d\n", id));
            return;
          }
          token => {
            debug!("thread: %d   got token: %d", id, token);
            ch.send(token - 1);
            if token <= n_tasks {
                return;
            }
          }
        }
    }
}

fn main() {
    let args = if os::getenv(~"RUST_BENCH").is_some() {
        ~[~"", ~"2000000", ~"503"]
    }
    else {
        os::args()
    };
    let token = if args.len() > 1u {
        FromStr::from_str(args[1]).get()
    }
    else {
        1000
    };
    let n_tasks = if args.len() > 2u {
        FromStr::from_str(args[2]).get()
    }
    else {
        503
    };
    start(n_tasks, token);

}
<|endoftext|>