<commit_before>//! Code for generating action code.
//!
//! From the outside, action fns have one of two forms. If they take
//! symbols as input, e.g. from a production like `X = Y Z => ...`
//! (which takes Y and Z as input), they have this form:
//!
//! ```
//! fn __action17<
//!     'input,                       // user-declared type parameters (*)
//! >(
//!     input: &'input str,           // user-declared parameters
//!     __0: (usize, usize, usize),   // symbols being reduced, if any
//!     ...
//!     __N: (usize, Foo, usize),     // each has a type (L, T, L)
//! ) -> Box<Expr<'input>>
//! ```
//!
//! Otherwise, they have this form:
//!
//! ```
//! fn __action17<
//!     'input,                       // user-declared type parameters (*)
//! >(
//!     input: &'input str,           // user-declared parameters
//!    __lookbehind: &usize,          // value for @R -- "end of previous token"
//!    __lookahead: &usize,           // value for @L -- "start of next token"
//! ) -> Box<Expr<'input>>
//! ```
//!
//! * -- in this case, those "user-declared" parameters are inserted by
//! the "internal tokenizer".

use grammar::repr as r;
use rust::RustWrite;
use std::io::{self, Write};

pub fn emit_action_code<W: Write>(grammar: &r::Grammar, rust: &mut RustWrite<W>) -> io::Result<()> {
    for (i, defn) in grammar.action_fn_defns.iter().enumerate() {
        rust!(rust, "");

        // we always thread the parameters through to the action code,
        // even if they are not used, and hence we need to disable the
        // unused variables lint, which otherwise gets very excited.
        if !grammar.parameters.is_empty() {
            rust!(rust, "#[allow(unused_variables)]");
        }

        match defn.kind {
            r::ActionFnDefnKind::User(ref data) => {
                try!(emit_user_action_code(grammar, rust, i, defn, data))
            }
            r::ActionFnDefnKind::Lookaround(ref variant) => {
                try!(emit_lookaround_action_code(grammar, rust, i, defn, variant))
            }
            r::ActionFnDefnKind::Inline(ref data) => {
                try!(emit_inline_action_code(grammar, rust, i, defn, data))
            }
        }
    }

    Ok(())
}

fn ret_type_string(grammar: &r::Grammar, defn: &r::ActionFnDefn) -> String {
    if defn.fallible {
        format!(
            "Result<{},{}lalrpop_util::ParseError<{},{},{}>>",
            defn.ret_type,
            grammar.prefix,
            grammar.types.terminal_loc_type(),
            grammar.types.terminal_token_type(),
            grammar.types.error_type()
        )
    } else {
        format!("{}", defn.ret_type)
    }
}

fn emit_user_action_code<W: Write>(
    grammar: &r::Grammar,
    rust: &mut RustWrite<W>,
    index: usize,
    defn: &r::ActionFnDefn,
    data: &r::UserActionFnDefn,
) -> io::Result<()> {
    let ret_type = ret_type_string(grammar, defn);

    // For each symbol to be reduced, we will receive
    // a (L, T, L) triple where the Ls are locations and
    // the T is the data. Ignore the locations and bind
    // the data to the name the user gave.
    let mut arguments: Vec<String> = data
        .arg_patterns
        .iter()
        .zip(
            data.arg_types
                .iter()
                .cloned()
                .map(|t| grammar.types.spanned_type(t)),
        )
        .map(|(p, t)| format!("(_, {}, _): {}", p, t))
        .collect();

    // If this is a reduce of an empty production, we will
    // automatically add position information in the form of
    // lookbehind/lookahead values. Otherwise, those values would be
    // determined from the arguments themselves.
    if data.arg_patterns.is_empty() {
        arguments.extend(vec![
            format!(
                "{}lookbehind: &{}",
                grammar.prefix,
                grammar.types.terminal_loc_type()
            ),
            format!(
                "{}lookahead: &{}",
                grammar.prefix,
                grammar.types.terminal_loc_type()
            ),
        ]);
    }

    try!(rust.write_fn_header(
        grammar,
        &r::Visibility::Priv,
        format!("{}action{}", grammar.prefix, index),
        vec![],
        None,
        arguments,
        ret_type,
        vec![]
    ));
    rust!(rust, "{{");
    rust!(rust, "{}", data.code);
    rust!(rust, "}}");
    Ok(())
}

fn emit_lookaround_action_code<W: Write>(
    grammar: &r::Grammar,
    rust: &mut RustWrite<W>,
    index: usize,
    _defn: &r::ActionFnDefn,
    data: &r::LookaroundActionFnDefn,
) -> io::Result<()> {
    try!(rust.write_fn_header(
        grammar,
        &r::Visibility::Priv,
        format!("{}action{}", grammar.prefix, index),
        vec![],
        None,
        vec![
            format!(
                "{}lookbehind: &{}",
                grammar.prefix,
                grammar.types.terminal_loc_type()
            ),
            format!(
                "{}lookahead: &{}",
                grammar.prefix,
                grammar.types.terminal_loc_type()
            ),
        ],
        format!("{}", grammar.types.terminal_loc_type()),
        vec![]
    ));

    rust!(rust, "{{");
    match *data {
        r::LookaroundActionFnDefn::Lookahead => {
            // take the lookahead, if any; otherwise, we are
            // at EOF, so taker the lookbehind (end of last
            // pushed token); if that is missing too, then
            // supply default.
            rust!(rust, "{}lookahead.clone()", grammar.prefix);
        }
        r::LookaroundActionFnDefn::Lookbehind => {
            // take lookbehind or supply default
            rust!(rust, "{}lookbehind.clone()", grammar.prefix);
        }
    }
    rust!(rust, "}}");
    Ok(())
}

fn emit_inline_action_code<W: Write>(
    grammar: &r::Grammar,
    rust: &mut RustWrite<W>,
    index: usize,
    defn: &r::ActionFnDefn,
    data: &r::InlineActionFnDefn,
) -> io::Result<()> {
    let ret_type = ret_type_string(grammar, defn);

    let arg_types: Vec<_> = data
        .symbols
        .iter()
        .flat_map(|sym| match *sym {
            r::InlinedSymbol::Original(ref s) => vec![s.clone()],
            r::InlinedSymbol::Inlined(_, ref syms) => syms.clone(),
        })
        .map(|s| s.ty(&grammar.types))
        .collect();

    // this is the number of symbols we expect to be passed in; it is
    // distinct from data.symbols.len(), because sometimes we have
    // inlined actions with no input symbols
    let num_flat_args = arg_types.len();

    let mut arguments: Vec<_> = arg_types
        .iter()
        .map(|&t| grammar.types.spanned_type(t.clone()))
        .enumerate()
        .map(|(i, t)| format!("{}{}: {}", grammar.prefix, i, t))
        .collect();

    // If no symbols are being reduced, add in the
    // lookbehind/lookahead.
    if arguments.len() == 0 {
        arguments.extend(vec![
            format!(
                "{}lookbehind: &{}",
                grammar.prefix,
                grammar.types.terminal_loc_type()
            ),
            format!(
                "{}lookahead: &{}",
                grammar.prefix,
                grammar.types.terminal_loc_type()
            ),
        ]);
    }

    try!(rust.write_fn_header(
        grammar,
        &r::Visibility::Priv,
        format!("{}action{}", grammar.prefix, index),
        vec![],
        None,
        arguments,
        ret_type,
        vec![]
    ));
    rust!(rust, "{{");

    // For each inlined thing, compute the start/end locations.
    // Do this first so that none of the arguments have been moved
    // yet and we can easily access their locations.
    let mut arg_counter = 0;
    let mut temp_counter = 0;
    for symbol in &data.symbols {
        match *symbol {
            r::InlinedSymbol::Original(_) => {
                arg_counter += 1;
            }
            r::InlinedSymbol::Inlined(_, ref syms) => {
                if syms.len() > 0 {
                    // If we are reducing symbols, then start and end
                    // can be the start/end location of the first/last
                    // symbol respectively. Easy peezy.

                    rust!(
                        rust,
                        "let {}start{} = {}{}.0.clone();",
                        grammar.prefix,
                        temp_counter,
                        grammar.prefix,
                        arg_counter
                    );

                    let last_arg_index = arg_counter + syms.len() - 1;
                    rust!(
                        rust,
                        "let {}end{} = {}{}.2.clone();",
                        grammar.prefix,
                        temp_counter,
                        grammar.prefix,
                        last_arg_index
                    );
                } else {
                    // If we have no symbols, then `arg_counter`
                    // represents index of the first symbol after this
                    // inlined item (if any), and `arg_counter-1`
                    // represents index of the symbol before this
                    // item.

                    if arg_counter > 0 {
                        rust!(
                            rust,
                            "let {}start{} = {}{}.2.clone();",
                            grammar.prefix,
                            temp_counter,
                            grammar.prefix,
                            arg_counter - 1
                        );
                    } else if num_flat_args > 0 {
                        rust!(
                            rust,
                            "let {}start{} = {}{}.0.clone();",
                            grammar.prefix,
                            temp_counter,
                            grammar.prefix,
                            arg_counter
                        );
                    } else {
                        rust!(
                            rust,
                            "let {}start{} = {}lookbehind.clone();",
                            grammar.prefix,
                            temp_counter,
                            grammar.prefix
                        );
                    }

                    if arg_counter < num_flat_args {
                        rust!(
                            rust,
                            "let {}end{} = {}{}.0.clone();",
                            grammar.prefix,
                            temp_counter,
                            grammar.prefix,
                            arg_counter
                        );
                    } else if num_flat_args > 0 {
                        rust!(
                            rust,
                            "let {}end{} = {}{}.2.clone();",
                            grammar.prefix,
                            temp_counter,
                            grammar.prefix,
                            num_flat_args - 1
                        );
                    } else {
                        rust!(
                            rust,
                            "let {}end{} = {}lookahead.clone();",
                            grammar.prefix,
                            temp_counter,
                            grammar.prefix
                        );
                    }
                }

                temp_counter += 1;
                arg_counter += syms.len();
            }
        }
    }

    // Now create temporaries for the inlined things
    let mut arg_counter = 0;
    let mut temp_counter = 0;
    for symbol in &data.symbols {
        match *symbol {
            r::InlinedSymbol::Original(_) => {
                arg_counter += 1;
            }
            r::InlinedSymbol::Inlined(inlined_action, ref syms) => {
                // execute the inlined reduce action
                rust!(
                    rust,
                    "let {}temp{} = {}action{}(",
                    grammar.prefix,
                    temp_counter,
                    grammar.prefix,
                    inlined_action.index()
                );
                for parameter in &grammar.parameters {
                    rust!(rust, "{},", parameter.name);
                }
                for i in 0..syms.len() {
                    rust!(rust, "{}{},", grammar.prefix, arg_counter + i);
                }
                if syms.len() == 0 {
                    rust!(rust, "&{}start{},", grammar.prefix, temp_counter);
                    rust!(rust, "&{}end{},", grammar.prefix, temp_counter);
                }
                rust!(rust, ");");

                // wrap up the inlined value along with its span
                rust!(
                    rust,
                    "let {}temp{} = ({}start{}, {}temp{}, {}end{});",
                    grammar.prefix,
                    temp_counter,
                    grammar.prefix,
                    temp_counter,
                    grammar.prefix,
                    temp_counter,
                    grammar.prefix,
                    temp_counter
                );

                temp_counter += 1;
                arg_counter += syms.len();
            }
        }
    }

    rust!(rust, "{}action{}(", grammar.prefix, data.action.index());
    for parameter in &grammar.parameters {
        rust!(rust, "{},", parameter.name);
    }
    let mut arg_counter = 0;
    let mut temp_counter = 0;
    for symbol in &data.symbols {
        match *symbol {
            r::InlinedSymbol::Original(_) => {
                rust!(rust, "{}{},", grammar.prefix, arg_counter);
                arg_counter += 1;
            }
            r::InlinedSymbol::Inlined(_, ref syms) => {
                rust!(rust, "{}temp{},", grammar.prefix, temp_counter);
                temp_counter += 1;
                arg_counter += syms.len();
            }
        }
    }
    assert!(data.symbols.len() > 0);
    rust!(rust, ")");

    rust!(rust, "}}");
    Ok(())
}
<commit_msg>Propogate type parameters in inline actions<commit_after>//! Code for generating action code.
//!
//! From the outside, action fns have one of two forms. If they take
//! symbols as input, e.g. from a production like `X = Y Z => ...`
//! (which takes Y and Z as input), they have this form:
//!
//! ```
//! fn __action17<
//!     'input,                       // user-declared type parameters (*)
//! >(
//!     input: &'input str,           // user-declared parameters
//!     __0: (usize, usize, usize),   // symbols being reduced, if any
//!     ...
//!     __N: (usize, Foo, usize),     // each has a type (L, T, L)
//! ) -> Box<Expr<'input>>
//! ```
//!
//! Otherwise, they have this form:
//!
//! ```
//! fn __action17<
//!     'input,                       // user-declared type parameters (*)
//! >(
//!     input: &'input str,           // user-declared parameters
//!    __lookbehind: &usize,          // value for @R -- "end of previous token"
//!    __lookahead: &usize,           // value for @L -- "start of next token"
//! ) -> Box<Expr<'input>>
//! ```
//!
//! * -- in this case, those "user-declared" parameters are inserted by
//! the "internal tokenizer".

use grammar::repr as r;
use rust::RustWrite;
use std::io::{self, Write};

pub fn emit_action_code<W: Write>(grammar: &r::Grammar, rust: &mut RustWrite<W>) -> io::Result<()> {
    for (i, defn) in grammar.action_fn_defns.iter().enumerate() {
        rust!(rust, "");

        // we always thread the parameters through to the action code,
        // even if they are not used, and hence we need to disable the
        // unused variables lint, which otherwise gets very excited.
        if !grammar.parameters.is_empty() {
            rust!(rust, "#[allow(unused_variables)]");
        }

        match defn.kind {
            r::ActionFnDefnKind::User(ref data) => {
                try!(emit_user_action_code(grammar, rust, i, defn, data))
            }
            r::ActionFnDefnKind::Lookaround(ref variant) => {
                try!(emit_lookaround_action_code(grammar, rust, i, defn, variant))
            }
            r::ActionFnDefnKind::Inline(ref data) => {
                try!(emit_inline_action_code(grammar, rust, i, defn, data))
            }
        }
    }

    Ok(())
}

fn ret_type_string(grammar: &r::Grammar, defn: &r::ActionFnDefn) -> String {
    if defn.fallible {
        format!(
            "Result<{},{}lalrpop_util::ParseError<{},{},{}>>",
            defn.ret_type,
            grammar.prefix,
            grammar.types.terminal_loc_type(),
            grammar.types.terminal_token_type(),
            grammar.types.error_type()
        )
    } else {
        format!("{}", defn.ret_type)
    }
}

fn emit_user_action_code<W: Write>(
    grammar: &r::Grammar,
    rust: &mut RustWrite<W>,
    index: usize,
    defn: &r::ActionFnDefn,
    data: &r::UserActionFnDefn,
) -> io::Result<()> {
    let ret_type = ret_type_string(grammar, defn);

    // For each symbol to be reduced, we will receive
    // a (L, T, L) triple where the Ls are locations and
    // the T is the data. Ignore the locations and bind
    // the data to the name the user gave.
    let mut arguments: Vec<String> = data
        .arg_patterns
        .iter()
        .zip(
            data.arg_types
                .iter()
                .cloned()
                .map(|t| grammar.types.spanned_type(t)),
        )
        .map(|(p, t)| format!("(_, {}, _): {}", p, t))
        .collect();

    // If this is a reduce of an empty production, we will
    // automatically add position information in the form of
    // lookbehind/lookahead values. Otherwise, those values would be
    // determined from the arguments themselves.
    if data.arg_patterns.is_empty() {
        arguments.extend(vec![
            format!(
                "{}lookbehind: &{}",
                grammar.prefix,
                grammar.types.terminal_loc_type()
            ),
            format!(
                "{}lookahead: &{}",
                grammar.prefix,
                grammar.types.terminal_loc_type()
            ),
        ]);
    }

    try!(rust.write_fn_header(
        grammar,
        &r::Visibility::Priv,
        format!("{}action{}", grammar.prefix, index),
        vec![],
        None,
        arguments,
        ret_type,
        vec![]
    ));
    rust!(rust, "{{");
    rust!(rust, "{}", data.code);
    rust!(rust, "}}");
    Ok(())
}

fn emit_lookaround_action_code<W: Write>(
    grammar: &r::Grammar,
    rust: &mut RustWrite<W>,
    index: usize,
    _defn: &r::ActionFnDefn,
    data: &r::LookaroundActionFnDefn,
) -> io::Result<()> {
    try!(rust.write_fn_header(
        grammar,
        &r::Visibility::Priv,
        format!("{}action{}", grammar.prefix, index),
        vec![],
        None,
        vec![
            format!(
                "{}lookbehind: &{}",
                grammar.prefix,
                grammar.types.terminal_loc_type()
            ),
            format!(
                "{}lookahead: &{}",
                grammar.prefix,
                grammar.types.terminal_loc_type()
            ),
        ],
        format!("{}", grammar.types.terminal_loc_type()),
        vec![]
    ));

    rust!(rust, "{{");
    match *data {
        r::LookaroundActionFnDefn::Lookahead => {
            // take the lookahead, if any; otherwise, we are
            // at EOF, so taker the lookbehind (end of last
            // pushed token); if that is missing too, then
            // supply default.
            rust!(rust, "{}lookahead.clone()", grammar.prefix);
        }
        r::LookaroundActionFnDefn::Lookbehind => {
            // take lookbehind or supply default
            rust!(rust, "{}lookbehind.clone()", grammar.prefix);
        }
    }
    rust!(rust, "}}");
    Ok(())
}

fn emit_inline_action_code<W: Write>(
    grammar: &r::Grammar,
    rust: &mut RustWrite<W>,
    index: usize,
    defn: &r::ActionFnDefn,
    data: &r::InlineActionFnDefn,
) -> io::Result<()> {
    let ret_type = ret_type_string(grammar, defn);

    let arg_types: Vec<_> = data
        .symbols
        .iter()
        .flat_map(|sym| match *sym {
            r::InlinedSymbol::Original(ref s) => vec![s.clone()],
            r::InlinedSymbol::Inlined(_, ref syms) => syms.clone(),
        })
        .map(|s| s.ty(&grammar.types))
        .collect();

    // this is the number of symbols we expect to be passed in; it is
    // distinct from data.symbols.len(), because sometimes we have
    // inlined actions with no input symbols
    let num_flat_args = arg_types.len();

    let mut arguments: Vec<_> = arg_types
        .iter()
        .map(|&t| grammar.types.spanned_type(t.clone()))
        .enumerate()
        .map(|(i, t)| format!("{}{}: {}", grammar.prefix, i, t))
        .collect();

    // If no symbols are being reduced, add in the
    // lookbehind/lookahead.
    if arguments.len() == 0 {
        arguments.extend(vec![
            format!(
                "{}lookbehind: &{}",
                grammar.prefix,
                grammar.types.terminal_loc_type()
            ),
            format!(
                "{}lookahead: &{}",
                grammar.prefix,
                grammar.types.terminal_loc_type()
            ),
        ]);
    }

    try!(rust.write_fn_header(
        grammar,
        &r::Visibility::Priv,
        format!("{}action{}", grammar.prefix, index),
        vec![],
        None,
        arguments,
        ret_type,
        vec![]
    ));
    rust!(rust, "{{");

    // For each inlined thing, compute the start/end locations.
    // Do this first so that none of the arguments have been moved
    // yet and we can easily access their locations.
    let mut arg_counter = 0;
    let mut temp_counter = 0;
    for symbol in &data.symbols {
        match *symbol {
            r::InlinedSymbol::Original(_) => {
                arg_counter += 1;
            }
            r::InlinedSymbol::Inlined(_, ref syms) => {
                if syms.len() > 0 {
                    // If we are reducing symbols, then start and end
                    // can be the start/end location of the first/last
                    // symbol respectively. Easy peezy.

                    rust!(
                        rust,
                        "let {}start{} = {}{}.0.clone();",
                        grammar.prefix,
                        temp_counter,
                        grammar.prefix,
                        arg_counter
                    );

                    let last_arg_index = arg_counter + syms.len() - 1;
                    rust!(
                        rust,
                        "let {}end{} = {}{}.2.clone();",
                        grammar.prefix,
                        temp_counter,
                        grammar.prefix,
                        last_arg_index
                    );
                } else {
                    // If we have no symbols, then `arg_counter`
                    // represents index of the first symbol after this
                    // inlined item (if any), and `arg_counter-1`
                    // represents index of the symbol before this
                    // item.

                    if arg_counter > 0 {
                        rust!(
                            rust,
                            "let {}start{} = {}{}.2.clone();",
                            grammar.prefix,
                            temp_counter,
                            grammar.prefix,
                            arg_counter - 1
                        );
                    } else if num_flat_args > 0 {
                        rust!(
                            rust,
                            "let {}start{} = {}{}.0.clone();",
                            grammar.prefix,
                            temp_counter,
                            grammar.prefix,
                            arg_counter
                        );
                    } else {
                        rust!(
                            rust,
                            "let {}start{} = {}lookbehind.clone();",
                            grammar.prefix,
                            temp_counter,
                            grammar.prefix
                        );
                    }

                    if arg_counter < num_flat_args {
                        rust!(
                            rust,
                            "let {}end{} = {}{}.0.clone();",
                            grammar.prefix,
                            temp_counter,
                            grammar.prefix,
                            arg_counter
                        );
                    } else if num_flat_args > 0 {
                        rust!(
                            rust,
                            "let {}end{} = {}{}.2.clone();",
                            grammar.prefix,
                            temp_counter,
                            grammar.prefix,
                            num_flat_args - 1
                        );
                    } else {
                        rust!(
                            rust,
                            "let {}end{} = {}lookahead.clone();",
                            grammar.prefix,
                            temp_counter,
                            grammar.prefix
                        );
                    }
                }

                temp_counter += 1;
                arg_counter += syms.len();
            }
        }
    }

    // Now create temporaries for the inlined things
    let mut arg_counter = 0;
    let mut temp_counter = 0;
    for symbol in &data.symbols {
        match *symbol {
            r::InlinedSymbol::Original(_) => {
                arg_counter += 1;
            }
            r::InlinedSymbol::Inlined(inlined_action, ref syms) => {
                // execute the inlined reduce action
                rust!(
                    rust,
                    "let {}temp{} = {}action{}::<",
                    grammar.prefix,
                    temp_counter,
                    grammar.prefix,
                    inlined_action.index()
                );
                for t in grammar.non_lifetime_type_parameters() {
                    rust!(rust, "{},", t);
                }
                rust!(rust, ">(");
                for parameter in &grammar.parameters {
                    rust!(rust, "{},", parameter.name);
                }
                for i in 0..syms.len() {
                    rust!(rust, "{}{},", grammar.prefix, arg_counter + i);
                }
                if syms.len() == 0 {
                    rust!(rust, "&{}start{},", grammar.prefix, temp_counter);
                    rust!(rust, "&{}end{},", grammar.prefix, temp_counter);
                }
                rust!(rust, ");");

                // wrap up the inlined value along with its span
                rust!(
                    rust,
                    "let {}temp{} = ({}start{}, {}temp{}, {}end{});",
                    grammar.prefix,
                    temp_counter,
                    grammar.prefix,
                    temp_counter,
                    grammar.prefix,
                    temp_counter,
                    grammar.prefix,
                    temp_counter
                );

                temp_counter += 1;
                arg_counter += syms.len();
            }
        }
    }
    rust!(rust, "{}action{}::<", grammar.prefix, data.action.index());
    for t in grammar.non_lifetime_type_parameters() {
        rust!(rust, "{},", t);
    }
    rust!(rust, ">(");
    for parameter in &grammar.parameters {
        rust!(rust, "{},", parameter.name);
    }
    let mut arg_counter = 0;
    let mut temp_counter = 0;
    for symbol in &data.symbols {
        match *symbol {
            r::InlinedSymbol::Original(_) => {
                rust!(rust, "{}{},", grammar.prefix, arg_counter);
                arg_counter += 1;
            }
            r::InlinedSymbol::Inlined(_, ref syms) => {
                rust!(rust, "{}temp{},", grammar.prefix, temp_counter);
                temp_counter += 1;
                arg_counter += syms.len();
            }
        }
    }
    assert!(data.symbols.len() > 0);
    rust!(rust, ")");

    rust!(rust, "}}");
    Ok(())
}
<|endoftext|>


<commit_before>use self::Term::*;
use self::Error::*;

#[derive(Debug, PartialEq, Clone)]
pub enum Term {
	Var(usize),
	Abs(Box<Term>),
	App(Box<Term>, Box<Term>)
}

#[derive(Debug, PartialEq, Clone)]
pub enum Error {
	NotAVar,
	NotAnAbs,
	NotAnApp,
	NotANum,
	NotAPair,
	NotAList
}

impl Term {
	pub fn app(self, term: Term) -> Term { App(Box::new(self), Box::new(term)) }

	pub fn unvar(self) -> Result<usize, Error> {
		match self {
			Var(n) => Ok(n),
			_ => Err(NotAVar)
		}
	}

	pub fn unabs(self) -> Result<Term, Error> {
		match self {
			Abs(x) => Ok(*x),
			_ => Err(NotAnAbs)
		}
	}

	pub fn unabs_ref(&self) -> Result<&Term, Error> {
		match *self {
			Abs(ref x) => Ok(&*x),
			_ => Err(NotAnAbs)
		}
	}

	pub fn unabs_ref_mut(&mut self) -> Result<&mut Term, Error> {
		match *self {
			Abs(ref mut x) => Ok(&mut *x),
			_ => Err(NotAnAbs)
		}
	}

	pub fn unapp(self) -> Result<(Term, Term), Error> {
		match self {
			App(lhs, rhs) => Ok((*lhs, *rhs)),
			_ => Err(NotAnApp)
		}
	}

	pub fn unapp_ref(&self) -> Result<(&Term, &Term), Error> {
		match *self {
			App(ref lhs, ref rhs) => Ok((&*lhs, &*rhs)),
			_ => Err(NotAnApp)
		}
	}

	pub fn unapp_ref_mut(&mut self) -> Result<(&mut Term, &mut Term), Error> {
		match *self {
			App(ref mut lhs, ref mut rhs) => Ok((&mut *lhs, &mut *rhs)),
			_ => Err(NotAnApp)
		}
	}

	pub fn lhs(self) -> Result<Term, Error> {
		if let Ok((lhs, _)) = self.unapp() { Ok(lhs) } else { Err(NotAnApp) }
	}

	pub fn lhs_ref(&self) -> Result<&Term, Error> {
		if let Ok((lhs, _)) = self.unapp_ref() { Ok(lhs) } else { Err(NotAnApp) }
	}

	pub fn lhs_ref_mut(&mut self) -> Result<&mut Term, Error> {
		if let Ok((lhs, _)) = self.unapp_ref_mut() { Ok(lhs) } else { Err(NotAnApp) }
	}

	pub fn rhs(self) -> Result<Term, Error> {
		if let Ok((_, rhs)) = self.unapp() { Ok(rhs) } else { Err(NotAnApp) }
	}

	pub fn rhs_ref(&self) -> Result<&Term, Error> {
		if let Ok((_, rhs)) = self.unapp_ref() { Ok(rhs) } else { Err(NotAnApp) }
	}

	pub fn rhs_ref_mut(&mut self) -> Result<&mut Term, Error> {
		if let Ok((_, rhs)) = self.unapp_ref_mut() { Ok(rhs) } else { Err(NotAnApp) }
	}
}

pub fn abs(term: Term) -> Term { Abs(Box::new(term)) }
pub fn app(lhs: Term, rhs: Term) -> Term { App(Box::new(lhs), Box::new(rhs)) }<commit_msg>Delete term.rs<commit_after><|endoftext|>


<commit_before>extern crate hyper;

use emailaddress::EmailAddress;
use iron::Url;
use serde_json::de::from_reader;
use serde_json::value::Value;
use self::hyper::client::Client as HttpClient;
use self::hyper::header::ContentType as HyContentType;
use self::hyper::header::Headers;
use super::{AppConfig, create_jwt};
use super::crypto::{session_id, verify_jws};
use time::now_utc;
use url::percent_encoding::{utf8_percent_encode, QUERY_ENCODE_SET};
use urlencoded::QueryMap;

/// Helper method to issue an OAuth authorization request.
///
/// When an authentication request comes in and matches one of the "famous"
/// identity providers configured in the `AppConfig`, we redirect the client
/// to an Authentication Request URL, which we discover by reading the
/// provider's configured Discovery URL. We pass in the client ID we received
/// when pre-registering for the provider, as well as a callback URL which the
/// user will be redirected back to after confirming (or denying) the
/// Authentication Request. Included in the request is a nonce which we can
/// later use to definitively match the callback to this request.
pub fn request(app: &AppConfig, params: &QueryMap) -> Url {

    let email_addr = EmailAddress::new(&params.get("login_hint").unwrap()[0]).unwrap();
    let client_id = &params.get("client_id").unwrap()[0];
    let nonce = &params.get("nonce").unwrap()[0];
    let session = session_id(&email_addr, client_id);

    // Store the nonce and the RP's `redirect_uri` in Redis for use in the
    // callback handler.
    let _ = app.store.store_session(&session, &[
        ("email", &email_addr.to_string()),
        ("client_id", client_id),
        ("nonce", nonce),
        ("redirect", &params.get("redirect_uri").unwrap()[0]),
    ]).unwrap();

    // Retrieve the provider's Discovery document and extract the
    // `authorization_endpoint` from it.
    // TODO: cache other data for use in the callback handler, so that we
    // don't have to request the Discovery document twice. We could even store
    // per-provider data in Redis so we can amortize the cost of discovery.
    let provider = &app.providers[&email_addr.domain];
    let client = HttpClient::new();
    let rsp = client.get(&provider.discovery).send().unwrap();
    let val: Value = from_reader(rsp).unwrap();
    let config = val.as_object().unwrap();
    let authz_base = config["authorization_endpoint"].as_str().unwrap();

    // Create the URL to redirect to, properly escaping all parameters.
    Url::parse(&vec![
        authz_base,
        "?",
        "client_id=",
        &utf8_percent_encode(&provider.client_id, QUERY_ENCODE_SET).to_string(),
        "&response_type=code",
        "&scope=",
        &utf8_percent_encode("openid email", QUERY_ENCODE_SET).to_string(),
        "&redirect_uri=",
        &utf8_percent_encode(&format!("{}/callback", &app.base_url),
                             QUERY_ENCODE_SET).to_string(),
        "&state=",
        &utf8_percent_encode(&session, QUERY_ENCODE_SET).to_string(),
        "&login_hint=",
        &utf8_percent_encode(&email_addr.to_string(), QUERY_ENCODE_SET).to_string(),
    ].join("")).unwrap()

}

/// Helper method to verify OAuth authentication result.
///
/// Match the returned email address and nonce against our Redis data, then
/// extract the identity token returned by the provider and verify it. Return
/// an identity token for the RP if successful, or an error message otherwise.
pub fn verify(app: &AppConfig, session: &str, code: &str)
              -> Result<(String, String), String> {

    // Validate that the callback matches an auth request in Redis.
    let stored = try!(app.store.get_session(&session));
    let email_addr = EmailAddress::new(stored.get("email").unwrap()).unwrap();
    let origin = stored.get("client_id").unwrap();
    let nonce = stored.get("nonce").unwrap();

    // Request the provider's Discovery document to get the
    // `token_endpoint` and `jwks_uri` values from it. TODO: save these
    // when requesting the Discovery document in the `oauth_request()`
    // function, and/or cache them by provider host.
    let client = HttpClient::new();
    let provider = &app.providers[&email_addr.domain];
    let rsp = client.get(&provider.discovery).send().unwrap();
    let val: Value = from_reader(rsp).unwrap();
    let config = val.as_object().unwrap();
    let token_url = config["token_endpoint"].as_str().unwrap();

    // Create form data for the Token Request, where we exchange the code
    // received in this callback request for an identity token (while
    // proving our identity by passing the client secret value).
    let body: String = vec![
        "code=",
        &utf8_percent_encode(code, QUERY_ENCODE_SET).to_string(),
        "&client_id=",
        &utf8_percent_encode(&provider.client_id, QUERY_ENCODE_SET).to_string(),
        "&client_secret=",
        &utf8_percent_encode(&provider.secret, QUERY_ENCODE_SET).to_string(),
        "&redirect_uri=",
        &utf8_percent_encode(&format!("{}/callback", &app.base_url),
                             QUERY_ENCODE_SET).to_string(),
        "&grant_type=authorization_code",
    ].join("");

    // Send the Token Request and extract the `id_token` from the response.
    let mut headers = Headers::new();
    headers.set(HyContentType::form_url_encoded());
    let token_rsp = client.post(token_url).headers(headers).body(&body).send().unwrap();
    let token_obj: Value = from_reader(token_rsp).unwrap();
    let id_token = token_obj.find("id_token").unwrap().as_str().unwrap();

    // Grab the keys from the provider, then verify the signature.
    let keys_url = config["jwks_uri"].as_str().unwrap();
    let keys_rsp = client.get(keys_url).send().unwrap();
    let keys_doc: Value = from_reader(keys_rsp).unwrap();
    let jwt_payload = verify_jws(id_token, &keys_doc).unwrap();

    // Verify that the issuer matches the configured value.
    let iss = jwt_payload.find("iss").unwrap().as_str().unwrap();
    let issuer_origin = vec!["https://", &provider.issuer].join("");
    assert!(iss == provider.issuer || iss == issuer_origin);

    // Verify the audience, subject, and expiry.
    let aud = jwt_payload.find("aud").unwrap().as_str().unwrap();
    assert!(aud == provider.client_id);
    let token_addr = jwt_payload.find("email").unwrap().as_str().unwrap();
    assert!(token_addr == email_addr.to_string());
    let now = now_utc().to_timespec().sec;
    let exp = jwt_payload.find("exp").unwrap().as_i64().unwrap();
    assert!(now < exp);

    // If everything is okay, build a new identity token and send it
    // to the relying party.
    let id_token = create_jwt(app, &email_addr.to_string(), origin, nonce);
    let redirect = stored.get("redirect").unwrap();
    Ok((id_token, redirect.to_string()))

}
<commit_msg>Wrap oidc fetch/parse code in blocks.<commit_after>extern crate hyper;

use emailaddress::EmailAddress;
use iron::Url;
use serde_json::de::from_reader;
use serde_json::value::Value;
use self::hyper::client::Client as HttpClient;
use self::hyper::header::ContentType as HyContentType;
use self::hyper::header::Headers;
use super::{AppConfig, create_jwt};
use super::crypto::{session_id, verify_jws};
use time::now_utc;
use url::percent_encoding::{utf8_percent_encode, QUERY_ENCODE_SET};
use urlencoded::QueryMap;

/// Helper method to issue an OAuth authorization request.
///
/// When an authentication request comes in and matches one of the "famous"
/// identity providers configured in the `AppConfig`, we redirect the client
/// to an Authentication Request URL, which we discover by reading the
/// provider's configured Discovery URL. We pass in the client ID we received
/// when pre-registering for the provider, as well as a callback URL which the
/// user will be redirected back to after confirming (or denying) the
/// Authentication Request. Included in the request is a nonce which we can
/// later use to definitively match the callback to this request.
pub fn request(app: &AppConfig, params: &QueryMap) -> Url {

    let email_addr = EmailAddress::new(&params.get("login_hint").unwrap()[0]).unwrap();
    let client_id = &params.get("client_id").unwrap()[0];
    let nonce = &params.get("nonce").unwrap()[0];
    let session = session_id(&email_addr, client_id);

    // Store the nonce and the RP's `redirect_uri` in Redis for use in the
    // callback handler.
    let _ = app.store.store_session(&session, &[
        ("email", &email_addr.to_string()),
        ("client_id", client_id),
        ("nonce", nonce),
        ("redirect", &params.get("redirect_uri").unwrap()[0]),
    ]).unwrap();

    let client = HttpClient::new();

    // Retrieve the provider's Discovery document and extract the
    // `authorization_endpoint` from it.
    // TODO: cache other data for use in the callback handler, so that we
    // don't have to request the Discovery document twice. We could even store
    // per-provider data in Redis so we can amortize the cost of discovery.
    let provider = &app.providers[&email_addr.domain];
    let val: Value = {
        let rsp = client.get(&provider.discovery).send().unwrap();
        from_reader(rsp).unwrap()
    };
    let config = val.as_object().unwrap();
    let authz_base = config["authorization_endpoint"].as_str().unwrap();

    // Create the URL to redirect to, properly escaping all parameters.
    Url::parse(&vec![
        authz_base,
        "?",
        "client_id=",
        &utf8_percent_encode(&provider.client_id, QUERY_ENCODE_SET).to_string(),
        "&response_type=code",
        "&scope=",
        &utf8_percent_encode("openid email", QUERY_ENCODE_SET).to_string(),
        "&redirect_uri=",
        &utf8_percent_encode(&format!("{}/callback", &app.base_url),
                             QUERY_ENCODE_SET).to_string(),
        "&state=",
        &utf8_percent_encode(&session, QUERY_ENCODE_SET).to_string(),
        "&login_hint=",
        &utf8_percent_encode(&email_addr.to_string(), QUERY_ENCODE_SET).to_string(),
    ].join("")).unwrap()

}

/// Helper method to verify OAuth authentication result.
///
/// Match the returned email address and nonce against our Redis data, then
/// extract the identity token returned by the provider and verify it. Return
/// an identity token for the RP if successful, or an error message otherwise.
pub fn verify(app: &AppConfig, session: &str, code: &str)
              -> Result<(String, String), String> {

    // Validate that the callback matches an auth request in Redis.
    let stored = try!(app.store.get_session(&session));
    let email_addr = EmailAddress::new(stored.get("email").unwrap()).unwrap();
    let origin = stored.get("client_id").unwrap();
    let nonce = stored.get("nonce").unwrap();

    let client = HttpClient::new();

    // Request the provider's Discovery document to get the
    // `token_endpoint` and `jwks_uri` values from it. TODO: save these
    // when requesting the Discovery document in the `oauth_request()`
    // function, and/or cache them by provider host.
    let provider = &app.providers[&email_addr.domain];
    let val: Value = {
        let rsp = client.get(&provider.discovery).send().unwrap();
        from_reader(rsp).unwrap()
    };
    let config = val.as_object().unwrap();
    let token_url = config["token_endpoint"].as_str().unwrap();

    // Create form data for the Token Request, where we exchange the code
    // received in this callback request for an identity token (while
    // proving our identity by passing the client secret value).
    let body: String = vec![
        "code=",
        &utf8_percent_encode(code, QUERY_ENCODE_SET).to_string(),
        "&client_id=",
        &utf8_percent_encode(&provider.client_id, QUERY_ENCODE_SET).to_string(),
        "&client_secret=",
        &utf8_percent_encode(&provider.secret, QUERY_ENCODE_SET).to_string(),
        "&redirect_uri=",
        &utf8_percent_encode(&format!("{}/callback", &app.base_url),
                             QUERY_ENCODE_SET).to_string(),
        "&grant_type=authorization_code",
    ].join("");

    // Send the Token Request and extract the `id_token` from the response.
    let token_obj: Value = {
        let mut headers = Headers::new();
        headers.set(HyContentType::form_url_encoded());
        let rsp = client.post(token_url).headers(headers).body(&body).send().unwrap();
        from_reader(rsp).unwrap()
    };
    let id_token = token_obj.find("id_token").unwrap().as_str().unwrap();

    // Grab the keys from the provider, then verify the signature.
    let jwt_payload = {
        let url = config["jwks_uri"].as_str().unwrap();
        let rsp = client.get(url).send().unwrap();
        let doc: Value = from_reader(rsp).unwrap();
        verify_jws(id_token, &doc).unwrap()
    };

    // Verify that the issuer matches the configured value.
    let iss = jwt_payload.find("iss").unwrap().as_str().unwrap();
    let issuer_origin = vec!["https://", &provider.issuer].join("");
    assert!(iss == provider.issuer || iss == issuer_origin);

    // Verify the audience, subject, and expiry.
    let aud = jwt_payload.find("aud").unwrap().as_str().unwrap();
    assert!(aud == provider.client_id);
    let token_addr = jwt_payload.find("email").unwrap().as_str().unwrap();
    assert!(token_addr == email_addr.to_string());
    let now = now_utc().to_timespec().sec;
    let exp = jwt_payload.find("exp").unwrap().as_i64().unwrap();
    assert!(now < exp);

    // If everything is okay, build a new identity token and send it
    // to the relying party.
    let id_token = create_jwt(app, &email_addr.to_string(), origin, nonce);
    let redirect = stored.get("redirect").unwrap();
    Ok((id_token, redirect.to_string()))

}
<|endoftext|>


<commit_before>//! ROM image loading code

use std::cmp;
use std::str;
use std::i16;
use std::io;

fn invalid_data(err: String) -> io::Error {
    io::Error::new(io::ErrorKind::InvalidData, err)
}

/// The (decoded) SNES header
#[derive(Clone)]
pub struct RomHeader {
    /// ASCII title, filled with spaces to 21 Bytes
    title: [u8; 21],
    rom_size: u32,
    ram_size: u32,
    checksum: u16,
    rom_type: RomType,
}

#[derive(Debug, PartialEq, Eq, Clone, Copy)]
enum RomType {
    LoRom,
    HiRom,
}

impl RomHeader {
    fn dump(&self) {
        info!("ROM name: '{}'", str::from_utf8(&self.title).unwrap_or("").trim_right());
        info!("{} KB ROM / {} KB Cartridge RAM", self.rom_size / 1024, self.ram_size / 1024);
    }

    /// Loads the ROM header from the given ROM byte slice.
    ///
    /// `rom_type` is the expected type of the ROM header, based on its location. This method
    /// decides where to look for the header based on this value.
    ///
    /// Returns the decoded `RomHeader` and a scoring value. The higher the score, the more likely
    /// the header matches the `RomType`.
    ///
    /// In case `bytes` is too small for the expected ROM type, a dummy header and the maximum
    /// negative score will be returned.
    fn load(bytes: &[u8], rom_type: RomType) -> (RomHeader, i16) {
        fn dummy_result() -> (RomHeader, i16) {
            (RomHeader {
                title: [0; 21],
                rom_size: 0,
                ram_size: 0,
                checksum: 0,
                rom_type: RomType::LoRom,
            }, i16::MIN)
        }

        // Extract header slice
        let bytes = match rom_type {
            RomType::LoRom => if bytes.len() < 0x8000 {
                return dummy_result();
            } else {
                &bytes[0x7FFF - 63..0x7FFF + 1]
            },
            RomType::HiRom => if bytes.len() < 0x10000 {
                return dummy_result();
            } else {
                &bytes[0xFFFF - 63..0xFFFF + 1]
            }
        };

        // The header size must be correct (the ROM loader won't pass a wrong size)
        assert_eq!(bytes.len(), 64);

        // Score value. Decremented whenever something isn't right. Subject to tweaking.
        let mut score = 0;

        debug!("loading {:?} header", rom_type);
        debug!("raw rom header: {:?}", bytes);

        // Byte 28/29 is the checksum's complement followed by the checksum itself in Byte 30/31.
        // The checksum is the sum of all bytes in the ROM (truncated to 16 bits). It can be
        // validated by the loader.
        // 16 bit, little-endian.
        let check_inv = (bytes[29] as u16) << 8 | bytes[28] as u16;
        let rom_checksum = (bytes[31] as u16) << 8 | bytes[30] as u16;
        if check_inv != !rom_checksum {
            debug!("checksum invalid: stored complement is ${:04X}, stored checksum is ${:04X}",
                check_inv, rom_checksum);
            score -= 4;
        }

        let mut title = [0; 21];
        let mut warned = false;
        for (i, c) in bytes[0..21].iter().enumerate() {
            match *c {
                0x20 ... 0x7E => {
                    title[i] = *c;
                }
                _ => {
                    if !warned {
                        debug!("title contains non-ascii byte: ${:02X}", *c);
                        warned = true;
                        score -= 2;
                    }
                    title[i] = b' ';
                }
            }
        }

        debug!("loading '{}'", str::from_utf8(&title).unwrap().trim_right());

        // 21) ROM makeup byte
        // `---smmmm`
        // * `s`: Speed (0: SlowROM, 1: FastROM)
        // * `m`: Map mode
        //  * `0000`: LoROM
        //  * `0001`: HiROM
        //  * `0010`: LoROM + S-DD1
        //  * `0011`: LoROM + SA-1
        //  * `0101`: ExHiROM
        //  * `1010`: HiROM + SPC7110
        // (TODO)

        let header_rom_type = match bytes[21] & 0x0f {
            0 => RomType::LoRom,
            1 => RomType::HiRom,
            t => {
                debug!("unknown / unimplemented ROM type {}", t);
                score -= 10;    // until we actually implement this (FIXME Dirty hack)
                RomType::LoRom
            }
        };

        if header_rom_type == rom_type {
            debug!("type: {:?}", rom_type);
        } else {
            debug!("expected rom type {:?}, got {:?}", rom_type, header_rom_type);
            score -= 3;
        }

        // bytes[22] is the chipset info. For now, we don't care about that.
        debug!("chipset: 0x{:02X}", bytes[22]);

        debug!("ROM/RAM size values: {:02X} {:02X}", bytes[23], bytes[24]);
        // Size values are masked with 0x0F to prevent overlong bitshifts. The valid values are all
        // in range 0x00 to 0x0F anyway.
        let rom_size = 0x400 << (bytes[23] as u32 & 0x0f);
        let ram_size = 0x400 << (bytes[24] as u32 & 0x0f);
        debug!("{} KB of ROM, {} KB of cartridge RAM", rom_size / 1024, ram_size / 1024);

        // bytes[25-26] is a vendor code (doesn't matter)
        debug!("vendor code: 0x{:02X}{:02X}", bytes[25], bytes[26]);
        // 27 = version (also doesn't matter for us)
        debug!("version: 0x{:02X}", bytes[27]);

        (RomHeader {
            title: title,
            rom_size: rom_size,
            ram_size: ram_size,
            checksum: rom_checksum,
            rom_type: rom_type,
        }, score)
    }
}

/// A ROM image
#[derive(Clone)]
pub struct Rom {
    header: RomHeader,
    ram: Vec<u8>,
    rom: Vec<u8>,
}

// NB: If we want to support "realistic" saves, we'd just save the cartridge RAM and nothing else
impl_save_state!(Rom { ram } ignore { header, rom });

impl Rom {
    /// Loads a ROM from raw data.
    pub fn from_bytes(mut bytes: &[u8]) -> io::Result<Rom> {
        // Would it be useful if we returned the warnings somehow?

        debug!("raw size: {} bytes (${:X})", bytes.len(), bytes.len());

        // ROMs may begin with a 512 Bytes SMC header. It needs to go.
        match bytes.len() % 1024 {
            512 => {
                info!("stripping SMC header");
                bytes = &bytes[512..];
            }
            0 => {},
            n => {
                let fmt = format!("len() % 1024 == {} (expected 512 or 0)", n);
                error!("{}", fmt);
                return Err(invalid_data(fmt));
            }
        }

        // Try all header locations and pick the one that's probably right.
        // Oh how much I wish there was a real standard for this.
        // FIXME: We might want to... like... not play *literally every file* but warn instead :)
        let (lo_header, lo_score) = RomHeader::load(bytes,
                                                    RomType::LoRom);
        let (hi_header, hi_score) = RomHeader::load(bytes,
                                                    RomType::HiRom);

        info!("LoROM/HiROM scores: {}, {}", lo_score, hi_score);
        let header = if lo_score > hi_score {
            lo_header
        } else {
            hi_header
        };

        header.dump();

        if bytes.len() != header.rom_size as usize {
            warn!("raw ROM is {} KB, but header specifies {} KB",
                bytes.len() / 1024, header.rom_size / 1024);
        }

        // Create the right amount of RAM...
        let ram = vec![0; header.ram_size as usize];
        // ...and copy the ROM
        let rom = bytes.iter().cloned().cycle()
            .take(cmp::max(header.rom_size as usize, bytes.len())).collect();

        // Calculate the ROM's checksum
        let mut checksum: u16 = 0;
        for &byte in &rom {
            checksum = checksum.wrapping_add(byte as u16);
        }

        debug!("computed checksum: ${:04X}", checksum);

        if header.checksum != checksum {
            warn!("incorrect checksum: computed ${:04X}, expected ${:04X}",
                checksum, header.checksum);
        }

        Ok(Rom {
            header: header,
            ram: ram,
            rom: rom,
        })
    }

    pub fn get_title(&self) -> Option<&str> {
        str::from_utf8(&self.header.title).ok().map(|s| s.trim_right())
    }

    fn resolve_lorom(&mut self, bank: u8, addr: u16) -> &mut u8 {
        match addr {
            0x0000 ... 0x7fff => {
                // Cartridge RAM mapped to the low 32 KB
                // (there's other stuff here, but that's handled much earlier than we are called)
                match bank {
                    0x70 ... 0x7d => {
                        let a = (bank as u32 - 0x70) * 0x8000 + addr as u32;
                        self.ram.get_mut(a as usize).unwrap_or_else(|| out_of_ram_bounds(bank, addr, a))
                    }
                    0xfe ... 0xff => {
                        // last 64k of RAM
                        let start = self.ram.len() as u32 - 64 * 1024;
                        let a = start + (bank - 0xfe) as u32 * 0x8000 + addr as u32;
                        self.ram.get_mut(a as usize).unwrap_or_else(|| out_of_ram_bounds(bank, addr, a))
                    }
                    _ => {
                        // 0x40 ... 0x6f | 0x7e ... 0xfd
                        panic!("attempted to access unmapped address: ${:02X}:{:04X}", bank, addr);
                    }
                }
            },
            0x8000 ... 0xffff => match bank {
                // LoROM is mapped to the higher 8 pages
                0xfe => {
                    let a = 0x3f0000 + addr as u32 - 0x8000;
                    self.rom.get_mut(a as usize).unwrap_or_else(|| out_of_rom_bounds(bank, addr, a))
                }
                0xff => {
                    let a = 0x3f8000 + addr as u32 - 0x8000;
                    self.rom.get_mut(a as usize).unwrap_or_else(|| out_of_rom_bounds(bank, addr, a))
                }
                0x80 ... 0xfd | 0x00 ... 0x7d => {
                    // `& !0x80` because 0x80-0xFD mirrors 0x00-0x7D
                    let a = (bank as u32 & !0x80) * 0x8000 + addr as u32 - 0x8000;
                    self.rom.get_mut(a as usize).unwrap_or_else(|| out_of_rom_bounds(bank, addr, a))
                }
                _ => panic!("attempted to access unmapped address: ${:02X}:{:04X}", bank, addr)
            },
            _ => unreachable!()
        }
    }

    fn resolve_hirom(&mut self, bank: u8, addr: u16) -> &mut u8 {
        let addr = addr as usize;
        match bank {
            0x00 ... 0x3f | 0x80 ... 0xbf if addr >= 0x8000 => {
                &mut self.rom[(bank as usize & 0x3f) << 16 | addr]
            }
            0x20 ... 0x3f | 0xa0 ... 0xbf if addr >= 0x6000 && addr <= 0x7fff => {
                // `addr` is masked with `0x1fff` since HiROM seems to have up to 8K mirrored RAM
                &mut self.ram[addr & 0x1fff]
            }
            0x40 ... 0x7d | 0xc0 ... 0xfd => {
                &mut self.rom[((bank as usize & 0x7f) - 0x40) << 16 | addr]
            }
            0x7e ... 0x7f => unreachable!(),    // WRAM banks
            0xfe ... 0xff => {
                &mut self.rom[(bank as usize - 0xfe + 0x3e) << 16 | addr]
            }
            _ => panic!("attempted to access unmapped address: ${:02}:{:04X}", bank, addr),
        }
    }

    fn resolve_addr(&mut self, bank: u8, addr: u16) -> &mut u8 {
        match self.header.rom_type {
            RomType::LoRom => self.resolve_lorom(bank, addr),
            RomType::HiRom => self.resolve_hirom(bank, addr),
        }
    }
}

impl Rom {
    pub fn load(&mut self, bank: u8, addr: u16) -> u8 {
        *self.resolve_addr(bank, addr)
    }

    pub fn store(&mut self, bank: u8, addr: u16, value: u8) {
        if addr >= 0x8000 {
            warn!("writing ${:02X} to ROM address ${:02X}:{:04X}", value, bank, addr);
        }
        *self.resolve_addr(bank, addr) = value;
    }
}

fn out_of_ram_bounds(bank: u8, addr: u16, abs: u32) -> ! {
    panic!("RAM access out of bounds at {:02X}:{:04X} -> {:04X}",
        bank, addr, abs)
}

fn out_of_rom_bounds(bank: u8, addr: u16, abs: u32) -> ! {
    panic!("ROM access out of bounds at {:02X}:{:04X} -> {:06X}",
        bank, addr, abs)
}
<commit_msg>"info!" the checksum instead of "debug"ging it<commit_after>//! ROM image loading code

use std::cmp;
use std::str;
use std::i16;
use std::io;

fn invalid_data(err: String) -> io::Error {
    io::Error::new(io::ErrorKind::InvalidData, err)
}

/// The (decoded) SNES header
#[derive(Clone)]
pub struct RomHeader {
    /// ASCII title, filled with spaces to 21 Bytes
    title: [u8; 21],
    rom_size: u32,
    ram_size: u32,
    checksum: u16,
    rom_type: RomType,
}

#[derive(Debug, PartialEq, Eq, Clone, Copy)]
enum RomType {
    LoRom,
    HiRom,
}

impl RomHeader {
    fn dump(&self) {
        info!("ROM name: '{}'", str::from_utf8(&self.title).unwrap_or("").trim_right());
        info!("{} KB ROM / {} KB Cartridge RAM", self.rom_size / 1024, self.ram_size / 1024);
    }

    /// Loads the ROM header from the given ROM byte slice.
    ///
    /// `rom_type` is the expected type of the ROM header, based on its location. This method
    /// decides where to look for the header based on this value.
    ///
    /// Returns the decoded `RomHeader` and a scoring value. The higher the score, the more likely
    /// the header matches the `RomType`.
    ///
    /// In case `bytes` is too small for the expected ROM type, a dummy header and the maximum
    /// negative score will be returned.
    fn load(bytes: &[u8], rom_type: RomType) -> (RomHeader, i16) {
        fn dummy_result() -> (RomHeader, i16) {
            (RomHeader {
                title: [0; 21],
                rom_size: 0,
                ram_size: 0,
                checksum: 0,
                rom_type: RomType::LoRom,
            }, i16::MIN)
        }

        // Extract header slice
        let bytes = match rom_type {
            RomType::LoRom => if bytes.len() < 0x8000 {
                return dummy_result();
            } else {
                &bytes[0x7FFF - 63..0x7FFF + 1]
            },
            RomType::HiRom => if bytes.len() < 0x10000 {
                return dummy_result();
            } else {
                &bytes[0xFFFF - 63..0xFFFF + 1]
            }
        };

        // The header size must be correct (the ROM loader won't pass a wrong size)
        assert_eq!(bytes.len(), 64);

        // Score value. Decremented whenever something isn't right. Subject to tweaking.
        let mut score = 0;

        debug!("loading {:?} header", rom_type);
        debug!("raw rom header: {:?}", bytes);

        // Byte 28/29 is the checksum's complement followed by the checksum itself in Byte 30/31.
        // The checksum is the sum of all bytes in the ROM (truncated to 16 bits). It can be
        // validated by the loader.
        // 16 bit, little-endian.
        let check_inv = (bytes[29] as u16) << 8 | bytes[28] as u16;
        let rom_checksum = (bytes[31] as u16) << 8 | bytes[30] as u16;
        if check_inv != !rom_checksum {
            debug!("checksum invalid: stored complement is ${:04X}, stored checksum is ${:04X}",
                check_inv, rom_checksum);
            score -= 4;
        }

        let mut title = [0; 21];
        let mut warned = false;
        for (i, c) in bytes[0..21].iter().enumerate() {
            match *c {
                0x20 ... 0x7E => {
                    title[i] = *c;
                }
                _ => {
                    if !warned {
                        debug!("title contains non-ascii byte: ${:02X}", *c);
                        warned = true;
                        score -= 2;
                    }
                    title[i] = b' ';
                }
            }
        }

        debug!("loading '{}'", str::from_utf8(&title).unwrap().trim_right());

        // 21) ROM makeup byte
        // `---smmmm`
        // * `s`: Speed (0: SlowROM, 1: FastROM)
        // * `m`: Map mode
        //  * `0000`: LoROM
        //  * `0001`: HiROM
        //  * `0010`: LoROM + S-DD1
        //  * `0011`: LoROM + SA-1
        //  * `0101`: ExHiROM
        //  * `1010`: HiROM + SPC7110
        // (TODO)

        let header_rom_type = match bytes[21] & 0x0f {
            0 => RomType::LoRom,
            1 => RomType::HiRom,
            t => {
                debug!("unknown / unimplemented ROM type {}", t);
                score -= 10;    // until we actually implement this (FIXME Dirty hack)
                RomType::LoRom
            }
        };

        if header_rom_type == rom_type {
            debug!("type: {:?}", rom_type);
        } else {
            debug!("expected rom type {:?}, got {:?}", rom_type, header_rom_type);
            score -= 3;
        }

        // bytes[22] is the chipset info. For now, we don't care about that.
        debug!("chipset: 0x{:02X}", bytes[22]);

        debug!("ROM/RAM size values: {:02X} {:02X}", bytes[23], bytes[24]);
        // Size values are masked with 0x0F to prevent overlong bitshifts. The valid values are all
        // in range 0x00 to 0x0F anyway.
        let rom_size = 0x400 << (bytes[23] as u32 & 0x0f);
        let ram_size = 0x400 << (bytes[24] as u32 & 0x0f);
        debug!("{} KB of ROM, {} KB of cartridge RAM", rom_size / 1024, ram_size / 1024);

        // bytes[25-26] is a vendor code (doesn't matter)
        debug!("vendor code: 0x{:02X}{:02X}", bytes[25], bytes[26]);
        // 27 = version (also doesn't matter for us)
        debug!("version: 0x{:02X}", bytes[27]);

        (RomHeader {
            title: title,
            rom_size: rom_size,
            ram_size: ram_size,
            checksum: rom_checksum,
            rom_type: rom_type,
        }, score)
    }
}

/// A ROM image
#[derive(Clone)]
pub struct Rom {
    header: RomHeader,
    ram: Vec<u8>,
    rom: Vec<u8>,
}

// NB: If we want to support "realistic" saves, we'd just save the cartridge RAM and nothing else
impl_save_state!(Rom { ram } ignore { header, rom });

impl Rom {
    /// Loads a ROM from raw data.
    pub fn from_bytes(mut bytes: &[u8]) -> io::Result<Rom> {
        // Would it be useful if we returned the warnings somehow?

        debug!("raw size: {} bytes (${:X})", bytes.len(), bytes.len());

        // ROMs may begin with a 512 Bytes SMC header. It needs to go.
        match bytes.len() % 1024 {
            512 => {
                info!("stripping SMC header");
                bytes = &bytes[512..];
            }
            0 => {},
            n => {
                let fmt = format!("len() % 1024 == {} (expected 512 or 0)", n);
                error!("{}", fmt);
                return Err(invalid_data(fmt));
            }
        }

        // Try all header locations and pick the one that's probably right.
        // Oh how much I wish there was a real standard for this.
        // FIXME: We might want to... like... not play *literally every file* but warn instead :)
        let (lo_header, lo_score) = RomHeader::load(bytes,
                                                    RomType::LoRom);
        let (hi_header, hi_score) = RomHeader::load(bytes,
                                                    RomType::HiRom);

        info!("LoROM/HiROM scores: {}, {}", lo_score, hi_score);
        let header = if lo_score > hi_score {
            lo_header
        } else {
            hi_header
        };

        header.dump();

        if bytes.len() != header.rom_size as usize {
            warn!("raw ROM is {} KB, but header specifies {} KB",
                bytes.len() / 1024, header.rom_size / 1024);
        }

        // Create the right amount of RAM...
        let ram = vec![0; header.ram_size as usize];
        // ...and copy the ROM
        let rom = bytes.iter().cloned().cycle()
            .take(cmp::max(header.rom_size as usize, bytes.len())).collect();

        // Calculate the ROM's checksum
        let mut checksum: u16 = 0;
        for &byte in &rom {
            checksum = checksum.wrapping_add(byte as u16);
        }

        info!("computed checksum: ${:04X}", checksum);

        if header.checksum != checksum {
            warn!("incorrect checksum: computed ${:04X}, expected ${:04X}",
                checksum, header.checksum);
        }

        Ok(Rom {
            header: header,
            ram: ram,
            rom: rom,
        })
    }

    pub fn get_title(&self) -> Option<&str> {
        str::from_utf8(&self.header.title).ok().map(|s| s.trim_right())
    }

    fn resolve_lorom(&mut self, bank: u8, addr: u16) -> &mut u8 {
        match addr {
            0x0000 ... 0x7fff => {
                // Cartridge RAM mapped to the low 32 KB
                // (there's other stuff here, but that's handled much earlier than we are called)
                match bank {
                    0x70 ... 0x7d => {
                        let a = (bank as u32 - 0x70) * 0x8000 + addr as u32;
                        self.ram.get_mut(a as usize).unwrap_or_else(|| out_of_ram_bounds(bank, addr, a))
                    }
                    0xfe ... 0xff => {
                        // last 64k of RAM
                        let start = self.ram.len() as u32 - 64 * 1024;
                        let a = start + (bank - 0xfe) as u32 * 0x8000 + addr as u32;
                        self.ram.get_mut(a as usize).unwrap_or_else(|| out_of_ram_bounds(bank, addr, a))
                    }
                    _ => {
                        // 0x40 ... 0x6f | 0x7e ... 0xfd
                        panic!("attempted to access unmapped address: ${:02X}:{:04X}", bank, addr);
                    }
                }
            },
            0x8000 ... 0xffff => match bank {
                // LoROM is mapped to the higher 8 pages
                0xfe => {
                    let a = 0x3f0000 + addr as u32 - 0x8000;
                    self.rom.get_mut(a as usize).unwrap_or_else(|| out_of_rom_bounds(bank, addr, a))
                }
                0xff => {
                    let a = 0x3f8000 + addr as u32 - 0x8000;
                    self.rom.get_mut(a as usize).unwrap_or_else(|| out_of_rom_bounds(bank, addr, a))
                }
                0x80 ... 0xfd | 0x00 ... 0x7d => {
                    // `& !0x80` because 0x80-0xFD mirrors 0x00-0x7D
                    let a = (bank as u32 & !0x80) * 0x8000 + addr as u32 - 0x8000;
                    self.rom.get_mut(a as usize).unwrap_or_else(|| out_of_rom_bounds(bank, addr, a))
                }
                _ => panic!("attempted to access unmapped address: ${:02X}:{:04X}", bank, addr)
            },
            _ => unreachable!()
        }
    }

    fn resolve_hirom(&mut self, bank: u8, addr: u16) -> &mut u8 {
        let addr = addr as usize;
        match bank {
            0x00 ... 0x3f | 0x80 ... 0xbf if addr >= 0x8000 => {
                &mut self.rom[(bank as usize & 0x3f) << 16 | addr]
            }
            0x20 ... 0x3f | 0xa0 ... 0xbf if addr >= 0x6000 && addr <= 0x7fff => {
                // `addr` is masked with `0x1fff` since HiROM seems to have up to 8K mirrored RAM
                &mut self.ram[addr & 0x1fff]
            }
            0x40 ... 0x7d | 0xc0 ... 0xfd => {
                &mut self.rom[((bank as usize & 0x7f) - 0x40) << 16 | addr]
            }
            0x7e ... 0x7f => unreachable!(),    // WRAM banks
            0xfe ... 0xff => {
                &mut self.rom[(bank as usize - 0xfe + 0x3e) << 16 | addr]
            }
            _ => panic!("attempted to access unmapped address: ${:02}:{:04X}", bank, addr),
        }
    }

    fn resolve_addr(&mut self, bank: u8, addr: u16) -> &mut u8 {
        match self.header.rom_type {
            RomType::LoRom => self.resolve_lorom(bank, addr),
            RomType::HiRom => self.resolve_hirom(bank, addr),
        }
    }
}

impl Rom {
    pub fn load(&mut self, bank: u8, addr: u16) -> u8 {
        *self.resolve_addr(bank, addr)
    }

    pub fn store(&mut self, bank: u8, addr: u16, value: u8) {
        if addr >= 0x8000 {
            warn!("writing ${:02X} to ROM address ${:02X}:{:04X}", value, bank, addr);
        }
        *self.resolve_addr(bank, addr) = value;
    }
}

fn out_of_ram_bounds(bank: u8, addr: u16, abs: u32) -> ! {
    panic!("RAM access out of bounds at {:02X}:{:04X} -> {:04X}",
        bank, addr, abs)
}

fn out_of_rom_bounds(bank: u8, addr: u16, abs: u32) -> ! {
    panic!("ROM access out of bounds at {:02X}:{:04X} -> {:06X}",
        bank, addr, abs)
}
<|endoftext|>


<commit_before>use libc::*;
use ::list::{ENetList, ENetListNode};
use ::address::ENetAddress;
use ::ENetChannel;
use ::host::ENetHost;

pub const ENET_PEER_DEFAULT_ROUND_TRIP_TIME: size_t = 500;
pub const ENET_PEER_DEFAULT_PACKET_THROTTLE: size_t = 32;
pub const ENET_PEER_PACKET_THROTTLE_SCALE: size_t = 32;
pub const ENET_PEER_PACKET_THROTTLE_COUNTER: size_t = 7;
pub const ENET_PEER_PACKET_THROTTLE_ACCELERATION: size_t = 2;
pub const ENET_PEER_PACKET_THROTTLE_DECELERATION: size_t = 2;
pub const ENET_PEER_PACKET_THROTTLE_INTERVAL: size_t = 5000;
pub const ENET_PEER_PACKET_LOSS_SCALE: size_t = 1<<16;
pub const ENET_PEER_PACKET_LOSS_INTERVAL: size_t = 10000;
pub const ENET_PEER_WINDOW_SIZE_SCALE: size_t = 64 * 1024;
pub const ENET_PEER_TIMEOUT_LIMIT: size_t = 32;
pub const ENET_PEER_TIMEOUT_MINIMUM: size_t = 5000;
pub const ENET_PEER_TIMEOUT_MAXIMUM: size_t = 30000;
pub const ENET_PEER_PING_INTERVAL: size_t = 500;
pub const ENET_PEER_UNSEQUENCED_WINDOWS: size_t = 64;
pub const ENET_PEER_UNSEQUENCED_WINDOW_SIZE: size_t = 1024;
pub const ENET_PEER_FREE_UNSEQUENCED_WINDOWS: size_t = 32;
pub const ENET_PEER_RELIABLE_WINDOWS: size_t = 16;
pub const ENET_PEER_RELIABLE_WINDOW_SIZE: size_t = 0x1000;
pub const ENET_PEER_FREE_RELIABLE_WINDOWS: size_t = 8;

#[repr(C)]
pub struct ENetPeer {
    pub acknowledgements: ENetList,
    pub address: ENetAddress,
    pub channelCount: size_t,
    pub channels: *mut ENetChannel,
    pub connectID: uint32_t,
    pub data: *mut c_void,
    pub dispatchCommands: ENetList,
    pub dispatchList: ENetListNode,
    pub earliestTimeout: uint32_t,
    pub eventData: uint32_t,
    pub highestRoundTripTimeVariance: uint32_t,
    pub host: *mut ENetHost,
    pub incomingBandwidth: uint32_t,
    pub incomingBandwidthThrottleEpoch: uint32_t,
    pub outgoingBandwidth: uint32_t,
    pub outgoingBandwidthThrottleEpoch: uint32_t,
    pub outgoingDataTotal: uint32_t,
    pub outgoingPeerID: uint16_t,
    pub outgoingReliableCommands: ENetList,
    pub outgoingReliableSequenceNumber: uint16_t,
    pub outgoingSessionID: uint8_t,
    pub outgoingUnreliableCommands: ENetList,
    pub outgoingUnsequencedGroup: uint16_t,
    pub packetLoss: uint32_t,
    pub packetLossEpoch: uint32_t,
    pub packetLossVariance: uint32_t,
    pub packetLost: uint32_t,
    pub packetSent: uint32_t,
    pub packetThrottle: uint32_t,
    pub packetThrottleAcceleration: uint32_t,
    pub packetThrottleCounter: uint32_t,
    pub packetThrottleDeceleration: uint32_t,
    pub packetThrottleEpoch: uint32_t,
    pub packetThrottleInterval: uint32_t,
    pub packetThrottleLimit: uint32_t,
    pub pingInterval: uint32_t,
    pub reliabledataInTransit: uint32_t,
    pub roundTripTimeVariance: uint32_t,
    pub sentReliableCommands: ENetList,
    pub sentUnreliableCommands: ENetList,
    pub state: ENetPeerState,
    pub timeoutLimit: uint32_t,
    pub timeoutMaximum: uint32_t,
    pub timeoutMinimum: uint32_t,
    pub totalWaitingData: size_t,
    pub unsequencedWindows: [uint32_t; ENET_PEER_UNSEQUENCED_WINDOW_SIZE/32],
    pub windowSize: uint32_t,
}

#[repr(C)]
pub enum ENetPeerState {
    ENET_PEER_STATE_DISCONNECTED = 0,
    ENET_PEER_STATE_CONNECTING = 1,
    ENET_PEER_STATE_ACKNOWLEDGING_CONNECT = 2,
    ENET_PEER_STATE_CONNECTION_PENDING = 3,
    ENET_PEER_STATE_CONNECTION_SUCCEEDED = 4,
    ENET_PEER_STATE_CONNECTED = 5,
    ENET_PEER_STATE_DISCONNECT_LATER = 6,
    ENET_PEER_STATE_DISCONNECTING = 7,
    ENET_PEER_STATE_ACHNOWLEDGING_DISCONNECT = 8,
    ENET_PEER_STATE_ZOMBIE = 9,
}
<commit_msg>Added marker where to continue<commit_after>use libc::*;
use ::list::{ENetList, ENetListNode};
use ::address::ENetAddress;
use ::ENetChannel;
use ::host::ENetHost;

pub const ENET_PEER_DEFAULT_ROUND_TRIP_TIME: size_t = 500;
pub const ENET_PEER_DEFAULT_PACKET_THROTTLE: size_t = 32;
pub const ENET_PEER_PACKET_THROTTLE_SCALE: size_t = 32;
pub const ENET_PEER_PACKET_THROTTLE_COUNTER: size_t = 7;
pub const ENET_PEER_PACKET_THROTTLE_ACCELERATION: size_t = 2;
pub const ENET_PEER_PACKET_THROTTLE_DECELERATION: size_t = 2;
pub const ENET_PEER_PACKET_THROTTLE_INTERVAL: size_t = 5000;
pub const ENET_PEER_PACKET_LOSS_SCALE: size_t = 1<<16;
pub const ENET_PEER_PACKET_LOSS_INTERVAL: size_t = 10000;
pub const ENET_PEER_WINDOW_SIZE_SCALE: size_t = 64 * 1024;
pub const ENET_PEER_TIMEOUT_LIMIT: size_t = 32;
pub const ENET_PEER_TIMEOUT_MINIMUM: size_t = 5000;
pub const ENET_PEER_TIMEOUT_MAXIMUM: size_t = 30000;
pub const ENET_PEER_PING_INTERVAL: size_t = 500;
pub const ENET_PEER_UNSEQUENCED_WINDOWS: size_t = 64;
pub const ENET_PEER_UNSEQUENCED_WINDOW_SIZE: size_t = 1024;
pub const ENET_PEER_FREE_UNSEQUENCED_WINDOWS: size_t = 32;
pub const ENET_PEER_RELIABLE_WINDOWS: size_t = 16;
pub const ENET_PEER_RELIABLE_WINDOW_SIZE: size_t = 0x1000;
pub const ENET_PEER_FREE_RELIABLE_WINDOWS: size_t = 8;

#[repr(C)]
pub struct ENetPeer {
    pub acknowledgements: ENetList,
    pub address: ENetAddress,
    pub channelCount: size_t,
    pub channels: *mut ENetChannel,
    pub connectID: uint32_t,
    pub data: *mut c_void,
    pub dispatchCommands: ENetList,
    pub dispatchList: ENetListNode,
    pub earliestTimeout: uint32_t,
    pub eventData: uint32_t,
    pub highestRoundTripTimeVariance: uint32_t,
    pub host: *mut ENetHost,
    pub incomingBandwidth: uint32_t,
    pub incomingBandwidthThrottleEpoch: uint32_t,
    pub outgoingBandwidth: uint32_t,
    pub outgoingBandwidthThrottleEpoch: uint32_t,
    pub outgoingDataTotal: uint32_t,
    pub outgoingPeerID: uint16_t,
    pub outgoingReliableCommands: ENetList,
    pub outgoingReliableSequenceNumber: uint16_t,
    pub outgoingSessionID: uint8_t,
    pub outgoingUnreliableCommands: ENetList,
    pub outgoingUnsequencedGroup: uint16_t,
    pub packetLoss: uint32_t,
    pub packetLossEpoch: uint32_t,
    pub packetLossVariance: uint32_t,
    pub packetLost: uint32_t,
    pub packetSent: uint32_t,
    pub packetThrottle: uint32_t,
    pub packetThrottleAcceleration: uint32_t,
    pub packetThrottleCounter: uint32_t,
    pub packetThrottleDeceleration: uint32_t,
    pub packetThrottleEpoch: uint32_t,
    pub packetThrottleInterval: uint32_t,
    pub packetThrottleLimit: uint32_t,
    pub pingInterval: uint32_t,
    pub reliabledataInTransit: uint32_t,
    pub roundTripTimeVariance: uint32_t,
    pub sentReliableCommands: ENetList,
    pub sentUnreliableCommands: ENetList,
    pub state: ENetPeerState,
    pub timeoutLimit: uint32_t,
    pub timeoutMaximum: uint32_t,
    pub timeoutMinimum: uint32_t,
    pub totalWaitingData: size_t,
    pub unsequencedWindows: [uint32_t; ENET_PEER_UNSEQUENCED_WINDOW_SIZE/32],
    pub windowSize: uint32_t,
}

#[repr(C)]
pub enum ENetPeerState {
    ENET_PEER_STATE_DISCONNECTED = 0,
    ENET_PEER_STATE_CONNECTING = 1,
    ENET_PEER_STATE_ACKNOWLEDGING_CONNECT = 2,
    ENET_PEER_STATE_CONNECTION_PENDING = 3,
    ENET_PEER_STATE_CONNECTION_SUCCEEDED = 4,
    ENET_PEER_STATE_CONNECTED = 5,
    ENET_PEER_STATE_DISCONNECT_LATER = 6,
    ENET_PEER_STATE_DISCONNECTING = 7,
    ENET_PEER_STATE_ACHNOWLEDGING_DISCONNECT = 8,
    ENET_PEER_STATE_ZOMBIE = 9,
}

extern {
    // TODO: Continue here.
}
<|endoftext|>


<commit_before>use flow::{Flow, Observer};
use std::collections::HashMap;
use std::iter;
use std::sync::{Arc, Mutex, RwLock, Weak};
use std::time::{Duration, Instant};

type SharedFlow = Arc<RwLock<Flow>>;

type LinkPointer<T> = Weak<Mutex<T>>;

struct TimeLinkNode {
    key: String,
    timestamp: Instant,
    prev: Option<LinkPointer<TimeLinkNode>>,
    next: Option<LinkPointer<TimeLinkNode>>,
}

struct Entry {
    flow: SharedFlow,
    timelink: Arc<Mutex<TimeLinkNode>>,
}

pub struct Pool {
    weakref: Weak<RwLock<Pool>>,
    bucket: HashMap<String, Entry>,
    pool_size: Option<usize>,
    deactive_timeout: Option<Duration>,
    timelink_head: Option<LinkPointer<TimeLinkNode>>,
    timelink_tail: Option<LinkPointer<TimeLinkNode>>,
}

impl Entry {
    fn new(flow_id: &str, flow_ptr: SharedFlow) -> Self {
        Entry {
            flow: flow_ptr,
            timelink: Arc::new(Mutex::new(TimeLinkNode {
                                              key: flow_id.to_owned(),
                                              timestamp: Instant::now(),
                                              prev: Some(Weak::new()),
                                              next: Some(Weak::new()),
                                          })),
        }
    }
}

impl TimeLinkNode {
    fn push(node: &mut TimeLinkNode, node_ptr: &Arc<Mutex<TimeLinkNode>>, pool: &mut Pool) {
        node.prev = None;
        node.next = pool.timelink_head.clone();
        let weakref = Arc::downgrade(node_ptr);
        if let Some(ref head) = pool.timelink_head {
            let head = head.upgrade().unwrap();
            head.lock().unwrap().prev = Some(weakref.clone());
        }
        pool.timelink_head = Some(weakref);
    }

    fn unlink(node: &mut TimeLinkNode, pool: &mut Pool) {
        if let Some(ref prev) = node.prev {
            // Upgrade should always success.
            let prev = prev.upgrade().unwrap();
            prev.lock().unwrap().next = node.next.clone();
        } else {
            pool.timelink_head = node.next.clone();
        }
        if let Some(ref next) = node.next {
            // Upgrade should always success.
            let next = next.upgrade().unwrap();
            next.lock().unwrap().prev = node.prev.clone();
        } else {
            pool.timelink_tail = node.prev.clone();
        }
        node.prev = Some(Weak::new());
        node.next = Some(Weak::new());
    }
}

impl Pool {
    pub fn new(pool_size: Option<usize>, deactive_timeout: Option<Duration>) -> Arc<RwLock<Self>> {
        let pool = Pool {
            weakref: Weak::new(),
            bucket: HashMap::new(),
            pool_size,
            deactive_timeout,
            timelink_head: None,
            timelink_tail: None,
        };
        let pool_ptr = Arc::new(RwLock::new(pool));
        pool_ptr.write().unwrap().weakref = Arc::downgrade(&pool_ptr);
        pool_ptr
    }

    pub fn insert(&mut self, flow_ptr: SharedFlow) -> Result<(), ()> {
        if let Some(pool_size) = self.pool_size {
            if self.bucket.len() >= pool_size {
                self.sanitize_bucket();
                if self.bucket.len() >= pool_size {
                    return Err(());
                }
            }
        }

        // Occupy the flow to prevent from race condition.
        let mut flow = flow_ptr.write().unwrap();
        let timelink = {
            self.bucket
                .entry(flow.id.to_owned())
                .or_insert(Entry::new(&flow.id, flow_ptr.clone()))
                .timelink
                .clone()
        };

        flow.observe(self.weakref.clone());

        {
            let mut link = timelink.lock().unwrap();
            TimeLinkNode::push(&mut link, &timelink, self);
        }

        Ok(())
    }

    pub fn get(&self, flow_id: &str) -> Option<SharedFlow> {
        self.bucket.get(flow_id).map(|entry| entry.flow.clone())
    }

    pub fn remove(&mut self, flow_id: &str) -> Result<(), ()> {
        self.bucket
            .remove(flow_id)
            .ok_or(())
            .and_then(|entry| {
                let mut link = entry.timelink.lock().unwrap();
                TimeLinkNode::unlink(&mut link, self);
                Ok(())
            })
    }

    fn sanitize_bucket(&mut self) {
        let deactive_timeout = match self.deactive_timeout {
            Some(deactive_timeout) => deactive_timeout,
            None => return,
        };

        let droplist = iter::repeat(())
            .scan(self.timelink_tail.clone(), |tail_link, _| {
                tail_link
                    .as_ref()
                    .and_then(|tail| match tail.upgrade() {
                                  Some(tail_ptr) => Some(tail_ptr),
                                  None => None,
                              })
                    .and_then(|tail_ptr| {
                        let node = tail_ptr.lock().unwrap();
                        if node.timestamp.elapsed() <= deactive_timeout {
                            None
                        } else {
                            *tail_link = node.prev.clone();
                            Some(node.key.to_owned())
                        }
                    })
            })
            .collect::<Vec<String>>();

        for key in droplist.iter() {
            // Try to remove dead flows.
            self.remove(key).is_ok();
        }
    }
}

impl Observer for Weak<RwLock<Pool>> {
    fn on_active(&self, flow: &Flow) {
        if let Some(pool_ptr) = self.upgrade() {
            // TODO minimize the pool write lock.
            let mut pool = pool_ptr.write().unwrap();
            let link_ptr = match pool.bucket.get(&flow.id) {
                Some(entry) => entry.timelink.clone(),
                None => return,
            };
            {
                let mut link = link_ptr.lock().unwrap();
                link.timestamp = Instant::now();
                TimeLinkNode::unlink(&mut link, &mut pool);
                TimeLinkNode::push(&mut link, &link_ptr, &mut pool);
            }
        }
    }

    fn on_close(&self, flow: &Flow) {
        if let Some(pool_ptr) = self.upgrade() {
            let mut pool = pool_ptr.write().unwrap();
            // Try to remove the flow.
            pool.remove(&flow.id).is_ok();
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use flow::{self, Flow};
    use std::sync::Arc;
    use std::thread;
    use std::time::Duration;
    use tokio::reactor::Core;

    #[test]
    fn basic_operations() {
        let ptr = Pool::new(None, None);
        let flow_a = Flow::new(None);
        let flow_b = Flow::new(None);
        let flow_c = Flow::new(None);
        let (flowa_id, flowb_id, flowc_id) = {
            (flow_a.read().unwrap().id.to_owned(),
             flow_b.read().unwrap().id.to_owned(),
             flow_c.read().unwrap().id.to_owned())
        };
        {
            let mut pool = ptr.write().unwrap();
            assert_eq!(pool.insert(flow_a.clone()), Ok(()));
            assert_eq!(pool.insert(flow_b.clone()), Ok(()));
            assert_eq!(pool.insert(flow_c.clone()), Ok(()));
        }
        {
            let pool = ptr.read().unwrap();
            assert!(Arc::ptr_eq(&pool.get(&flowa_id).unwrap(), &flow_a));
            assert!(Arc::ptr_eq(&pool.get(&flowb_id).unwrap(), &flow_b));
            assert!(!Arc::ptr_eq(&pool.get(&flowa_id).unwrap(), &flow_b));
            assert!(!Arc::ptr_eq(&pool.get(&flowb_id).unwrap(), &flow_a));
            assert!(pool.get("C").is_none());
        }
        {
            let mut pool = ptr.write().unwrap();
            assert_eq!(pool.remove(&flowb_id), Ok(()));
            assert_eq!(pool.remove(&flowa_id), Ok(()));
            assert_eq!(pool.remove(&flowc_id), Ok(()));
        }
        {
            let pool = ptr.read().unwrap();
            assert!(pool.get(&flowa_id).is_none());
            assert!(pool.get(&flowb_id).is_none());
            assert!(pool.get(&flowc_id).is_none());
        }
    }

    #[test]
    fn close_recycle() {
        let mut core = Core::new().unwrap();
        let ptr = Pool::new(None, None);
        let flow = Flow::new(None);
        let flow_id = {
            flow.read().unwrap().id.to_owned()
        };
        {
            let mut pool = ptr.write().unwrap();
            pool.insert(flow.clone()).unwrap();
            assert!(Arc::ptr_eq(&pool.get(&flow_id).unwrap(), &flow));
        }
        {
            let fut = flow.write().unwrap().close();
            core.run(fut).unwrap();
        }
        {
            let fut = flow.read().unwrap().pull(0, Some(0));
            core.run(fut).is_err();
        }
        {
            let pool = ptr.read().unwrap();
            assert!(pool.get(&flow_id).is_none());
        }
    }

    #[test]
    fn dropped() {
        let mut core = Core::new().unwrap();
        let flow = Flow::new(None);
        {
            let ptr = Pool::new(None, None);
            let flow_id = {
                flow.read().unwrap().id.to_owned()
            };
            {
                let mut pool = ptr.write().unwrap();
                pool.insert(flow.clone()).unwrap();
                pool.remove(&flow_id).unwrap();
            }
            {
                let pool = ptr.read().unwrap();
                assert!(pool.get(&flow_id).is_none());
            }
            {
                let fut = flow.write().unwrap().close();
                core.run(fut).unwrap();
            }
        }
        {
            let fut = flow.read().unwrap().pull(0, Some(0));
            assert_eq!(core.run(fut), Err(flow::Error::Eof));
        }
    }

    #[test]
    fn overload_size() {
        let ptr = Pool::new(Some(1), None);
        let flow_a = Flow::new(None);
        let flow_b = Flow::new(None);
        {
            let mut pool = ptr.write().unwrap();
            assert_eq!(pool.insert(flow_a.clone()), Ok(()));
            assert_eq!(pool.insert(flow_b.clone()), Err(()));
        }
    }

    #[test]
    fn overload_time() {
        let mut core = Core::new().unwrap();
        let ptr = Pool::new(Some(3), Some(Duration::from_secs(6)));
        let flow_a = Flow::new(None);
        let flow_b = Flow::new(None);
        let flow_c = Flow::new(None);
        let flow_d = Flow::new(None);
        let flow_e = Flow::new(None);
        let flow_f = Flow::new(None);
        {
            let mut pool = ptr.write().unwrap();
            assert_eq!(pool.insert(flow_a.clone()), Ok(()));
            assert_eq!(pool.insert(flow_b.clone()), Ok(()));
            assert_eq!(pool.insert(flow_c.clone()), Ok(()));
        }
        thread::sleep(Duration::from_secs(4));
        {
            let fut = flow_a.write().unwrap().push(b"Hello");
            core.run(fut).unwrap();
        }
        thread::sleep(Duration::from_secs(4));
        {
            let mut pool = ptr.write().unwrap();
            assert_eq!(pool.insert(flow_d.clone()), Ok(()));
            assert_eq!(pool.insert(flow_e.clone()), Ok(()));
            assert_eq!(pool.insert(flow_f.clone()), Err(()));
        }
    }
}
<commit_msg>Refactor<commit_after>use flow::{Flow, Observer};
use std::collections::HashMap;
use std::iter;
use std::sync::{Arc, Mutex, RwLock, Weak};
use std::time::{Duration, Instant};

type SharedFlow = Arc<RwLock<Flow>>;

type LinkPointer<T> = Weak<Mutex<T>>;

struct TimeLinkNode {
    key: String,
    timestamp: Instant,
    prev: Option<LinkPointer<TimeLinkNode>>,
    next: Option<LinkPointer<TimeLinkNode>>,
}

struct Entry {
    flow: SharedFlow,
    node: Arc<Mutex<TimeLinkNode>>,
}

pub struct Pool {
    weakref: Weak<RwLock<Pool>>,
    bucket: HashMap<String, Entry>,
    pool_size: Option<usize>,
    deactive_timeout: Option<Duration>,
    timelink_head: Option<LinkPointer<TimeLinkNode>>,
    timelink_tail: Option<LinkPointer<TimeLinkNode>>,
}

impl Entry {
    fn new(flow_id: &str, flow_ptr: SharedFlow) -> Self {
        Entry {
            flow: flow_ptr,
            node: Arc::new(Mutex::new(TimeLinkNode {
                                          key: flow_id.to_owned(),
                                          timestamp: Instant::now(),
                                          prev: Some(Weak::new()),
                                          next: Some(Weak::new()),
                                      })),
        }
    }
}

impl TimeLinkNode {
    fn push(node: &mut TimeLinkNode, node_ptr: &Arc<Mutex<TimeLinkNode>>, pool: &mut Pool) {
        node.prev = None;
        node.next = pool.timelink_head.clone();
        let weakref = Arc::downgrade(node_ptr);
        if let Some(ref head) = pool.timelink_head {
            let head = head.upgrade().unwrap();
            head.lock().unwrap().prev = Some(weakref.clone());
        }
        pool.timelink_head = Some(weakref);
    }

    fn unlink(node: &mut TimeLinkNode, pool: &mut Pool) {
        if let Some(ref prev) = node.prev {
            // Upgrade should always success.
            let prev = prev.upgrade().unwrap();
            prev.lock().unwrap().next = node.next.clone();
        } else {
            pool.timelink_head = node.next.clone();
        }
        if let Some(ref next) = node.next {
            // Upgrade should always success.
            let next = next.upgrade().unwrap();
            next.lock().unwrap().prev = node.prev.clone();
        } else {
            pool.timelink_tail = node.prev.clone();
        }
        node.prev = Some(Weak::new());
        node.next = Some(Weak::new());
    }
}

impl Pool {
    pub fn new(pool_size: Option<usize>, deactive_timeout: Option<Duration>) -> Arc<RwLock<Self>> {
        let pool = Pool {
            weakref: Weak::new(),
            bucket: HashMap::new(),
            pool_size,
            deactive_timeout,
            timelink_head: None,
            timelink_tail: None,
        };
        let pool_ptr = Arc::new(RwLock::new(pool));
        pool_ptr.write().unwrap().weakref = Arc::downgrade(&pool_ptr);
        pool_ptr
    }

    pub fn insert(&mut self, flow_ptr: SharedFlow) -> Result<(), ()> {
        if let Some(pool_size) = self.pool_size {
            if self.bucket.len() >= pool_size {
                self.sanitize_bucket();
                if self.bucket.len() >= pool_size {
                    return Err(());
                }
            }
        }
        {
            // Occupy the flow to prevent from race condition.
            let mut flow = flow_ptr.write().unwrap();
            let node_ptr = {
                self.bucket
                    .entry(flow.id.to_owned())
                    .or_insert(Entry::new(&flow.id, flow_ptr.clone()))
                    .node
                    .clone()
            };
            {
                let mut node = node_ptr.lock().unwrap();
                TimeLinkNode::push(&mut node, &node_ptr, self);
            }
            flow.observe(self.weakref.clone());
        }
        Ok(())
    }

    pub fn get(&self, flow_id: &str) -> Option<SharedFlow> {
        self.bucket.get(flow_id).map(|entry| entry.flow.clone())
    }

    pub fn remove(&mut self, flow_id: &str) -> Result<(), ()> {
        self.bucket
            .remove(flow_id)
            .ok_or(())
            .and_then(|entry| {
                let mut node = entry.node.lock().unwrap();
                TimeLinkNode::unlink(&mut node, self);
                Ok(())
            })
    }

    fn sanitize_bucket(&mut self) {
        let deactive_timeout = match self.deactive_timeout {
            Some(deactive_timeout) => deactive_timeout,
            None => return,
        };

        let droplist = iter::repeat(())
            .scan(self.timelink_tail.clone(), |tail_weakref, _| {
                tail_weakref
                    .as_ref()
                    .and_then(|weakref| weakref.upgrade())
                    .and_then(|tail_ptr| {
                        let node = tail_ptr.lock().unwrap();
                        if node.timestamp.elapsed() <= deactive_timeout {
                            None
                        } else {
                            *tail_weakref = node.prev.clone();
                            Some(node.key.to_owned())
                        }
                    })
            })
            .collect::<Vec<String>>();

        for key in droplist.iter() {
            // Try to remove dead flows.
            self.remove(key).is_ok();
        }
    }
}

impl Observer for Weak<RwLock<Pool>> {
    fn on_active(&self, flow: &Flow) {
        if let Some(pool_ptr) = self.upgrade() {
            // TODO minimize the pool write lock.
            let mut pool = pool_ptr.write().unwrap();
            let node_ptr = match pool.bucket.get(&flow.id) {
                Some(entry) => entry.node.clone(),
                None => return,
            };
            {
                let mut node = node_ptr.lock().unwrap();
                node.timestamp = Instant::now();
                TimeLinkNode::unlink(&mut node, &mut pool);
                TimeLinkNode::push(&mut node, &node_ptr, &mut pool);
            }
        }
    }

    fn on_close(&self, flow: &Flow) {
        if let Some(pool_ptr) = self.upgrade() {
            let mut pool = pool_ptr.write().unwrap();
            // Try to remove the flow.
            pool.remove(&flow.id).is_ok();
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use flow::{self, Flow};
    use std::sync::Arc;
    use std::thread;
    use std::time::Duration;
    use tokio::reactor::Core;

    #[test]
    fn basic_operations() {
        let ptr = Pool::new(None, None);
        let flow_a = Flow::new(None);
        let flow_b = Flow::new(None);
        let flow_c = Flow::new(None);
        let (flowa_id, flowb_id, flowc_id) = {
            (flow_a.read().unwrap().id.to_owned(),
             flow_b.read().unwrap().id.to_owned(),
             flow_c.read().unwrap().id.to_owned())
        };
        {
            let mut pool = ptr.write().unwrap();
            assert_eq!(pool.insert(flow_a.clone()), Ok(()));
            assert_eq!(pool.insert(flow_b.clone()), Ok(()));
            assert_eq!(pool.insert(flow_c.clone()), Ok(()));
        }
        {
            let pool = ptr.read().unwrap();
            assert!(Arc::ptr_eq(&pool.get(&flowa_id).unwrap(), &flow_a));
            assert!(Arc::ptr_eq(&pool.get(&flowb_id).unwrap(), &flow_b));
            assert!(!Arc::ptr_eq(&pool.get(&flowa_id).unwrap(), &flow_b));
            assert!(!Arc::ptr_eq(&pool.get(&flowb_id).unwrap(), &flow_a));
            assert!(pool.get("C").is_none());
        }
        {
            let mut pool = ptr.write().unwrap();
            assert_eq!(pool.remove(&flowb_id), Ok(()));
            assert_eq!(pool.remove(&flowa_id), Ok(()));
            assert_eq!(pool.remove(&flowc_id), Ok(()));
        }
        {
            let pool = ptr.read().unwrap();
            assert!(pool.get(&flowa_id).is_none());
            assert!(pool.get(&flowb_id).is_none());
            assert!(pool.get(&flowc_id).is_none());
        }
    }

    #[test]
    fn close_recycle() {
        let mut core = Core::new().unwrap();
        let ptr = Pool::new(None, None);
        let flow = Flow::new(None);
        let flow_id = {
            flow.read().unwrap().id.to_owned()
        };
        {
            let mut pool = ptr.write().unwrap();
            pool.insert(flow.clone()).unwrap();
            assert!(Arc::ptr_eq(&pool.get(&flow_id).unwrap(), &flow));
        }
        {
            let fut = flow.write().unwrap().close();
            core.run(fut).unwrap();
        }
        {
            let fut = flow.read().unwrap().pull(0, Some(0));
            core.run(fut).is_err();
        }
        {
            let pool = ptr.read().unwrap();
            assert!(pool.get(&flow_id).is_none());
        }
    }

    #[test]
    fn dropped() {
        let mut core = Core::new().unwrap();
        let flow = Flow::new(None);
        {
            let ptr = Pool::new(None, None);
            let flow_id = {
                flow.read().unwrap().id.to_owned()
            };
            {
                let mut pool = ptr.write().unwrap();
                pool.insert(flow.clone()).unwrap();
                pool.remove(&flow_id).unwrap();
            }
            {
                let pool = ptr.read().unwrap();
                assert!(pool.get(&flow_id).is_none());
            }
            {
                let fut = flow.write().unwrap().close();
                core.run(fut).unwrap();
            }
        }
        {
            let fut = flow.read().unwrap().pull(0, Some(0));
            assert_eq!(core.run(fut), Err(flow::Error::Eof));
        }
    }

    #[test]
    fn overload_size() {
        let ptr = Pool::new(Some(1), None);
        let flow_a = Flow::new(None);
        let flow_b = Flow::new(None);
        {
            let mut pool = ptr.write().unwrap();
            assert_eq!(pool.insert(flow_a.clone()), Ok(()));
            assert_eq!(pool.insert(flow_b.clone()), Err(()));
        }
    }

    #[test]
    fn overload_time() {
        let mut core = Core::new().unwrap();
        let ptr = Pool::new(Some(3), Some(Duration::from_secs(6)));
        let flow_a = Flow::new(None);
        let flow_b = Flow::new(None);
        let flow_c = Flow::new(None);
        let flow_d = Flow::new(None);
        let flow_e = Flow::new(None);
        let flow_f = Flow::new(None);
        {
            let mut pool = ptr.write().unwrap();
            assert_eq!(pool.insert(flow_a.clone()), Ok(()));
            assert_eq!(pool.insert(flow_b.clone()), Ok(()));
            assert_eq!(pool.insert(flow_c.clone()), Ok(()));
        }
        thread::sleep(Duration::from_secs(4));
        {
            let fut = flow_a.write().unwrap().push(b"Hello");
            core.run(fut).unwrap();
        }
        thread::sleep(Duration::from_secs(4));
        {
            let mut pool = ptr.write().unwrap();
            assert_eq!(pool.insert(flow_d.clone()), Ok(()));
            assert_eq!(pool.insert(flow_e.clone()), Ok(()));
            assert_eq!(pool.insert(flow_f.clone()), Err(()));
        }
    }
}
<|endoftext|>