<commit_before>use std::net::SocketAddr;
use std::str::FromStr;

use hyper::header;
use hyper::method::Method;

use url::Host;

use urlencoded;

use unicase::UniCase;

use iron;
use iron::prelude::*;

use models;

pub struct XForwardedMiddleware;

fn get_one_raw(r: &mut Request, key: &str) -> Option<String> {
    r.headers
        .get_raw(key)
        .and_then(|vals| if vals.len() > 0 { vals.to_owned().pop() } else { None })
        .and_then(|x| String::from_utf8(x).ok())
}

impl iron::middleware::BeforeMiddleware for XForwardedMiddleware {
    fn before(&self, r: &mut Request) -> IronResult<()> {
        get_one_raw(r, "X-Forwarded-Host")
            .and_then(|host| Host::parse(&host[..]).ok())
            .map(|x| r.url.host = x);

        get_one_raw(r, "X-Forwarded-Port")
            .and_then(|s| u16::from_str(&s[..]).ok())
            .map(|port| r.url.port = port);

        get_one_raw(r, "X-Forwarded-For")
            .and_then(parse_remote_addrs)
            .map(|ip| r.remote_addr = ip);

        get_one_raw(r, "X-Forwarded-Proto")
            .map(|scheme| r.url.scheme = scheme.to_lowercase());

        Ok(())
    }
}

fn parse_remote_addrs(s: String) -> Option<SocketAddr> {
    let split = s.split(',');
    let mut iter_ips = split.map(|x| x.trim()).filter(|x| x.len() > 0);
    iter_ips.next().and_then(|ip| SocketAddr::from_str(ip).ok())
}

pub struct SecurityHeaderMiddleware;

impl iron::middleware::AfterMiddleware for SecurityHeaderMiddleware {
    fn after(&self, request: &mut Request, mut response: Response) -> IronResult<Response> {
        set_cors_headers(&request, &mut response);
        set_frame_options(&request, &mut response);
        Ok(response)
    }

    fn catch(&self, request: &mut Request, mut error: IronError) -> IronResult<Response> {
        set_cors_headers(&request, &mut error.response);
        set_frame_options(&request, &mut error.response);
        Err(error)
    }
}

/// Required by remoteStorage spec
fn set_cors_headers(rq: &Request, r: &mut Response) {
    match &rq.url.path[0][..] {
        ".well-known" | "storage" => (),
        _ => return
    };

    let origin = match rq.headers.get_raw("Origin") {
        Some(x) => if x.len() == 1 {
            match String::from_utf8(x.to_owned().into_iter().next().unwrap()) {
                Ok(x) => x,
                Err(_) => return
            }
        } else {
            return;
        },
        None => return
    };

    r.headers.set(header::AccessControlAllowOrigin::Value(origin));
    // FIXME: https://github.com/hyperium/hyper/issues/673
    r.headers.set_raw("Access-Control-Expose-Headers", vec![b"ETag".to_vec()]);
    r.headers.set(header::AccessControlAllowMethods(vec![Method::Get, Method::Put, Method::Delete]));
    r.headers.set(header::AccessControlAllowHeaders(vec![
        UniCase("Authorization".to_owned()),
        UniCase("Content-Type".to_owned()),
        UniCase("Origin".to_owned()),
        UniCase("If-Match".to_owned()),
        UniCase("If-None-Match".to_owned()),
    ]));
}

/// Prevent clickjacking attacks like described in OAuth RFC
/// https://tools.ietf.org/html/rfc6749#section-10.13
fn set_frame_options(rq: &Request, r: &mut Response) {
    match &rq.url.path[0][..] {
        // It's probably fine to embed user storage data into other documents
        "storage" => return,
        _ => ()
    };

    r.headers.set_raw("X-Frame-Options", vec![b"DENY".to_vec()]);
}

pub trait EtagMatcher {
    fn matches_etag(&self, given: Option<&str>) -> bool;
}

impl EtagMatcher for header::IfNoneMatch {
    fn matches_etag(&self, given: Option<&str>) -> bool {
        match *self {
            header::IfNoneMatch::Any => given.is_some(),
            header::IfNoneMatch::Items(ref values) => {
                match given {
                    Some(given_value) => values.iter().any(|val| val.tag() == &given_value[..]),
                    None => false
                }
            }
        }
    }
}

impl EtagMatcher for header::IfMatch {
    fn matches_etag(&self, given: Option<&str>) -> bool {
        match *self {
            header::IfMatch::Any => given.is_some(),
            header::IfMatch::Items(ref values) => {
                match given {
                    Some(given_value) => values.iter().any(|val| val.tag() == &given_value[..]),
                    None => false
                }
            }
        }
    }
}

pub fn preconditions_ok(request: &Request, etag: Option<&str>) -> bool {
    match request.headers.get::<header::IfNoneMatch>() {
        Some(header) => if header.matches_etag(etag) { return false; },
        None => ()
    };

    match request.headers.get::<header::IfMatch>() {
        Some(header) => if !header.matches_etag(etag) { return false; },
        None => ()
    };
    true
}

pub trait FormDataHelper<K: ?Sized, V> {
    fn get_only<Q: AsRef<K>>(&self, k: Q) -> Option<&String>;
}

impl FormDataHelper<str, String> for urlencoded::QueryMap {
    fn get_only<Q: AsRef<str>>(&self, k: Q) -> Option<&String> {
        match self.get(&k.as_ref().to_owned()) {
            Some(x) if x.len() == 1 => Some(&x[0]),
            _ => None
        }
    }
}

pub fn get_account_id(user: &models::User, request: &Request) -> String {
    let ref url = request.url;
    format!("{}@{}:{}", user.userid, url.host, url.port)
}
<commit_msg>Fix port in account_id<commit_after>use std::net::SocketAddr;
use std::str::FromStr;

use hyper::header;
use hyper::method::Method;

use url::Host;
use url::SchemeData;

use urlencoded;

use unicase::UniCase;

use iron;
use iron::prelude::*;

use models;

pub struct XForwardedMiddleware;

fn get_one_raw(r: &mut Request, key: &str) -> Option<String> {
    r.headers
        .get_raw(key)
        .and_then(|vals| if vals.len() > 0 { vals.to_owned().pop() } else { None })
        .and_then(|x| String::from_utf8(x).ok())
}

impl iron::middleware::BeforeMiddleware for XForwardedMiddleware {
    fn before(&self, r: &mut Request) -> IronResult<()> {
        get_one_raw(r, "X-Forwarded-Host")
            .and_then(|host| Host::parse(&host[..]).ok())
            .map(|x| r.url.host = x);

        get_one_raw(r, "X-Forwarded-Port")
            .and_then(|s| u16::from_str(&s[..]).ok())
            .map(|port| r.url.port = port);

        get_one_raw(r, "X-Forwarded-For")
            .and_then(parse_remote_addrs)
            .map(|ip| r.remote_addr = ip);

        get_one_raw(r, "X-Forwarded-Proto")
            .map(|scheme| r.url.scheme = scheme.to_lowercase());

        Ok(())
    }
}

fn parse_remote_addrs(s: String) -> Option<SocketAddr> {
    let split = s.split(',');
    let mut iter_ips = split.map(|x| x.trim()).filter(|x| x.len() > 0);
    iter_ips.next().and_then(|ip| SocketAddr::from_str(ip).ok())
}

pub struct SecurityHeaderMiddleware;

impl iron::middleware::AfterMiddleware for SecurityHeaderMiddleware {
    fn after(&self, request: &mut Request, mut response: Response) -> IronResult<Response> {
        set_cors_headers(&request, &mut response);
        set_frame_options(&request, &mut response);
        Ok(response)
    }

    fn catch(&self, request: &mut Request, mut error: IronError) -> IronResult<Response> {
        set_cors_headers(&request, &mut error.response);
        set_frame_options(&request, &mut error.response);
        Err(error)
    }
}

/// Required by remoteStorage spec
fn set_cors_headers(rq: &Request, r: &mut Response) {
    match &rq.url.path[0][..] {
        ".well-known" | "storage" => (),
        _ => return
    };

    let origin = match rq.headers.get_raw("Origin") {
        Some(x) => if x.len() == 1 {
            match String::from_utf8(x.to_owned().into_iter().next().unwrap()) {
                Ok(x) => x,
                Err(_) => return
            }
        } else {
            return;
        },
        None => return
    };

    r.headers.set(header::AccessControlAllowOrigin::Value(origin));
    // FIXME: https://github.com/hyperium/hyper/issues/673
    r.headers.set_raw("Access-Control-Expose-Headers", vec![b"ETag".to_vec()]);
    r.headers.set(header::AccessControlAllowMethods(vec![Method::Get, Method::Put, Method::Delete]));
    r.headers.set(header::AccessControlAllowHeaders(vec![
        UniCase("Authorization".to_owned()),
        UniCase("Content-Type".to_owned()),
        UniCase("Origin".to_owned()),
        UniCase("If-Match".to_owned()),
        UniCase("If-None-Match".to_owned()),
    ]));
}

/// Prevent clickjacking attacks like described in OAuth RFC
/// https://tools.ietf.org/html/rfc6749#section-10.13
fn set_frame_options(rq: &Request, r: &mut Response) {
    match &rq.url.path[0][..] {
        // It's probably fine to embed user storage data into other documents
        "storage" => return,
        _ => ()
    };

    r.headers.set_raw("X-Frame-Options", vec![b"DENY".to_vec()]);
}

pub trait EtagMatcher {
    fn matches_etag(&self, given: Option<&str>) -> bool;
}

impl EtagMatcher for header::IfNoneMatch {
    fn matches_etag(&self, given: Option<&str>) -> bool {
        match *self {
            header::IfNoneMatch::Any => given.is_some(),
            header::IfNoneMatch::Items(ref values) => {
                match given {
                    Some(given_value) => values.iter().any(|val| val.tag() == &given_value[..]),
                    None => false
                }
            }
        }
    }
}

impl EtagMatcher for header::IfMatch {
    fn matches_etag(&self, given: Option<&str>) -> bool {
        match *self {
            header::IfMatch::Any => given.is_some(),
            header::IfMatch::Items(ref values) => {
                match given {
                    Some(given_value) => values.iter().any(|val| val.tag() == &given_value[..]),
                    None => false
                }
            }
        }
    }
}

pub fn preconditions_ok(request: &Request, etag: Option<&str>) -> bool {
    match request.headers.get::<header::IfNoneMatch>() {
        Some(header) => if header.matches_etag(etag) { return false; },
        None => ()
    };

    match request.headers.get::<header::IfMatch>() {
        Some(header) => if !header.matches_etag(etag) { return false; },
        None => ()
    };
    true
}

pub trait FormDataHelper<K: ?Sized, V> {
    fn get_only<Q: AsRef<K>>(&self, k: Q) -> Option<&String>;
}

impl FormDataHelper<str, String> for urlencoded::QueryMap {
    fn get_only<Q: AsRef<str>>(&self, k: Q) -> Option<&String> {
        match self.get(&k.as_ref().to_owned()) {
            Some(x) if x.len() == 1 => Some(&x[0]),
            _ => None
        }
    }
}

pub fn get_account_id(user: &models::User, request: &Request) -> String {
    let url = request.url.clone().into_generic_url();
    let scheme_data = match url.scheme_data {
        SchemeData::Relative(ref x) => x,
        _ => panic!("Expected relative scheme data.")
    };

    let mut rv = format!("{}@{}", user.userid, scheme_data.host);
    if let Some(port) = scheme_data.port {
        if Some(port) != scheme_data.default_port {
            rv.push_str(&format!(":{}", port)[..]);
        }
    };

    rv
}
<|endoftext|>


<commit_before>pub mod pooling;
pub mod convolution;
pub mod loss;
pub mod activation;
pub mod dropout;
pub mod linear;
pub mod auto;

pub use self::pooling::*;
pub use self::convolution::*;
pub use self::loss::*;
pub use self::activation::*;
pub use self::dropout::*;
pub use self::linear::*;
pub use self::auto::*;
<commit_msg>remove reference to loss<commit_after>pub mod pooling;
pub mod convolution;
pub mod activation;
pub mod dropout;
pub mod linear;
pub mod auto;

pub use self::pooling::*;
pub use self::convolution::*;
pub use self::activation::*;
pub use self::dropout::*;
pub use self::linear::*;
pub use self::auto::*;
<|endoftext|>


<commit_before>//! gram defines the atomic unit of the miknet protocol.

use bincode::serialize;
use itertools::{Either, Itertools};
use serde_derive::{Deserialize, Serialize};
use std::net::SocketAddr;
use std::time::Duration;

use crate::api;
use crate::conn::handshake::{Key, StateCookie, Tcb};
use crate::conn::sequence::Segment;
use crate::random::random;

#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub enum Chunk {
    Init {
        token: u32,
        tsn:   u32,
    },
    InitAck {
        token:        u32,
        tsn:          u32,
        state_cookie: StateCookie,
    },
    CookieEcho(StateCookie),
    CookieAck,
    Shutdown,
    ShutdownAck,
    ShutdownComplete,
    CfgMismatch,
    Data {
        channel_id: u32,
        seg:        Segment,
    },
    DataAck {
        channel_id: u32,
        seq:        u32,
    },
}

/// Gram is the atomic unit of the miknet protocol. All transmissions are represented as a gram
/// before they are written on the network.
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub struct Gram {
    pub token:  u32,
    pub chunks: Vec<Chunk>,
}

#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum Timer {
    InitTimer,
    CookieSentTimer,
}

impl Timer {
    fn duration(&self) -> Duration {
        match *self {
            Timer::InitTimer => Duration::new(3, 0),
            Timer::CookieSentTimer => Duration::new(5, 0),
        }
    }
}

#[derive(Clone, Debug, PartialEq)]
pub enum Api {
    Tx(Vec<u8>),
    Disc,
    Conn,
    Shutdown,
}

#[derive(Debug, PartialEq)]
pub enum Event {
    Api(Api),
    Gram(Gram),
    Chunk(Chunk),
    Timer(Timer),
    InvalidGram,
}

impl From<Chunk> for Event {
    fn from(chunk: Chunk) -> Event { Event::Chunk(chunk) }
}

#[derive(Clone, Debug, Eq, PartialEq)]
pub enum Cmd {
    Chunk(Chunk),
    Net(Vec<u8>),
    Timer(Timer),
    User(api::Event),
}

pub trait Protocol: Sized {
    fn establish() -> Self;
    fn step(self, event: Event) -> (Self, Vec<Cmd>);
}

/// Connection is a relationship between two miknet nodes. Handshake and teardown based roughly on
/// SCTP.
#[derive(Clone, Debug, PartialEq)]
pub enum Connection<P: Protocol> {
    Listen {
        key: Key,
    },
    InitSent {
        token: u32,
        tsn:   u32,
        queue: Vec<(SocketAddr, Api)>,
    },
    InitAckSent {
        their_token: u32,
    },
    CookieEchoed {
        tcb:   Tcb,
        queue: Vec<(SocketAddr, Api)>,
    },
    Established {
        tcb:      Tcb,
        protocol: P,
    },
    ShutdownSent {
        token:       u32,
        their_token: u32,
    },
    ShutdownAckSent {
        token:       u32,
        their_token: u32,
    },
    Shutdown {
        their_token: u32,
    },
    Failed,
}

impl<P: Protocol> Connection<P> {
    /// Returns a connection using the given key to sign and verify state cookies.
    pub fn new(key: Key) -> Self { Connection::Listen { key } }

    /// Processes an event and returns the next state of the connection and any commands that
    /// should be executed as part of the transition.
    pub fn step(
        self,
        peer: SocketAddr,
        event: Event,
    ) -> (Self, Vec<(SocketAddr, Cmd)>) {
        let (next, cmds) = self.handle_event(&peer, event);
        let (chunks, mut cmds): (Vec<Chunk>, Vec<Cmd>) =
            cmds.into_iter().partition_map(|cmd| match cmd {
                Cmd::Chunk(chunk) => Either::Left(chunk),
                cmd => Either::Right(cmd),
            });
        cmds.extend(next.build_grams(chunks));
        (
            next,
            cmds.into_iter().map(|cmd| (peer.clone(), cmd)).collect(),
        )
    }

    /// Returns whether this connection should be persisted between events. For example, we do not
    /// keep an InitAckSent state between events to prevent DOS attacks.
    pub fn should_persist(&self) -> bool {
        match *self {
            Connection::Listen { .. } => false,
            Connection::InitAckSent { .. } => false,
            Connection::Failed => false,
            _ => true,
        }
    }

    /// Prepares chunks as net commands to write out grams.
    fn build_grams(&self, chunks: Vec<Chunk>) -> Vec<Cmd> {
        let token = self.token();
        let grams = match *self {
            _ => vec![Gram { token, chunks }],
        };
        grams
            .into_iter()
            .map(|gram| Cmd::Net(serialize(&gram).unwrap()))
            .collect()
    }

    /// Returns the token this connection should embed in all its grams for association.
    fn token(&self) -> u32 {
        match *self {
            Connection::InitAckSent { their_token } => their_token,
            Connection::InitSent { token, .. } => token,
            Connection::CookieEchoed { ref tcb, .. } => tcb.their_token,
            Connection::Established { ref tcb, .. } => tcb.their_token,
            Connection::ShutdownSent { their_token, .. } => their_token,
            Connection::ShutdownAckSent { their_token, .. } => their_token,
            Connection::Shutdown { their_token } => their_token,
            _ => 0,
        }
    }

    /// Returns the token we expect in valid grams from our peer.
    fn expected_token(&self) -> Option<u32> {
        match *self {
            Connection::InitSent { token, .. } => Some(token),
            Connection::CookieEchoed { ref tcb, .. } => Some(tcb.our_token),
            Connection::Established { ref tcb, .. } => Some(tcb.our_token),
            Connection::ShutdownSent { token, .. } => Some(token),
            Connection::ShutdownAckSent { token, .. } => Some(token),
            _ => None,
        }
    }

    fn handle_events(
        self,
        peer: &SocketAddr,
        events: Vec<Event>,
    ) -> (Self, Vec<Cmd>) {
        events.into_iter().fold(
            (self, Vec::new()),
            |(conn, mut cmds), event| {
                let (next_conn, more_cmds) = conn.handle_event(peer, event);
                cmds.extend(more_cmds);
                (next_conn, cmds)
            },
        )
    }

    fn handle_event(self, peer: &SocketAddr, event: Event) -> (Self, Vec<Cmd>) {
        match (self, event) {
            (conn, Event::Gram(gram)) => match conn.expected_token() {
                Some(expected_token) if gram.token != expected_token => {
                    conn.handle_event(peer, Event::InvalidGram)
                }
                _ => conn.handle_events(
                    peer,
                    gram.chunks.into_iter().map(Chunk::into).collect(),
                ),
            },
            (
                Connection::Listen { key },
                Event::Chunk(Chunk::Init {
                    token: their_token,
                    tsn: their_tsn,
                }),
            ) => {
                let (our_tsn, our_token) = (random(), random());
                (
                    Connection::InitAckSent { their_token },
                    vec![Cmd::Chunk(Chunk::InitAck {
                        tsn:          our_tsn,
                        token:        our_token,
                        state_cookie: StateCookie::new(
                            Tcb {
                                our_tsn,
                                our_token,
                                their_token,
                                their_tsn,
                            },
                            &key,
                        ),
                    })],
                )
            }
            (Connection::Listen { .. }, Event::Api(Api::Conn)) => {
                let (token, tsn) = (random(), random());
                (
                    Connection::InitSent {
                        token,
                        tsn,
                        queue: Vec::new(),
                    },
                    vec![
                        Cmd::Chunk(Chunk::Init { token, tsn }),
                        Cmd::Timer(Timer::InitTimer),
                    ],
                )
            }
            (Connection::InitSent { .. }, Event::Timer(Timer::InitTimer)) => (
                Connection::Failed,
                vec![Cmd::User(api::Event::ConnectionAttemptTimedOut(*peer))],
            ),
            (
                Connection::InitSent {
                    token: our_token,
                    tsn: our_tsn,
                    queue,
                },
                Event::Chunk(Chunk::InitAck {
                    token: their_token,
                    tsn: their_tsn,
                    state_cookie,
                }),
            ) => (
                Connection::CookieEchoed {
                    tcb:   Tcb {
                        our_tsn,
                        our_token,
                        their_tsn,
                        their_token,
                    },
                    queue: queue,
                },
                vec![
                    Cmd::Chunk(Chunk::CookieEcho(state_cookie)),
                    Cmd::Timer(Timer::CookieSentTimer),
                ],
            ),
            (Connection::InitSent { token, tsn, queue }, Event::Api(ae)) => (
                Connection::InitSent {
                    token,
                    tsn,
                    queue: queue
                        .into_iter()
                        .chain(vec![(*peer, ae)].into_iter())
                        .collect(),
                },
                Vec::new(),
            ),
            (Connection::CookieEchoed { tcb, queue }, Event::Api(ae)) => (
                Connection::CookieEchoed {
                    tcb,
                    queue: queue
                        .into_iter()
                        .chain(vec![(*peer, ae)].into_iter())
                        .collect(),
                },
                Vec::new(),
            ),
            (
                Connection::CookieEchoed { tcb, queue },
                Event::Chunk(Chunk::CookieAck),
            ) => {
                let (conn, mut cmds) = queue
                    .into_iter()
                    .map(|(peer, ae)| (peer, Event::Api(ae)))
                    .fold(
                        (
                            Connection::Established {
                                tcb,
                                protocol: P::establish(),
                            },
                            Vec::new(),
                        ),
                        |(conn, mut cmds), (peer, ae)| {
                            let (next_conn, more_cmds) =
                                conn.handle_event(&peer, ae);
                            cmds.extend(more_cmds);
                            (next_conn, cmds)
                        },
                    );
                cmds.extend(vec![Cmd::User(
                    api::Event::ConnectionEstablished(*peer),
                )]);
                (conn, cmds)
            }
            (
                Connection::Listen { ref key, .. },
                Event::Chunk(Chunk::CookieEcho(ref state_cookie)),
            )
                if state_cookie.signed_by(&key) =>
            {
                (
                    Connection::Established {
                        tcb:      state_cookie.tcb.clone(),
                        protocol: P::establish(),
                    },
                    vec![
                        Cmd::Chunk(Chunk::CookieAck),
                        Cmd::User(api::Event::ConnectionEstablished(*peer)),
                    ],
                )
            }
            (Connection::Established { tcb, .. }, Event::Api(Api::Disc)) => (
                Connection::ShutdownSent {
                    token:       tcb.our_token,
                    their_token: tcb.their_token,
                },
                vec![Cmd::Chunk(Chunk::Shutdown)],
            ),
            (
                Connection::Established { tcb, .. },
                Event::Chunk(Chunk::Shutdown),
            ) => (
                Connection::ShutdownAckSent {
                    token:       tcb.our_token,
                    their_token: tcb.their_token,
                },
                vec![Cmd::Chunk(Chunk::ShutdownAck)],
            ),
            (
                Connection::ShutdownSent { token, their_token },
                Event::Chunk(Chunk::Shutdown),
            ) => (
                Connection::ShutdownAckSent { token, their_token },
                vec![Cmd::Chunk(Chunk::ShutdownAck)],
            ),
            (
                Connection::ShutdownSent { their_token, .. },
                Event::Chunk(Chunk::ShutdownAck),
            ) => (
                Connection::Shutdown { their_token },
                vec![
                    Cmd::User(api::Event::Disconnect(*peer)),
                    Cmd::Chunk(Chunk::ShutdownComplete),
                ],
            ),
            (
                Connection::ShutdownAckSent { their_token, .. },
                Event::Chunk(Chunk::ShutdownComplete),
            ) => (
                Connection::Shutdown { their_token },
                vec![Cmd::User(api::Event::Disconnect(*peer))],
            ),
            (
                Connection::ShutdownAckSent { their_token, .. },
                Event::Chunk(Chunk::ShutdownAck),
            ) => (
                Connection::Shutdown { their_token },
                vec![
                    Cmd::User(api::Event::Disconnect(*peer)),
                    Cmd::Chunk(Chunk::ShutdownComplete),
                ],
            ),
            (conn, _) => (conn, Vec::new()),
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use crate::random;
    use std::str::FromStr;

    struct DummyProtocol;

    impl Protocol for DummyProtocol {
        fn establish() -> Self { DummyProtocol {} }

        fn step(self, event: Event) -> (Self, Vec<Cmd>) { (self, Vec::new()) }
    }

    fn expect(expectations: Vec<(Event, Vec<Cmd>)>) {
        let peer_addr =
            SocketAddr::from_str("127.0.0.1:0").expect("any address");
        let mut conn =
            Connection::<DummyProtocol>::new(Key::new().expect("key"));
        for (event, expected_cmds) in expectations {
            let (next_conn, cmds) = conn.handle_event(&peer_addr, event);
            assert_eq!(cmds, expected_cmds);
            conn = next_conn
        }
    }

    #[test]
    fn handshake() {
        expect(vec![(
            Event::Api(Api::Conn),
            vec![
                Cmd::Chunk(Chunk::Init {
                    token: random::RAND_TEST_CONST,
                    tsn:   random::RAND_TEST_CONST,
                }),
                Cmd::Timer(Timer::InitTimer),
            ],
        )])
    }
}
<commit_msg>Create tumbler test for protocol.<commit_after>//! gram defines the atomic unit of the miknet protocol.

use bincode::serialize;
use itertools::{Either, Itertools};
use serde_derive::{Deserialize, Serialize};
use std::fmt::Debug;
use std::net::SocketAddr;
use std::time::Duration;

use crate::api;
use crate::conn::handshake::{Key, StateCookie, Tcb};
use crate::conn::sequence::Segment;
use crate::random::random;

#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub enum Chunk {
    Init {
        token: u32,
        tsn:   u32,
    },
    InitAck {
        token:        u32,
        tsn:          u32,
        state_cookie: StateCookie,
    },
    CookieEcho(StateCookie),
    CookieAck,
    Shutdown,
    ShutdownAck,
    ShutdownComplete,
    CfgMismatch,
    Data {
        channel_id: u32,
        seg:        Segment,
    },
    DataAck {
        channel_id: u32,
        seq:        u32,
    },
}

/// Gram is the atomic unit of the miknet protocol. All transmissions are represented as a gram
/// before they are written on the network.
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub struct Gram {
    pub token:  u32,
    pub chunks: Vec<Chunk>,
}

#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum Timer {
    InitTimer,
    CookieSentTimer,
}

impl Timer {
    fn duration(&self) -> Duration {
        match *self {
            Timer::InitTimer => Duration::new(3, 0),
            Timer::CookieSentTimer => Duration::new(5, 0),
        }
    }
}

#[derive(Clone, Debug, PartialEq)]
pub enum ApiEvent {
    Tx(Vec<u8>),
    Disc,
    Conn,
    Shutdown,
}

#[derive(Debug, PartialEq)]
pub enum Event {
    Api(ApiEvent),
    Gram(Gram),
    Chunk(Chunk),
    Timer(Timer),
    InvalidGram,
}

impl From<Chunk> for Event {
    fn from(chunk: Chunk) -> Event { Event::Chunk(chunk) }
}

#[derive(Clone, Debug, Eq, PartialEq)]
pub enum ApiCmd {
    Disconnect,
    ConnectionAttemptTimedOut,
    ConnectionEstablished,
}

#[derive(Clone, Debug, Eq, PartialEq)]
pub enum Cmd {
    Chunk(Chunk),
    Net(Gram),
    Timer(Timer),
    Api(ApiCmd),
}

pub trait Protocol: Sized {
    fn establish() -> Self;
    fn step(self, event: Event) -> (Self, Vec<Cmd>);
}

/// Connection is a relationship between two miknet nodes. Handshake and teardown based roughly on
/// SCTP.
#[derive(Clone, Debug, PartialEq)]
pub enum Connection<P: Protocol + Debug> {
    Listen {
        key: Key,
    },
    InitSent {
        token: u32,
        tsn:   u32,
        queue: Vec<ApiEvent>,
    },
    InitAckSent {
        their_token: u32,
    },
    CookieEchoed {
        tcb:   Tcb,
        queue: Vec<ApiEvent>,
    },
    Established {
        tcb:      Tcb,
        protocol: P,
    },
    ShutdownSent {
        token:       u32,
        their_token: u32,
    },
    ShutdownAckSent {
        token:       u32,
        their_token: u32,
    },
    Shutdown {
        their_token: u32,
    },
    Failed,
}

impl<P: Protocol + Debug> Connection<P> {
    /// Returns a connection using the given key to sign and verify state cookies.
    pub fn new(key: Key) -> Self { Connection::Listen { key } }

    /// Processes an event and returns the next state of the connection and any commands that
    /// should be executed as part of the transition.
    pub fn step(self, event: Event) -> (Self, Vec<Cmd>) {
        let (next, cmds) = self.handle_event(event);
        let (chunks, mut cmds): (Vec<Chunk>, Vec<Cmd>) =
            cmds.into_iter().partition_map(|cmd| match cmd {
                Cmd::Chunk(chunk) => Either::Left(chunk),
                cmd => Either::Right(cmd),
            });
        if !chunks.is_empty() {
            cmds.extend(next.build_grams(chunks));
        }
        (next, cmds)
    }

    /// Returns whether this connection should be persisted between events. For example, we do not
    /// keep an InitAckSent state between events to prevent DOS attacks.
    pub fn should_persist(&self) -> bool {
        match *self {
            Connection::Listen { .. } => false,
            Connection::InitAckSent { .. } => false,
            Connection::Failed => false,
            _ => true,
        }
    }

    /// Prepares chunks as net commands to write out grams.
    fn build_grams(&self, chunks: Vec<Chunk>) -> Vec<Cmd> {
        let token = self.token();
        let grams = match *self {
            _ => vec![Gram { token, chunks }],
        };
        grams.into_iter().map(|gram| Cmd::Net(gram)).collect()
    }

    /// Returns the token this connection should embed in all its grams for association.
    fn token(&self) -> u32 {
        match *self {
            Connection::InitAckSent { their_token } => their_token,
            Connection::InitSent { token, .. } => token,
            Connection::CookieEchoed { ref tcb, .. } => tcb.their_token,
            Connection::Established { ref tcb, .. } => tcb.their_token,
            Connection::ShutdownSent { their_token, .. } => their_token,
            Connection::ShutdownAckSent { their_token, .. } => their_token,
            Connection::Shutdown { their_token } => their_token,
            _ => 0,
        }
    }

    /// Returns the token we expect in valid grams from our peer.
    fn expected_token(&self) -> Option<u32> {
        match *self {
            Connection::InitSent { token, .. } => Some(token),
            Connection::CookieEchoed { ref tcb, .. } => Some(tcb.our_token),
            Connection::Established { ref tcb, .. } => Some(tcb.our_token),
            Connection::ShutdownSent { token, .. } => Some(token),
            Connection::ShutdownAckSent { token, .. } => Some(token),
            _ => None,
        }
    }

    fn handle_events(self, events: Vec<Event>) -> (Self, Vec<Cmd>) {
        events.into_iter().fold(
            (self, Vec::new()),
            |(conn, mut cmds), event| {
                let (next_conn, more_cmds) = conn.handle_event(event);
                cmds.extend(more_cmds);
                (next_conn, cmds)
            },
        )
    }

    fn handle_event(self, event: Event) -> (Self, Vec<Cmd>) {
        match (self, event) {
            (conn, Event::Gram(gram)) => match conn.expected_token() {
                Some(expected_token) if gram.token != expected_token => {
                    conn.handle_event(Event::InvalidGram)
                }
                _ => conn.handle_events(
                    gram.chunks.into_iter().map(Chunk::into).collect(),
                ),
            },
            (
                Connection::Listen { key },
                Event::Chunk(Chunk::Init {
                    token: their_token,
                    tsn: their_tsn,
                }),
            ) => {
                let (our_tsn, our_token) = (random(), random());
                (
                    Connection::InitAckSent { their_token },
                    vec![Cmd::Chunk(Chunk::InitAck {
                        tsn:          our_tsn,
                        token:        our_token,
                        state_cookie: StateCookie::new(
                            Tcb {
                                our_tsn,
                                our_token,
                                their_token,
                                their_tsn,
                            },
                            &key,
                        ),
                    })],
                )
            }
            (Connection::Listen { .. }, Event::Api(ApiEvent::Conn)) => {
                let (token, tsn) = (random(), random());
                (
                    Connection::InitSent {
                        token,
                        tsn,
                        queue: Vec::new(),
                    },
                    vec![
                        Cmd::Chunk(Chunk::Init { token, tsn }),
                        Cmd::Timer(Timer::InitTimer),
                    ],
                )
            }
            (
                Connection::Listen { ref key, .. },
                Event::Chunk(Chunk::CookieEcho(ref state_cookie)),
            )
                if state_cookie.signed_by(&key) =>
            {
                (
                    Connection::Established {
                        tcb:      state_cookie.tcb.clone(),
                        protocol: P::establish(),
                    },
                    vec![
                        Cmd::Chunk(Chunk::CookieAck),
                        Cmd::Api(ApiCmd::ConnectionEstablished),
                    ],
                )
            }
            (Connection::InitSent { .. }, Event::Timer(Timer::InitTimer)) => (
                Connection::Failed,
                vec![Cmd::Api(ApiCmd::ConnectionAttemptTimedOut)],
            ),
            (
                Connection::InitSent {
                    token: our_token,
                    tsn: our_tsn,
                    queue,
                },
                Event::Chunk(Chunk::InitAck {
                    token: their_token,
                    tsn: their_tsn,
                    state_cookie,
                }),
            ) => (
                Connection::CookieEchoed {
                    tcb: Tcb {
                        our_tsn,
                        our_token,
                        their_tsn,
                        their_token,
                    },
                    queue,
                },
                vec![
                    Cmd::Chunk(Chunk::CookieEcho(state_cookie)),
                    Cmd::Timer(Timer::CookieSentTimer),
                ],
            ),
            (
                Connection::InitSent {
                    token,
                    tsn,
                    mut queue,
                },
                Event::Api(ae),
            ) => (
                Connection::InitSent {
                    token,
                    tsn,
                    queue: {
                        queue.push(ae);
                        queue
                    },
                },
                Vec::new(),
            ),
            (Connection::CookieEchoed { tcb, queue }, Event::Api(ae)) => (
                Connection::CookieEchoed {
                    tcb,
                    queue: queue
                        .into_iter()
                        .chain(vec![ae].into_iter())
                        .collect(),
                },
                Vec::new(),
            ),
            (
                Connection::CookieEchoed { tcb, queue },
                Event::Chunk(Chunk::CookieAck),
            ) => {
                let (conn, mut cmds) = queue.into_iter().fold(
                    (
                        Connection::Established {
                            tcb,
                            protocol: P::establish(),
                        },
                        Vec::new(),
                    ),
                    |(conn, mut cmds), ae| {
                        let (next_conn, more_cmds) =
                            conn.handle_event(Event::Api(ae));
                        cmds.extend(more_cmds);
                        (next_conn, cmds)
                    },
                );
                cmds.push(Cmd::Api(ApiCmd::ConnectionEstablished));
                (conn, cmds)
            }
            (
                Connection::Established { tcb, .. },
                Event::Api(ApiEvent::Disc),
            ) => (
                Connection::ShutdownSent {
                    token:       tcb.our_token,
                    their_token: tcb.their_token,
                },
                vec![Cmd::Chunk(Chunk::Shutdown)],
            ),
            (
                Connection::Established { tcb, .. },
                Event::Chunk(Chunk::Shutdown),
            ) => (
                Connection::ShutdownAckSent {
                    token:       tcb.our_token,
                    their_token: tcb.their_token,
                },
                vec![Cmd::Chunk(Chunk::ShutdownAck)],
            ),
            (
                Connection::ShutdownSent { token, their_token },
                Event::Chunk(Chunk::Shutdown),
            ) => (
                Connection::ShutdownAckSent { token, their_token },
                vec![Cmd::Chunk(Chunk::ShutdownAck)],
            ),
            (
                Connection::ShutdownSent { their_token, .. },
                Event::Chunk(Chunk::ShutdownAck),
            ) => (
                Connection::Shutdown { their_token },
                vec![
                    Cmd::Api(ApiCmd::Disconnect),
                    Cmd::Chunk(Chunk::ShutdownComplete),
                ],
            ),
            (
                Connection::ShutdownAckSent { their_token, .. },
                Event::Chunk(Chunk::ShutdownComplete),
            ) => (
                Connection::Shutdown { their_token },
                vec![Cmd::Api(ApiCmd::Disconnect)],
            ),
            (
                Connection::ShutdownAckSent { their_token, .. },
                Event::Chunk(Chunk::ShutdownAck),
            ) => (
                Connection::Shutdown { their_token },
                vec![
                    Cmd::Api(ApiCmd::Disconnect),
                    Cmd::Chunk(Chunk::ShutdownComplete),
                ],
            ),
            (conn, _) => (conn, Vec::new()),
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use crate::random;
    use failure::Error;
    use std::str::FromStr;

    #[derive(Debug)]
    struct TumblerProtocol;

    impl Protocol for TumblerProtocol {
        fn establish() -> Self { Self {} }

        fn step(self, event: Event) -> (Self, Vec<Cmd>) { (self, Vec::new()) }
    }

    #[derive(Debug)]
    struct Tumbler {
        key1:    Key,
        key2:    Key,
        c1:      Connection<TumblerProtocol>,
        c2:      Connection<TumblerProtocol>,
        c1_cmds: Vec<Cmd>,
    }

    impl Tumbler {
        fn new() -> Result<Self, Error> {
            let key1 = Key::new()?;
            let key2 = Key::new()?;
            Ok(Self {
                c1: Connection::new(key1.clone()),
                c2: Connection::new(key2.clone()),
                key1,
                key2,
                c1_cmds: Vec::new(),
            })
        }

        fn start(self) -> Self {
            let (c1, c1_cmds) = self.c1.step(Event::Api(ApiEvent::Conn));
            Self {
                c1,
                c1_cmds,
                ..self
            }
        }

        fn established(&self) -> bool {
            match (&self.c1, &self.c2) {
                (
                    Connection::Established { .. },
                    Connection::Established { .. },
                ) => true,
                _ => false,
            }
        }

        fn tumble(self) -> Self {
            let (c2, c2_cmds) =
                Tumbler::tumble_connection(self.c2, self.c1_cmds);
            println!("c2 cmds: {:?}", c2_cmds);
            let (c1, c1_cmds) = Tumbler::tumble_connection(self.c1, c2_cmds);
            Self {
                c1: if c1.should_persist() {
                    c1
                } else {
                    Connection::new(self.key1.clone())
                },
                c2: if c2.should_persist() {
                    c2
                } else {
                    Connection::new(self.key2.clone())
                },
                c1_cmds,
                ..self
            }
        }

        fn tumble_connection(
            mut c: Connection<TumblerProtocol>,
            cmds: Vec<Cmd>,
        ) -> (Connection<TumblerProtocol>, Vec<Cmd>) {
            let mut out_cmds = Vec::new();
            for event in Tumbler::convert_cmds(cmds) {
                let (next_c, mut next_cmds) = c.step(event);
                c = next_c;
                out_cmds.append(&mut next_cmds);
            }
            (c, out_cmds)
        }

        fn convert_cmds(cmds: Vec<Cmd>) -> Vec<Event> {
            cmds.into_iter()
                .filter_map(|cmd| match cmd {
                    Cmd::Net(gram) => Some(Event::Gram(gram)),
                    _ => None,
                })
                .collect()
        }
    }

    #[test]
    fn handshake() -> Result<(), Error> {
        let mut tumbler = Tumbler::new()?;
        tumbler = tumbler.start();
        for i in 0..2 {
            tumbler = tumbler.tumble();
        }
        if !tumbler.established() {
            println!("Connection not established; states:");
            println!("{:?}", tumbler);
            panic!();
        }
        Ok(())
    }
}
<|endoftext|>


<commit_before>use std::collections::BTreeMap;
use std::io::{Cursor, Read, Seek, SeekFrom};
use cbor::cbor::{CborError, CborType};

/// Struct holding a cursor and additional information for decoding.
#[derive(Debug)]
struct DecoderCursor {
    pub cursor: Cursor<Vec<u8>>,
}

impl DecoderCursor {
    /// Convert num bytes to a u64
    fn read_int_from_bytes(&mut self, num: usize) -> Result<u64, CborError> {
        let mut x: Vec<u8> = vec![0; num];
        match self.cursor.read_exact(&mut x) {
            Err(_) => return Err(CborError::TruncatedInput),
            Ok(()) => {}
        };
        let mut result: u64 = 0;
        for i in (0..num).rev() {
            result += (x[num - 1 - i] as u64) << (i * 8);
        }
        Ok(result)
    }

    /// Read an integer and return it as u64.
    fn read_int(&mut self) -> Result<u64, CborError> {
        let first_value = self.peek_byte()? & 0x1F;
        match self.cursor.seek(SeekFrom::Current(1)) {
            Err(_) => return Err(CborError::LibraryError),
            Ok(_) => {}
        };
        let val: u64 = match first_value {
            0...23 => first_value as u64,
            24 => self.read_int_from_bytes(1)?,
            25 => self.read_int_from_bytes(2)?,
            26 => self.read_int_from_bytes(4)?,
            27 => self.read_int_from_bytes(8)?,
            _ => return Err(CborError::MalformedInput),
        };
        Ok(val)
    }

    fn read_signed_int(&mut self) -> Result<CborType, CborError> {
        let uint = self.read_int()?;
        if uint > i64::max_value() as u64 {
            return Err(CborError::InputValueOutOfRange);
        }
        let result: i64 = -1 - uint as i64;
        Ok(CborType::SignedInteger(result))
    }

    /// Read an array of data items and return it.
    fn read_array(&mut self) -> Result<CborType, CborError> {
        // Create a new array.
        let mut array: Vec<CborType> = Vec::new();
        // Read the length of the array.
        let num_items = self.read_int()?;
        // Decode each of the num_items data items.
        for _ in 0..num_items {
            let new_item = self.decode_item()?;
            array.push(new_item);
        }
        Ok(CborType::Array(array))
    }

    /// Read a byte string and return it.
    fn read_byte_string(&mut self) -> Result<CborType, CborError> {
        let length = self.read_int()?;
        if length > usize::max_value() as u64 {
            return Err(CborError::InputTooLarge);
        }
        let length = length as usize;
        let mut byte_string: Vec<u8> = vec![0; length];
        match self.cursor.read_exact(&mut byte_string) {
            Err(_) => return Err(CborError::TruncatedInput),
            Ok(()) => {}
        };
        Ok(CborType::Bytes(byte_string))
    }

    /// Read a map.
    fn read_map(&mut self) -> Result<CborType, CborError> {
        let num_items = self.read_int()?;
        // Create a new array.
        let mut map: BTreeMap<CborType, CborType> = BTreeMap::new();
        // Decode each of the num_items (key, data item) pairs.
        for _ in 0..num_items {
            let key_val = self.decode_item()?;
            let item_value = self.decode_item()?;
            if map.contains_key(&key_val) {
                return Err(CborError::DuplicateMapKey);
            }
            map.insert(key_val, item_value);
        }
        Ok(CborType::Map(map))
    }

    /// Peeks at the next byte in the cursor, but does not change the position.
    fn peek_byte(&mut self) -> Result<u8, CborError> {
        let mut x: Vec<u8> = vec![0; 1];
        match self.cursor.read_exact(&mut x) {
            Err(_) => return Err(CborError::TruncatedInput),
            Ok(()) => {}
        };
        match self.cursor.seek(SeekFrom::Current(-1)) {
            Err(_) => return Err(CborError::LibraryError),
            Ok(_) => {}
        };
        Ok(x[0])
    }

    /// Decodes the next CBOR item.
    pub fn decode_item(&mut self) -> Result<CborType, CborError> {
        let major_type = self.peek_byte()? >> 5;
        let result = match major_type {
            0 => {
                let value = self.read_int()?;
                CborType::Integer(value)
            }
            1 => self.read_signed_int()?,
            2 => self.read_byte_string()?,
            4 => self.read_array()?,
            5 => self.read_map()?,
            6 => {
                let tag = self.read_int()?;
                let item = self.decode_item()?;
                CborType::Tag(tag, Box::new(item))
            }
            _ => return Err(CborError::MalformedInput),
        };
        Ok(result)
    }
}

/// Read the CBOR structure in bytes and return it as a CBOR object.
pub fn decode(bytes: Vec<u8>) -> Result<CborType, CborError> {
    let mut decoder_cursor = DecoderCursor { cursor: Cursor::new(bytes) };
    decoder_cursor.decode_item()
    // TODO: check cursor at end?
}
<commit_msg>reduce direct calls to the decoder's cursor<commit_after>use std::collections::BTreeMap;
use std::io::{Cursor, Read, Seek, SeekFrom};
use cbor::cbor::{CborError, CborType};

/// Struct holding a cursor and additional information for decoding.
#[derive(Debug)]
struct DecoderCursor {
    pub cursor: Cursor<Vec<u8>>,
}

impl DecoderCursor {
    /// Convert num bytes to a u64
    fn read_int_from_bytes(&mut self, num: usize) -> Result<u64, CborError> {
        let mut x: Vec<u8> = vec![0; num];
        match self.cursor.read_exact(&mut x) {
            Err(_) => return Err(CborError::TruncatedInput),
            Ok(()) => {}
        };
        let mut result: u64 = 0;
        for i in (0..num).rev() {
            result += (x[num - 1 - i] as u64) << (i * 8);
        }
        Ok(result)
    }

    /// Read an integer and return it as u64.
    fn read_int(&mut self) -> Result<u64, CborError> {
        let first_value = self.read_int_from_bytes(1)? & 0x1F;
        let val = match first_value {
            0...23 => first_value,
            24 => self.read_int_from_bytes(1)?,
            25 => self.read_int_from_bytes(2)?,
            26 => self.read_int_from_bytes(4)?,
            27 => self.read_int_from_bytes(8)?,
            _ => return Err(CborError::MalformedInput),
        };
        Ok(val)
    }

    fn read_signed_int(&mut self) -> Result<CborType, CborError> {
        let uint = self.read_int()?;
        if uint > i64::max_value() as u64 {
            return Err(CborError::InputValueOutOfRange);
        }
        let result: i64 = -1 - uint as i64;
        Ok(CborType::SignedInteger(result))
    }

    /// Read an array of data items and return it.
    fn read_array(&mut self) -> Result<CborType, CborError> {
        // Create a new array.
        let mut array: Vec<CborType> = Vec::new();
        // Read the length of the array.
        let num_items = self.read_int()?;
        // Decode each of the num_items data items.
        for _ in 0..num_items {
            let new_item = self.decode_item()?;
            array.push(new_item);
        }
        Ok(CborType::Array(array))
    }

    /// Read a byte string and return it.
    fn read_byte_string(&mut self) -> Result<CborType, CborError> {
        let length = self.read_int()?;
        if length > usize::max_value() as u64 {
            return Err(CborError::InputTooLarge);
        }
        let length = length as usize;
        let mut byte_string: Vec<u8> = vec![0; length];
        match self.cursor.read_exact(&mut byte_string) {
            Err(_) => return Err(CborError::TruncatedInput),
            Ok(()) => {}
        };
        Ok(CborType::Bytes(byte_string))
    }

    /// Read a map.
    fn read_map(&mut self) -> Result<CborType, CborError> {
        let num_items = self.read_int()?;
        // Create a new array.
        let mut map: BTreeMap<CborType, CborType> = BTreeMap::new();
        // Decode each of the num_items (key, data item) pairs.
        for _ in 0..num_items {
            let key_val = self.decode_item()?;
            let item_value = self.decode_item()?;
            if map.contains_key(&key_val) {
                return Err(CborError::DuplicateMapKey);
            }
            map.insert(key_val, item_value);
        }
        Ok(CborType::Map(map))
    }

    /// Peeks at the next byte in the cursor, but does not change the position.
    fn peek_byte(&mut self) -> Result<u8, CborError> {
        let mut x: Vec<u8> = vec![0; 1];
        match self.cursor.read_exact(&mut x) {
            Err(_) => return Err(CborError::TruncatedInput),
            Ok(()) => {}
        };
        match self.cursor.seek(SeekFrom::Current(-1)) {
            Err(_) => return Err(CborError::LibraryError),
            Ok(_) => {}
        };
        Ok(x[0])
    }

    /// Decodes the next CBOR item.
    pub fn decode_item(&mut self) -> Result<CborType, CborError> {
        let major_type = self.peek_byte()? >> 5;
        let result = match major_type {
            0 => {
                let value = self.read_int()?;
                CborType::Integer(value)
            }
            1 => self.read_signed_int()?,
            2 => self.read_byte_string()?,
            4 => self.read_array()?,
            5 => self.read_map()?,
            6 => {
                let tag = self.read_int()?;
                let item = self.decode_item()?;
                CborType::Tag(tag, Box::new(item))
            }
            _ => return Err(CborError::MalformedInput),
        };
        Ok(result)
    }
}

/// Read the CBOR structure in bytes and return it as a CBOR object.
pub fn decode(bytes: Vec<u8>) -> Result<CborType, CborError> {
    let mut decoder_cursor = DecoderCursor { cursor: Cursor::new(bytes) };
    decoder_cursor.decode_item()
    // TODO: check cursor at end?
}
<|endoftext|>


<commit_before>use ast::Expr;
use cpu;
use sym::BuiltinType;

// first param offset to rbp is +16,
// rbp+0 -> saved rbp
// rbp+8 -> return address
pub static PARAM_OFFSET: i32 = 16;

// on x64 each parameter needs exactly 8 bytes
pub fn next_param_offset(param_offset: i32, _: BuiltinType) -> i32 {
    param_offset + 8
}

// needed memory on stack for function call in caller
pub fn reserve_stack_for_call(args: &[Box<Expr>]) -> u32 {
    // some function parameters are stored on the stack,
    // therefore we need to increase `tempsize` in this case.
    let params_on_stack = args.len() as i32 - cpu::REG_PARAMS.len() as i32;

    if params_on_stack > 0 {
        (params_on_stack as u32) * 8
    } else {
        0
    }
}
<commit_msg>cpu/x64: comment was wrong<commit_after>use ast::Expr;
use cpu;
use sym::BuiltinType;

// first param offset to rbp is +16,
// rbp+0 -> saved rbp
// rbp+8 -> return address
pub static PARAM_OFFSET: i32 = 16;

// on x64 each parameter needs exactly 8 bytes
pub fn next_param_offset(param_offset: i32, _: BuiltinType) -> i32 {
    param_offset + 8
}

// needed memory on stack for function call in caller
pub fn reserve_stack_for_call(args: &[Box<Expr>]) -> u32 {
    let params_on_stack = args.len() as i32 - cpu::REG_PARAMS.len() as i32;

    if params_on_stack > 0 {
        (params_on_stack as u32) * 8
    } else {
        0
    }
}
<|endoftext|>


<commit_before>#[feature(globs)];

extern mod native;
extern mod extra;
extern mod glfw;
extern mod gl;
extern mod cgmath;
extern mod noise;

use std::cast;
use std::ptr;
use std::str;
use std::vec;
use std::libc;
use std::io::Timer;

use gl::types::*;

use cgmath::matrix::Matrix;
use cgmath::matrix::Mat3;
use cgmath::matrix::Mat4;
use cgmath::matrix::ToMat4;
use cgmath::vector::Vector;
use cgmath::vector::Vec3;
use cgmath::vector::Vec4;
use cgmath::angle::rad;

#[link(name="GLU")]
extern {}

mod chunk;

static vertex_shader_src : &'static str = r"
#version 110

uniform mat4 modelview;
uniform mat4 projection;

attribute vec3 position;
attribute vec3 normal;

varying vec4 frag_color;

const vec4 obj_diffuse = vec4(0.2, 0.6, 0.2, 1.0);
const vec3 light_direction = vec3(0.408248, -0.816497, 0.408248);
const vec4 light_diffuse = vec4(0.8, 0.8, 0.8, 0.0);
const vec4 light_ambient = vec4(0.2, 0.2, 0.2, 1.0);

const float planet_radius = 6371000.0 / 10000.0;

void main() {
    vec4 eye_position = modelview * vec4(position, 1.0);

    /* Curvature of the planet */
    float distance_squared = pow(eye_position.x, 2) + pow(eye_position.z, 2);
    eye_position.y -= planet_radius - sqrt(pow(planet_radius, 2) - distance_squared);

    gl_Position = projection * eye_position;

    vec4 diffuse_factor
        = max(-dot(normal, light_direction), 0.0) * light_diffuse;
    vec4 ambient_diffuse_factor = diffuse_factor + light_ambient;

    frag_color = ambient_diffuse_factor * obj_diffuse;
}
";

static fragment_shader_src : &'static str = r"
#version 110

varying vec4 frag_color;

void main() {
    gl_FragColor = frag_color;
}
";

pub static WORLD_SIZE: uint = 4;
pub static CHUNK_SIZE: uint = 64;
pub static WORLD_SEED: u32 = 42;

static FRAME_TIME_TARGET_MS : u64 = 16;

struct GraphicsResources {
    program: GLuint,
    uniform_modelview: GLint,
    uniform_projection: GLint,
}

#[start]
fn start(argc: int, argv: **u8) -> int {
    do native::start(argc, argv) {
        main();
    }
}

fn main() {
   glfw::set_error_callback(~ErrorContext);

    do glfw::start {
        let mut window_width = 800;
        let mut window_height = 600;

        let window = glfw::Window::create(window_width, window_height, "Hello, I am a window.", glfw::Windowed)
            .expect("Failed to create GLFW window.");

        window.set_cursor_mode(glfw::CursorDisabled);
        window.make_context_current();

        gl::load_with(glfw::get_proc_address);

        gl::Enable(gl::DEPTH_TEST);
        gl::Enable(gl::CULL_FACE);

        glfw::set_swap_interval(1);

        let graphics_resources = load_graphics_resources();

        gl::UseProgram(graphics_resources.program);

        let mut chunk_loader = chunk::ChunkLoader::new(WORLD_SEED, &graphics_resources);

        window.set_key_callback(~KeyContext);

        let (fb_size_port, fb_size_chan): (Port<(u32,u32)>, Chan<(u32,u32)>) = std::comm::Chan::new();
        window.set_framebuffer_size_callback(~FramebufferSizeContext { chan: fb_size_chan });

        let start_time = extra::time::precise_time_ns();
        let mut last_frame_time = start_time;
        let mut num_frames = 0;

        let mut camera_position = Vec3::<f32>::new(0.0f32, 20.0f32, 40.0f32);

        //let mut timer = Timer::new().unwrap();

        while !window.should_close() {
            let frame_start_time = extra::time::precise_time_ns();

            glfw::poll_events();

            loop {
                match fb_size_port.try_recv() {
                    Some((w,h)) => {
                        window_width = w;
                        window_height = h;
                    }
                    None => break
                }
            }

            let (cursor_x, cursor_y) = window.get_cursor_pos();
            let camera_angle_y = ((cursor_x * 0.0005) % 1.0) * std::f64::consts::PI * 2.0;
            let camera_angle_x = ((cursor_y * 0.0005) % 1.0) * std::f64::consts::PI * 2.0;

            let mut camera_velocity = Vec3::<f32>::new(0.0f32, 0.0f32, 0.0f32);

            match window.get_key(glfw::KeySpace) {
                glfw::Press => { camera_velocity.y += 1.0f32 }
                _ => {}
            }

            match window.get_key(glfw::KeyLeftControl) {
                glfw::Press => { camera_velocity.y += -1.0f32 }
                _ => {}
            }

            match window.get_key(glfw::KeyS) {
                glfw::Press => { camera_velocity.z += 1.0f32 }
                _ => {}
            }

            match window.get_key(glfw::KeyW) {
                glfw::Press => { camera_velocity.z += -1.0f32 }
                _ => {}
            }

            match window.get_key(glfw::KeyD) {
                glfw::Press => { camera_velocity.x += 1.0f32 }
                _ => {}
            }

            match window.get_key(glfw::KeyA) {
                glfw::Press => { camera_velocity.x += -1.0f32 }
                _ => {}
            }

            gl::Viewport(0,0, window_width as GLint, window_height as GLint);

            gl::ClearColor(0.0, 0.0, 0.0, 1.0);
            gl::Clear(gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT);

            let projection = cgmath::projection::perspective(
                rad(1.57f32),
                window_width as f32 / window_height as f32,
                0.1f32, 1000.0f32);

            unsafe {
                gl::UniformMatrix4fv(graphics_resources.uniform_projection, 1, gl::FALSE, cast::transmute(&projection));
            }

            let camera_translation = Mat4::<f32>::from_cols(
                Vec4::<f32>::unit_x(),
                Vec4::<f32>::unit_y(),
                Vec4::<f32>::unit_z(),
                camera_position.mul_s(-1.0f32).extend(1.0f32));
            let camera_rotation_x = Mat3::<f32>::from_angle_x(rad(camera_angle_x as f32)).to_mat4();
            let camera_rotation_y = Mat3::<f32>::from_angle_y(rad(camera_angle_y as f32)).to_mat4();
            let camera = camera_rotation_x.mul_m(&camera_rotation_y).mul_m(&camera_translation);

            let inv_camera_rotation = Mat3::<f32>::from_euler(rad(-camera_angle_x as f32), rad(-camera_angle_y as f32), rad(0.0f32));
            let absolute_camera_velocity = inv_camera_rotation.mul_v(&camera_velocity).mul_s(0.5f32);
            camera_position.add_self_v(&absolute_camera_velocity);

            for (_, chunk) in chunk_loader.cache.iter() {
                let chunk_translation = Mat4::<f32>::from_cols(
                    Vec4::<f32>::unit_x(),
                    Vec4::<f32>::unit_y(),
                    Vec4::<f32>::unit_z(),
                    Vec4::<f32>::new(chunk.x as f32, 0.0f32, chunk.z as f32, 1.0f32));

                let modelview = camera.mul_m(&chunk_translation);

                gl::BindVertexArray(chunk.vao);

                unsafe {
                    gl::UniformMatrix4fv(graphics_resources.uniform_modelview, 1, gl::FALSE, cast::transmute(&modelview));
                    gl::DrawElements(gl::TRIANGLES, chunk.num_elements as i32, gl::UNSIGNED_INT, ptr::null());
                }

                gl::BindVertexArray(0);
            }

            window.swap_buffers();

            check_gl("main loop");

            chunk_loader.update(camera_position.x as i64, camera_position.z as i64);

            let cur_time = extra::time::precise_time_ns();
            num_frames += 1;
            if (cur_time - last_frame_time) > (1000*1000*1000) {
                println!("{} frames per second", num_frames);
                num_frames = 0;
                last_frame_time = cur_time;
            }

            let frame_end_time = extra::time::precise_time_ns();
            let frame_time_ms = (frame_end_time - frame_start_time)/(1000*1000);
            if (frame_time_ms < FRAME_TIME_TARGET_MS) {
                //timer.sleep(FRAME_TIME_TARGET_MS - frame_time_ms);
            }
        }
    }
}

fn load_graphics_resources() -> GraphicsResources {
    let vs = compile_shader(vertex_shader_src.as_bytes(), gl::VERTEX_SHADER);
    let fs = compile_shader(fragment_shader_src.as_bytes(), gl::FRAGMENT_SHADER);
    let program = link_program(vs, fs);

    let uniform_modelview = unsafe { "modelview".with_c_str(|ptr| gl::GetUniformLocation(program, ptr)) };
    let uniform_projection = unsafe { "projection".with_c_str(|ptr| gl::GetUniformLocation(program, ptr)) };

    return GraphicsResources {
        program: program,
        uniform_modelview: uniform_modelview,
        uniform_projection: uniform_projection,
    };

}

extern "C" {
    fn gluErrorString(error: GLenum) -> *GLubyte;
}

fn check_gl(message : &str) {
    let err = gl::GetError();
    if (err != gl::NO_ERROR) {
        unsafe {
            let err = std::str::raw::from_c_str(gluErrorString(err) as *i8);
            fail!("GL error {} at {}", err, message);
        }
    }
}

fn compile_shader(src: &[u8], ty: GLenum) -> GLuint {
    let shader = gl::CreateShader(ty);
    unsafe {
        // Attempt to compile the shader
        //transmute is used here because `as` causes ICE
        //wait a sec, is `src` null-terminated properly?
        gl::ShaderSource(shader, 1, std::cast::transmute(std::ptr::to_unsafe_ptr(&src.as_ptr())), ptr::null());
        gl::CompileShader(shader);

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

        // Fail on error
        if status != (gl::TRUE as GLint) {
            let mut len = 0;
            gl::GetShaderiv(shader, gl::INFO_LOG_LENGTH, &mut len);
            let mut buf = vec::from_elem(len as uint - 1, 0u8);     // subtract 1 to skip the trailing null character
            gl::GetShaderInfoLog(shader, len, ptr::mut_null(), buf.as_mut_ptr() as *mut GLchar);
            fail!(str::raw::from_utf8(buf).to_owned());
        }
    }
    shader
}

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

        // Fail on error
        if status != (gl::TRUE as GLint) {
            let mut len: GLint = 0;
            gl::GetProgramiv(program, gl::INFO_LOG_LENGTH, &mut len);
            let mut buf = vec::from_elem(len as uint - 1, 0u8);     // subtract 1 to skip the trailing null character
            gl::GetProgramInfoLog(program, len, ptr::mut_null(), buf.as_mut_ptr() as *mut GLchar);
            fail!(str::raw::from_utf8(buf).to_owned());
        }
    }
    program
}

struct ErrorContext;
impl glfw::ErrorCallback for ErrorContext {
    fn call(&self, _: glfw::Error, description: ~str) {
        fail!("GLFW Error: {:s}", description);
    }
}

struct KeyContext;
impl glfw::KeyCallback for KeyContext {
    fn call(&self, window: &glfw::Window, key: glfw::Key, _: libc::c_int, action: glfw::Action, _: glfw::Modifiers) {
        match (action, key) {
            (glfw::Press, glfw::KeyEscape) => window.set_should_close(true),
            _ => {}
        }
    }
}

struct FramebufferSizeContext {
    chan: Chan<(u32,u32)>
}
impl glfw::FramebufferSizeCallback for FramebufferSizeContext {
    fn call(&self, _: &glfw::Window, width: i32, height: i32) {
        self.chan.send((width as u32,height as u32));
    }
}
<commit_msg>change sky color<commit_after>#[feature(globs)];

extern mod native;
extern mod extra;
extern mod glfw;
extern mod gl;
extern mod cgmath;
extern mod noise;

use std::cast;
use std::ptr;
use std::str;
use std::vec;
use std::libc;
use std::io::Timer;

use gl::types::*;

use cgmath::matrix::Matrix;
use cgmath::matrix::Mat3;
use cgmath::matrix::Mat4;
use cgmath::matrix::ToMat4;
use cgmath::vector::Vector;
use cgmath::vector::Vec3;
use cgmath::vector::Vec4;
use cgmath::angle::rad;

#[link(name="GLU")]
extern {}

mod chunk;

static vertex_shader_src : &'static str = r"
#version 110

uniform mat4 modelview;
uniform mat4 projection;

attribute vec3 position;
attribute vec3 normal;

varying vec4 frag_color;

const vec4 obj_diffuse = vec4(0.2, 0.6, 0.2, 1.0);
const vec3 light_direction = vec3(0.408248, -0.816497, 0.408248);
const vec4 light_diffuse = vec4(0.8, 0.8, 0.8, 0.0);
const vec4 light_ambient = vec4(0.2, 0.2, 0.2, 1.0);

const float planet_radius = 6371000.0 / 10000.0;

void main() {
    vec4 eye_position = modelview * vec4(position, 1.0);

    /* Curvature of the planet */
    float distance_squared = pow(eye_position.x, 2) + pow(eye_position.z, 2);
    eye_position.y -= planet_radius - sqrt(pow(planet_radius, 2) - distance_squared);

    gl_Position = projection * eye_position;

    vec4 diffuse_factor
        = max(-dot(normal, light_direction), 0.0) * light_diffuse;
    vec4 ambient_diffuse_factor = diffuse_factor + light_ambient;

    frag_color = ambient_diffuse_factor * obj_diffuse;
}
";

static fragment_shader_src : &'static str = r"
#version 110

varying vec4 frag_color;

void main() {
    gl_FragColor = frag_color;
}
";

pub static WORLD_SIZE: uint = 4;
pub static CHUNK_SIZE: uint = 64;
pub static WORLD_SEED: u32 = 42;

static FRAME_TIME_TARGET_MS : u64 = 16;

struct GraphicsResources {
    program: GLuint,
    uniform_modelview: GLint,
    uniform_projection: GLint,
}

#[start]
fn start(argc: int, argv: **u8) -> int {
    do native::start(argc, argv) {
        main();
    }
}

fn main() {
   glfw::set_error_callback(~ErrorContext);

    do glfw::start {
        let mut window_width = 800;
        let mut window_height = 600;

        let window = glfw::Window::create(window_width, window_height, "Hello, I am a window.", glfw::Windowed)
            .expect("Failed to create GLFW window.");

        window.set_cursor_mode(glfw::CursorDisabled);
        window.make_context_current();

        gl::load_with(glfw::get_proc_address);

        gl::Enable(gl::DEPTH_TEST);
        gl::Enable(gl::CULL_FACE);

        glfw::set_swap_interval(1);

        let graphics_resources = load_graphics_resources();

        gl::UseProgram(graphics_resources.program);

        let mut chunk_loader = chunk::ChunkLoader::new(WORLD_SEED, &graphics_resources);

        window.set_key_callback(~KeyContext);

        let (fb_size_port, fb_size_chan): (Port<(u32,u32)>, Chan<(u32,u32)>) = std::comm::Chan::new();
        window.set_framebuffer_size_callback(~FramebufferSizeContext { chan: fb_size_chan });

        let start_time = extra::time::precise_time_ns();
        let mut last_frame_time = start_time;
        let mut num_frames = 0;

        let mut camera_position = Vec3::<f32>::new(0.0f32, 20.0f32, 40.0f32);

        //let mut timer = Timer::new().unwrap();

        while !window.should_close() {
            let frame_start_time = extra::time::precise_time_ns();

            glfw::poll_events();

            loop {
                match fb_size_port.try_recv() {
                    Some((w,h)) => {
                        window_width = w;
                        window_height = h;
                    }
                    None => break
                }
            }

            let (cursor_x, cursor_y) = window.get_cursor_pos();
            let camera_angle_y = ((cursor_x * 0.0005) % 1.0) * std::f64::consts::PI * 2.0;
            let camera_angle_x = ((cursor_y * 0.0005) % 1.0) * std::f64::consts::PI * 2.0;

            let mut camera_velocity = Vec3::<f32>::new(0.0f32, 0.0f32, 0.0f32);

            match window.get_key(glfw::KeySpace) {
                glfw::Press => { camera_velocity.y += 1.0f32 }
                _ => {}
            }

            match window.get_key(glfw::KeyLeftControl) {
                glfw::Press => { camera_velocity.y += -1.0f32 }
                _ => {}
            }

            match window.get_key(glfw::KeyS) {
                glfw::Press => { camera_velocity.z += 1.0f32 }
                _ => {}
            }

            match window.get_key(glfw::KeyW) {
                glfw::Press => { camera_velocity.z += -1.0f32 }
                _ => {}
            }

            match window.get_key(glfw::KeyD) {
                glfw::Press => { camera_velocity.x += 1.0f32 }
                _ => {}
            }

            match window.get_key(glfw::KeyA) {
                glfw::Press => { camera_velocity.x += -1.0f32 }
                _ => {}
            }

            gl::Viewport(0,0, window_width as GLint, window_height as GLint);

            gl::ClearColor(0.0, 0.75, 1.0, 1.0);
            gl::Clear(gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT);

            let projection = cgmath::projection::perspective(
                rad(1.57f32),
                window_width as f32 / window_height as f32,
                0.1f32, 1000.0f32);

            unsafe {
                gl::UniformMatrix4fv(graphics_resources.uniform_projection, 1, gl::FALSE, cast::transmute(&projection));
            }

            let camera_translation = Mat4::<f32>::from_cols(
                Vec4::<f32>::unit_x(),
                Vec4::<f32>::unit_y(),
                Vec4::<f32>::unit_z(),
                camera_position.mul_s(-1.0f32).extend(1.0f32));
            let camera_rotation_x = Mat3::<f32>::from_angle_x(rad(camera_angle_x as f32)).to_mat4();
            let camera_rotation_y = Mat3::<f32>::from_angle_y(rad(camera_angle_y as f32)).to_mat4();
            let camera = camera_rotation_x.mul_m(&camera_rotation_y).mul_m(&camera_translation);

            let inv_camera_rotation = Mat3::<f32>::from_euler(rad(-camera_angle_x as f32), rad(-camera_angle_y as f32), rad(0.0f32));
            let absolute_camera_velocity = inv_camera_rotation.mul_v(&camera_velocity).mul_s(0.5f32);
            camera_position.add_self_v(&absolute_camera_velocity);

            for (_, chunk) in chunk_loader.cache.iter() {
                let chunk_translation = Mat4::<f32>::from_cols(
                    Vec4::<f32>::unit_x(),
                    Vec4::<f32>::unit_y(),
                    Vec4::<f32>::unit_z(),
                    Vec4::<f32>::new(chunk.x as f32, 0.0f32, chunk.z as f32, 1.0f32));

                let modelview = camera.mul_m(&chunk_translation);

                gl::BindVertexArray(chunk.vao);

                unsafe {
                    gl::UniformMatrix4fv(graphics_resources.uniform_modelview, 1, gl::FALSE, cast::transmute(&modelview));
                    gl::DrawElements(gl::TRIANGLES, chunk.num_elements as i32, gl::UNSIGNED_INT, ptr::null());
                }

                gl::BindVertexArray(0);
            }

            window.swap_buffers();

            check_gl("main loop");

            chunk_loader.update(camera_position.x as i64, camera_position.z as i64);

            let cur_time = extra::time::precise_time_ns();
            num_frames += 1;
            if (cur_time - last_frame_time) > (1000*1000*1000) {
                println!("{} frames per second", num_frames);
                num_frames = 0;
                last_frame_time = cur_time;
            }

            let frame_end_time = extra::time::precise_time_ns();
            let frame_time_ms = (frame_end_time - frame_start_time)/(1000*1000);
            if (frame_time_ms < FRAME_TIME_TARGET_MS) {
                //timer.sleep(FRAME_TIME_TARGET_MS - frame_time_ms);
            }
        }
    }
}

fn load_graphics_resources() -> GraphicsResources {
    let vs = compile_shader(vertex_shader_src.as_bytes(), gl::VERTEX_SHADER);
    let fs = compile_shader(fragment_shader_src.as_bytes(), gl::FRAGMENT_SHADER);
    let program = link_program(vs, fs);

    let uniform_modelview = unsafe { "modelview".with_c_str(|ptr| gl::GetUniformLocation(program, ptr)) };
    let uniform_projection = unsafe { "projection".with_c_str(|ptr| gl::GetUniformLocation(program, ptr)) };

    return GraphicsResources {
        program: program,
        uniform_modelview: uniform_modelview,
        uniform_projection: uniform_projection,
    };

}

extern "C" {
    fn gluErrorString(error: GLenum) -> *GLubyte;
}

fn check_gl(message : &str) {
    let err = gl::GetError();
    if (err != gl::NO_ERROR) {
        unsafe {
            let err = std::str::raw::from_c_str(gluErrorString(err) as *i8);
            fail!("GL error {} at {}", err, message);
        }
    }
}

fn compile_shader(src: &[u8], ty: GLenum) -> GLuint {
    let shader = gl::CreateShader(ty);
    unsafe {
        // Attempt to compile the shader
        //transmute is used here because `as` causes ICE
        //wait a sec, is `src` null-terminated properly?
        gl::ShaderSource(shader, 1, std::cast::transmute(std::ptr::to_unsafe_ptr(&src.as_ptr())), ptr::null());
        gl::CompileShader(shader);

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

        // Fail on error
        if status != (gl::TRUE as GLint) {
            let mut len = 0;
            gl::GetShaderiv(shader, gl::INFO_LOG_LENGTH, &mut len);
            let mut buf = vec::from_elem(len as uint - 1, 0u8);     // subtract 1 to skip the trailing null character
            gl::GetShaderInfoLog(shader, len, ptr::mut_null(), buf.as_mut_ptr() as *mut GLchar);
            fail!(str::raw::from_utf8(buf).to_owned());
        }
    }
    shader
}

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

        // Fail on error
        if status != (gl::TRUE as GLint) {
            let mut len: GLint = 0;
            gl::GetProgramiv(program, gl::INFO_LOG_LENGTH, &mut len);
            let mut buf = vec::from_elem(len as uint - 1, 0u8);     // subtract 1 to skip the trailing null character
            gl::GetProgramInfoLog(program, len, ptr::mut_null(), buf.as_mut_ptr() as *mut GLchar);
            fail!(str::raw::from_utf8(buf).to_owned());
        }
    }
    program
}

struct ErrorContext;
impl glfw::ErrorCallback for ErrorContext {
    fn call(&self, _: glfw::Error, description: ~str) {
        fail!("GLFW Error: {:s}", description);
    }
}

struct KeyContext;
impl glfw::KeyCallback for KeyContext {
    fn call(&self, window: &glfw::Window, key: glfw::Key, _: libc::c_int, action: glfw::Action, _: glfw::Modifiers) {
        match (action, key) {
            (glfw::Press, glfw::KeyEscape) => window.set_should_close(true),
            _ => {}
        }
    }
}

struct FramebufferSizeContext {
    chan: Chan<(u32,u32)>
}
impl glfw::FramebufferSizeCallback for FramebufferSizeContext {
    fn call(&self, _: &glfw::Window, width: i32, height: i32) {
        self.chan.send((width as u32,height as u32));
    }
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate prodbg_api;

use prodbg_api::{View, Ui, Reader, Writer, PluginHandler, Service, CViewCallbacks};

use prodbg_api::events::*;

struct LocalsView {
    dummy: u8
}

impl LocalsView {
     fn show_in_ui(&mut self, reader: &mut Reader, ui: &mut Ui) {
         ui.text("");

         ui.columns(3, Some("callstack"), true);
         ui.text("Name"); ui.next_column();
         ui.text("Value"); ui.next_column();
         ui.text("Type"); ui.next_column();

         for entry in reader.find_array("threads").unwrap() {
             let name = entry.find_string("name").unwrap();
             let value = entry.find_string("value").unwrap();
             let type_cell = entry.find_string("type").unwrap();
         }
     }
}

impl View for LocalsView {
    fn new(_: &Ui, _: &Service) -> Self {
        LocalsView {
            dummy: 0u8
        }
    }

    fn update(&mut self, ui: &mut Ui, reader: &mut Reader, writer: &mut Writer) {

        for event in reader.get_events() {
            match event {
                EVENT_SET_LOCALS => {
                    self.show_in_ui(reader, ui)
                }

                _ => {},
            }
        }

        // Request callstack data
        // TODO: Dont' request locals all the time

        writer.event_begin(EVENT_GET_LOCALS as u16);
        writer.write_u8("dummy_remove", 0); // TODO Remove me
        writer.event_end();
    }
}

#[no_mangle]
pub fn init_plugin(plugin_handler: &mut PluginHandler) {
    define_view_plugin!(PLUGIN, b"Locals\0", LocalsView);
    plugin_handler.register_view(&PLUGIN);
}

<commit_msg>Fixed warnings<commit_after>#[macro_use]
extern crate prodbg_api;

use prodbg_api::{View, Ui, Reader, Writer, PluginHandler, Service, CViewCallbacks};

use prodbg_api::events::*;

struct LocalsView {
    _dummy: u8
}

impl LocalsView {
     fn show_in_ui(&mut self, reader: &mut Reader, ui: &mut Ui) {
         ui.text("");

         ui.columns(3, Some("callstack"), true);
         ui.text("Name"); ui.next_column();
         ui.text("Value"); ui.next_column();
         ui.text("Type"); ui.next_column();

         for entry in reader.find_array("threads").unwrap() {
             let _name = entry.find_string("name").unwrap();
             let _value = entry.find_string("value").unwrap();
             let _type_cell = entry.find_string("type").unwrap();
         }
     }
}

impl View for LocalsView {
    fn new(_: &Ui, _: &Service) -> Self {
        LocalsView {
            _dummy: 0u8
        }
    }

    fn update(&mut self, ui: &mut Ui, reader: &mut Reader, _writer: &mut Writer) {

        for event in reader.get_events() {
            match event {
                EVENT_SET_LOCALS => {
                    self.show_in_ui(reader, ui)
                }

                _ => {},
            }
        }

        // Request callstack data
        // TODO: Dont' request locals all the time

        //writer.event_begin(EVENT_GET_LOCALS as u16);
        //writer.event_end();
    }
}

#[no_mangle]
pub fn init_plugin(plugin_handler: &mut PluginHandler) {
    define_view_plugin!(PLUGIN, b"Locals\0", LocalsView);
    plugin_handler.register_view(&PLUGIN);
}

<|endoftext|>


<commit_before>use std::cmp;

use ast::*;
use ast::Stmt::*;
use ast::Expr::*;
use ast::visit::*;

use codegen::expr::is_leaf;
use codegen::x64::reg::*;
use ctxt::Context;

use mem;

use sym::BuiltinType;

pub fn generate<'a, 'ast>(ctxt: &'a Context<'a, 'ast>, fct: &'ast Function) -> Info {
    InfoGenerator::new(ctxt, fct).generate()
}

pub struct Info {
    pub localsize: u32,
    pub tempsize: u32,
    pub fct_call: bool,
}

impl Info {
    pub fn stacksize(&self) -> u32 {
        self.localsize + self.tempsize
    }
}

impl Default for Info {
    fn default() -> Info {
        Info {
            localsize: 0,
            tempsize: 0,
            fct_call: false,
        }
    }
}

struct InfoGenerator<'a, 'ast: 'a> {
    ctxt: &'a Context<'a, 'ast>,
    fct: &'ast Function,

    localsize: u32,
    max_tempsize: u32,
    cur_tempsize: u32,

    param_offset: i32,
    fct_call: bool,
}

impl<'a, 'ast> InfoGenerator<'a, 'ast> {
    fn new(ctxt: &'a Context<'a, 'ast>, fct: &'ast Function) -> InfoGenerator<'a, 'ast> {
        InfoGenerator {
            ctxt: ctxt,
            fct: fct,

            localsize: 0,
            max_tempsize: 0,
            cur_tempsize: 0,

            // first param offset to rbp is +16,
            // rbp+0 -> saved rbp
            // rbp+8 -> return address
            param_offset: 16,

            fct_call: false,
        }
    }

    fn generate(mut self) -> Info {
        self.visit_fct(self.fct);

        Info {
            localsize: self.localsize,
            tempsize: self.max_tempsize,
            fct_call: self.fct_call,
        }
    }

    fn increase_stack(&mut self, id: NodeId) {
        self.ctxt.var_mut(id, |v, _| {
            let ty_size = v.data_type.size();
            self.localsize = mem::align(self.localsize + ty_size, ty_size);
            v.offset = -(self.localsize as i32);
        });
    }
}

impl<'a, 'ast> Visitor<'ast> for InfoGenerator<'a, 'ast> {
    fn visit_param(&mut self, p: &'ast Param) {
        // on x64 only the first 6 parameters are stored in registers
        if (p.idx as usize) < REG_PARAMS.len() {
            self.increase_stack(p.id);

        // the rest of the parameters need to be stored in the callers stack
        } else {
            self.ctxt.var_mut(p.id, |v, _| { v.offset = self.param_offset; });

            // all params on stack need size of 8
            self.param_offset += 8;
        }
    }

    fn visit_stmt(&mut self, s: &'ast Stmt) {
        if let StmtVar(ref var) = *s {
            self.increase_stack(var.id);
        }

        visit::walk_stmt(self, s);
    }

    fn visit_expr_top(&mut self, e: &'ast Expr) {
        self.cur_tempsize = 0;
        self.visit_expr(e);
        self.max_tempsize = cmp::max(self.cur_tempsize, self.max_tempsize);
    }

    fn visit_expr(&mut self, e: &'ast Expr) {
        match *e {
            ExprCall(_) => {
                self.fct_call = true;
            }

            ExprBin(ref expr) => {
                if !is_leaf(&expr.rhs) {
                    self.cur_tempsize += BuiltinType::Int.size();
                }
            }

            _ => {}
        }

        visit::walk_expr(self, e);
    }
}

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

    use ctxt::*;
    use test;

    fn info<F>(code: &'static str, f: F) where F: FnOnce(&Context, &FctInfo, Info) {
        test::parse(code, |ctxt| {
            let fct = ctxt.ast.elements[0].to_function().unwrap();
            let info = generate(ctxt, fct);

            ctxt.fct_info(fct.id, |fct| {
                f(ctxt, fct, info);
            });
        });
    }

    #[test]
    fn test_tempsize() {
        info("fn f() { 1+2*3; }", |_, _, info| {
            assert_eq!(4, info.tempsize);
        });
    }

    #[test]
    fn test_invocation_flag() {
        info("fn f() { g(); } fn g() { }", |_, _, info| {
            assert!(info.fct_call);
        });

        info("fn f() { }", |_, _, info| {
            assert!(!info.fct_call);
        });
    }

    #[test]
    fn test_param_offset() {
        info("fn f(a: bool, b: int) { var c = 1; }", |ctxt, fct, info| {
            assert_eq!(12, info.localsize);

            for (varid, offset) in fct.vars.iter().zip(&[-1, -8, -12]) {
                assert_eq!(*offset, ctxt.var_infos.borrow()[varid.0].offset);
            }
        });
    }

    #[test]
    fn test_params_over_6_offset() {
        info("fn f(a: int, b: int, c: int, d: int,
                   e: int, f: int, g: int, h: int) {
                  var i : int = 1;
              }", |ctxt, fct, info| {
            assert_eq!(28, info.localsize);
            let offsets = [-4, -8, -12, -16, -20, -24, 16, 24, -28];

            for (varid, offset) in fct.vars.iter().zip(&offsets) {
                assert_eq!(*offset, ctxt.var_infos.borrow()[varid.0].offset);
            }
        });
    }

    #[test]
    fn test_var_offset() {
        info("fn f() { var a = true; var b = false; var c = 2; var d = \"abc\"; }", |ctxt, fct, info| {
            assert_eq!(16, info.localsize);

            for (varid, offset) in fct.vars.iter().zip(&[-1, -2, -8, -16]) {
                assert_eq!(*offset, ctxt.var_infos.borrow()[varid.0].offset);
            }
        });
    }
}
<commit_msg>codegen: added tests for tempsize<commit_after>use std::cmp;

use ast::*;
use ast::Stmt::*;
use ast::Expr::*;
use ast::visit::*;

use codegen::expr::is_leaf;
use codegen::x64::reg::*;
use ctxt::Context;

use mem;

use sym::BuiltinType;

pub fn generate<'a, 'ast>(ctxt: &'a Context<'a, 'ast>, fct: &'ast Function) -> Info {
    InfoGenerator::new(ctxt, fct).generate()
}

pub struct Info {
    pub localsize: u32,
    pub tempsize: u32,
    pub fct_call: bool,
}

impl Info {
    pub fn stacksize(&self) -> u32 {
        self.localsize + self.tempsize
    }
}

impl Default for Info {
    fn default() -> Info {
        Info {
            localsize: 0,
            tempsize: 0,
            fct_call: false,
        }
    }
}

struct InfoGenerator<'a, 'ast: 'a> {
    ctxt: &'a Context<'a, 'ast>,
    fct: &'ast Function,

    localsize: u32,
    max_tempsize: u32,
    cur_tempsize: u32,

    param_offset: i32,
    fct_call: bool,
}

impl<'a, 'ast> InfoGenerator<'a, 'ast> {
    fn new(ctxt: &'a Context<'a, 'ast>, fct: &'ast Function) -> InfoGenerator<'a, 'ast> {
        InfoGenerator {
            ctxt: ctxt,
            fct: fct,

            localsize: 0,
            max_tempsize: 0,
            cur_tempsize: 0,

            // first param offset to rbp is +16,
            // rbp+0 -> saved rbp
            // rbp+8 -> return address
            param_offset: 16,

            fct_call: false,
        }
    }

    fn generate(mut self) -> Info {
        self.visit_fct(self.fct);

        Info {
            localsize: self.localsize,
            tempsize: self.max_tempsize,
            fct_call: self.fct_call,
        }
    }

    fn increase_stack(&mut self, id: NodeId) {
        self.ctxt.var_mut(id, |v, _| {
            let ty_size = v.data_type.size();
            self.localsize = mem::align(self.localsize + ty_size, ty_size);
            v.offset = -(self.localsize as i32);
        });
    }
}

impl<'a, 'ast> Visitor<'ast> for InfoGenerator<'a, 'ast> {
    fn visit_param(&mut self, p: &'ast Param) {
        // on x64 only the first 6 parameters are stored in registers
        if (p.idx as usize) < REG_PARAMS.len() {
            self.increase_stack(p.id);

        // the rest of the parameters need to be stored in the callers stack
        } else {
            self.ctxt.var_mut(p.id, |v, _| { v.offset = self.param_offset; });

            // all params on stack need size of 8
            self.param_offset += 8;
        }
    }

    fn visit_stmt(&mut self, s: &'ast Stmt) {
        if let StmtVar(ref var) = *s {
            self.increase_stack(var.id);
        }

        visit::walk_stmt(self, s);
    }

    fn visit_expr_top(&mut self, e: &'ast Expr) {
        self.cur_tempsize = 0;
        self.visit_expr(e);
        self.max_tempsize = cmp::max(self.cur_tempsize, self.max_tempsize);
    }

    fn visit_expr(&mut self, e: &'ast Expr) {
        match *e {
            ExprCall(_) => {
                self.fct_call = true;
            }

            ExprBin(ref expr) => {
                if !is_leaf(&expr.rhs) {
                    self.cur_tempsize += BuiltinType::Int.size();
                }
            }

            _ => {}
        }

        visit::walk_expr(self, e);
    }
}

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

    use ctxt::*;
    use test;

    fn info<F>(code: &'static str, f: F) where F: FnOnce(&Context, &FctInfo, Info) {
        test::parse(code, |ctxt| {
            let fct = ctxt.ast.elements[0].to_function().unwrap();
            let info = generate(ctxt, fct);

            ctxt.fct_info(fct.id, |fct| {
                f(ctxt, fct, info);
            });
        });
    }

    #[test]
    fn test_tempsize() {
        info("fn f() { 1+2*3; }", |_, _, info| { assert_eq!(4, info.tempsize); });
        info("fn f() { 2*3+4+5; }", |_, _, info| { assert_eq!(0, info.tempsize); });
    }

    #[test]
    fn test_invocation_flag() {
        info("fn f() { g(); } fn g() { }", |_, _, info| {
            assert!(info.fct_call);
        });

        info("fn f() { }", |_, _, info| {
            assert!(!info.fct_call);
        });
    }

    #[test]
    fn test_param_offset() {
        info("fn f(a: bool, b: int) { var c = 1; }", |ctxt, fct, info| {
            assert_eq!(12, info.localsize);

            for (varid, offset) in fct.vars.iter().zip(&[-1, -8, -12]) {
                assert_eq!(*offset, ctxt.var_infos.borrow()[varid.0].offset);
            }
        });
    }

    #[test]
    fn test_params_over_6_offset() {
        info("fn f(a: int, b: int, c: int, d: int,
                   e: int, f: int, g: int, h: int) {
                  var i : int = 1;
              }", |ctxt, fct, info| {
            assert_eq!(28, info.localsize);
            let offsets = [-4, -8, -12, -16, -20, -24, 16, 24, -28];

            for (varid, offset) in fct.vars.iter().zip(&offsets) {
                assert_eq!(*offset, ctxt.var_infos.borrow()[varid.0].offset);
            }
        });
    }

    #[test]
    fn test_var_offset() {
        info("fn f() { var a = true; var b = false; var c = 2; var d = \"abc\"; }", |ctxt, fct, info| {
            assert_eq!(16, info.localsize);

            for (varid, offset) in fct.vars.iter().zip(&[-1, -2, -8, -16]) {
                assert_eq!(*offset, ctxt.var_infos.borrow()[varid.0].offset);
            }
        });
    }
}
<|endoftext|>


<commit_before>use error::{CliError, Usage, UsageKind};
use super::Command;

use lib::setting::{self, Config, Ignore, Storage};

pub struct Init;

impl Command for Init {
    fn validation(&self) -> bool { false }

    fn usage(&self) -> Usage {
        return Usage::new(UsageKind::Init);
    }

    fn main(&self) -> Result<(), CliError> {
        try!(setting::create_working_dir());

        try!(Storage::create());

        try!(Config::default().create());

        try!(Ignore::default().create());

        Ok(())
    }
}
<commit_msg>[Remedy] Do not remove setting files by "init" if already exists<commit_after>use error::{CliError, Usage, UsageKind};
use super::Command;

use lib::setting::{self, Config, Ignore, Storage};

pub struct Init;

impl Command for Init {
    fn validation(&self) -> bool { false }

    fn usage(&self) -> Usage {
        return Usage::new(UsageKind::Init);
    }

    fn main(&self) -> Result<(), CliError> {
        try!(setting::create_working_dir());

        try!(Storage::create());

        if !Config::exist() {
            try!(Config::default().create());
        }

        if !Ignore::exist() {
            try!(Ignore::default().create());
        }

        Ok(())
    }
}
<|endoftext|>