<commit_before>// Copyright (C) 2018 O.S. Systems Sofware LTDA
//
// SPDX-License-Identifier: Apache-2.0

use crate::{firmware::Metadata, runtime_settings::RuntimeSettings, update_package::UpdatePackage};

use failure::bail;
use log::debug;
use reqwest::{
    header::{HeaderMap, HeaderName, CONTENT_TYPE, RANGE, USER_AGENT},
    Client, StatusCode,
};
use serde_derive::Serialize;
use std::{path::Path, time::Duration};

#[cfg(test)]
pub(crate) mod tests;

pub(crate) struct Api<'a> {
    server: &'a str,
}

#[derive(Debug)]
pub(crate) enum ProbeResponse {
    NoUpdate,
    Update(UpdatePackage),
    ExtraPoll(i64),
}

impl<'a> Api<'a> {
    pub(crate) fn new(server: &'a str) -> Api<'a> {
        Api { server }
    }

    fn client(&self) -> Result<Client, failure::Error> {
        let mut headers = HeaderMap::new();

        headers.insert(USER_AGENT, "updatehub/next".parse()?);
        headers.insert(CONTENT_TYPE, "application/json".parse()?);
        headers.insert(
            HeaderName::from_static("api-content-type"),
            "application/vnd.updatehub-v1+json".parse()?,
        );

        Ok(Client::builder()
            .timeout(Duration::from_secs(10))
            .default_headers(headers)
            .build()?)
    }

    pub fn probe(
        &self,
        runtime_settings: &RuntimeSettings,
        firmware: &Metadata,
    ) -> Result<ProbeResponse, failure::Error> {
        let mut response = self
            .client()?
            .post(&format!("{}/upgrades", &self.server))
            .header(
                HeaderName::from_static("api-retries"),
                runtime_settings.retries(),
            )
            .json(firmware)
            .send()?;

        match response.status() {
            StatusCode::NOT_FOUND => Ok(ProbeResponse::NoUpdate),
            StatusCode::OK => {
                match response
                    .headers()
                    .get("add-extra-poll")
                    .and_then(|extra_poll| extra_poll.to_str().ok())
                    .and_then(|extra_poll| extra_poll.parse().ok())
                {
                    Some(extra_poll) => Ok(ProbeResponse::ExtraPoll(extra_poll)),
                    None => Ok(ProbeResponse::Update(UpdatePackage::parse(
                        &response.text()?,
                    )?)),
                }
            }
            _ => bail!("Invalid response. Status: {}", response.status()),
        }
    }

    pub fn download_object(
        &self,
        product_uid: &str,
        package_uid: &str,
        download_dir: &Path,
        object: &str,
    ) -> Result<(), failure::Error> {
        use std::fs::{create_dir_all, OpenOptions};

        // FIXME: Discuss the need of packages inside the route
        let mut client = self.client()?.get(&format!(
            "{}/products/{}/packages/{}/objects/{}",
            &self.server, product_uid, package_uid, object
        ));

        let path = download_dir;
        if !&path.exists() {
            debug!("Creating directory to store the downloads.");
            create_dir_all(&path)?;
        }

        let file = path.join(object);
        if file.exists() {
            client = client.header(RANGE, format!("bytes={}-", file.metadata()?.len() - 1));
        }

        let mut file = OpenOptions::new().create(true).append(true).open(&file)?;
        let mut response = client.send()?;
        if response.status().is_success() {
            response.copy_to(&mut file)?;
            return Ok(());
        }

        bail!("Couldn't download the object {}", object)
    }

    pub fn report(
        &self,
        state: &str,
        firmware: &'a Metadata,
        package_uid: &str,
        previous_state: Option<&str>,
        error_message: Option<String>,
    ) -> Result<(), failure::Error> {
        #[derive(Serialize)]
        #[serde(rename_all = "kebab-case")]
        struct Payload<'a> {
            #[serde(rename = "status")]
            state: &'a str,
            #[serde(flatten)]
            firmware: &'a Metadata,
            package_uid: &'a str,
            #[serde(skip_serializing_if = "Option::is_none")]
            previous_state: Option<&'a str>,
            #[serde(skip_serializing_if = "Option::is_none")]
            error_message: Option<String>,
        }

        let payload = Payload {
            state,
            firmware,
            package_uid,
            previous_state,
            error_message,
        };

        self.client()?
            .post(&format!("{}/report", &self.server))
            .json(&payload)
            .send()?;
        Ok(())
    }
}
<commit_msg>client: download_object: Reduce variable indirections<commit_after>// Copyright (C) 2018 O.S. Systems Sofware LTDA
//
// SPDX-License-Identifier: Apache-2.0

use crate::{firmware::Metadata, runtime_settings::RuntimeSettings, update_package::UpdatePackage};

use failure::bail;
use log::debug;
use reqwest::{
    header::{HeaderMap, HeaderName, CONTENT_TYPE, RANGE, USER_AGENT},
    Client, StatusCode,
};
use serde_derive::Serialize;
use std::{path::Path, time::Duration};

#[cfg(test)]
pub(crate) mod tests;

pub(crate) struct Api<'a> {
    server: &'a str,
}

#[derive(Debug)]
pub(crate) enum ProbeResponse {
    NoUpdate,
    Update(UpdatePackage),
    ExtraPoll(i64),
}

impl<'a> Api<'a> {
    pub(crate) fn new(server: &'a str) -> Api<'a> {
        Api { server }
    }

    fn client(&self) -> Result<Client, failure::Error> {
        let mut headers = HeaderMap::new();

        headers.insert(USER_AGENT, "updatehub/next".parse()?);
        headers.insert(CONTENT_TYPE, "application/json".parse()?);
        headers.insert(
            HeaderName::from_static("api-content-type"),
            "application/vnd.updatehub-v1+json".parse()?,
        );

        Ok(Client::builder()
            .timeout(Duration::from_secs(10))
            .default_headers(headers)
            .build()?)
    }

    pub fn probe(
        &self,
        runtime_settings: &RuntimeSettings,
        firmware: &Metadata,
    ) -> Result<ProbeResponse, failure::Error> {
        let mut response = self
            .client()?
            .post(&format!("{}/upgrades", &self.server))
            .header(
                HeaderName::from_static("api-retries"),
                runtime_settings.retries(),
            )
            .json(firmware)
            .send()?;

        match response.status() {
            StatusCode::NOT_FOUND => Ok(ProbeResponse::NoUpdate),
            StatusCode::OK => {
                match response
                    .headers()
                    .get("add-extra-poll")
                    .and_then(|extra_poll| extra_poll.to_str().ok())
                    .and_then(|extra_poll| extra_poll.parse().ok())
                {
                    Some(extra_poll) => Ok(ProbeResponse::ExtraPoll(extra_poll)),
                    None => Ok(ProbeResponse::Update(UpdatePackage::parse(
                        &response.text()?,
                    )?)),
                }
            }
            _ => bail!("Invalid response. Status: {}", response.status()),
        }
    }

    pub fn download_object(
        &self,
        product_uid: &str,
        package_uid: &str,
        download_dir: &Path,
        object: &str,
    ) -> Result<(), failure::Error> {
        use std::fs::{create_dir_all, OpenOptions};

        // FIXME: Discuss the need of packages inside the route
        let mut client = self.client()?.get(&format!(
            "{}/products/{}/packages/{}/objects/{}",
            &self.server, product_uid, package_uid, object
        ));

        if !download_dir.exists() {
            debug!("Creating directory to store the downloads.");
            create_dir_all(download_dir)?;
        }

        let file = download_dir.join(object);
        if file.exists() {
            client = client.header(RANGE, format!("bytes={}-", file.metadata()?.len() - 1));
        }

        let mut file = OpenOptions::new().create(true).append(true).open(&file)?;
        let mut response = client.send()?;
        if response.status().is_success() {
            response.copy_to(&mut file)?;
            return Ok(());
        }

        bail!("Couldn't download the object {}", object)
    }

    pub fn report(
        &self,
        state: &str,
        firmware: &'a Metadata,
        package_uid: &str,
        previous_state: Option<&str>,
        error_message: Option<String>,
    ) -> Result<(), failure::Error> {
        #[derive(Serialize)]
        #[serde(rename_all = "kebab-case")]
        struct Payload<'a> {
            #[serde(rename = "status")]
            state: &'a str,
            #[serde(flatten)]
            firmware: &'a Metadata,
            package_uid: &'a str,
            #[serde(skip_serializing_if = "Option::is_none")]
            previous_state: Option<&'a str>,
            #[serde(skip_serializing_if = "Option::is_none")]
            error_message: Option<String>,
        }

        let payload = Payload {
            state,
            firmware,
            package_uid,
            previous_state,
            error_message,
        };

        self.client()?
            .post(&format!("{}/report", &self.server))
            .json(&payload)
            .send()?;
        Ok(())
    }
}
<|endoftext|>


<commit_before>// Copyright 2020 Google LLC
//
// Use of this source code is governed by an MIT-style license that can be found
// in the LICENSE file or at https://opensource.org/licenses/MIT.
mod connection;

use self::connection::{Connection};

use lazy_static::lazy_static;
use std::io::{Result};
use std::sync::Mutex;

fn open(var: &str) -> std::fs::File {
    let fd = std::env::var(var)
        .expect(&format!("invalid variable `{}`", var))
        .parse()
        .expect(&format!("failed to parse file descriptor"));

    // TODO: Add support for Windows.
    unsafe {
        std::os::unix::io::FromRawFd::from_raw_fd(fd)
    }
}

lazy_static! {
    static ref CONNECTION: Mutex<Connection<std::fs::File, std::fs::File>> = {
        let input = open("FLEETSPEAK_COMMS_CHANNEL_INFD");
        let output = open("FLEETSPEAK_COMMS_CHANNEL_OUTFD");

        let conn = Connection::new(input, output).expect("handshake failure");
        Mutex::new(conn)
    };
}

pub fn heartbeat() -> Result<()> {
    connected(|conn| conn.heartbeat())
}

pub fn startup(version: &str) -> Result<()> {
    connected(|conn| conn.startup(version))
}

pub fn send<M>(service: &str, kind: &str, data: M) -> Result<()>
where
    M: prost::Message,
{
    connected(|conn| conn.send(service, kind, data))
}

pub fn receive<M>() -> Result<M>
where
    M: prost::Message + Default,
{
    connected(|conn| conn.receive())
}

fn connected<F, T>(f: F) -> Result<T>
where
    F: FnOnce(&mut Connection<std::fs::File, std::fs::File>) -> Result<T>
{
    let mut conn = CONNECTION.lock().expect("poisoned connection mutex");
    f(&mut conn)
}
<commit_msg>Improve code style in the client module.<commit_after>// Copyright 2020 Google LLC
//
// Use of this source code is governed by an MIT-style license that can be found
// in the LICENSE file or at https://opensource.org/licenses/MIT.
mod connection;

use self::connection::Connection;

use lazy_static::lazy_static;
use std::fs::File;
use std::io::Result;
use std::sync::Mutex;

fn open(var: &str) -> File {
    let fd = std::env::var(var)
        .expect(&format!("invalid variable `{}`", var))
        .parse()
        .expect(&format!("failed to parse file descriptor"));

    // TODO: Add support for Windows.
    unsafe {
        std::os::unix::io::FromRawFd::from_raw_fd(fd)
    }
}

lazy_static! {
    static ref CONNECTION: Mutex<Connection<File, File>> = {
        let input = open("FLEETSPEAK_COMMS_CHANNEL_INFD");
        let output = open("FLEETSPEAK_COMMS_CHANNEL_OUTFD");

        let conn = Connection::new(input, output).expect("handshake failure");
        Mutex::new(conn)
    };
}

pub fn heartbeat() -> Result<()> {
    connected(|conn| conn.heartbeat())
}

pub fn startup(version: &str) -> Result<()> {
    connected(|conn| conn.startup(version))
}

pub fn send<M>(service: &str, kind: &str, data: M) -> Result<()>
where
    M: prost::Message,
{
    connected(|conn| conn.send(service, kind, data))
}

pub fn receive<M>() -> Result<M>
where
    M: prost::Message + Default,
{
    connected(|conn| conn.receive())
}

fn connected<F, T>(f: F) -> Result<T>
where
    F: FnOnce(&mut Connection<File, File>) -> Result<T>
{
    let mut conn = CONNECTION.lock().expect("poisoned connection mutex");
    f(&mut conn)
}
<|endoftext|>


<commit_before>use std::sync::Arc;
use std::io;
use bifrost::conshash::{ConsistentHashing, CHError};
use bifrost::raft::client::{RaftClient, ClientError};
use bifrost::raft;
use bifrost::rpc::{RPCError, DEFAULT_CLIENT_POOL, Server as RPCServer};
use bifrost::raft::state_machine::master::ExecError;

use server::{transactions as txn_server, cell_rpc as plain_server};
use ram::types::Id;
use ram::cell::{Cell, Header, ReadError, WriteError};
use ram::schema::sm::client::{SMClient as SchemaClient};
use ram::schema::sm::{DEFAULT_SM_ID};
use ram::schema::Schema;
use self::transaction::*;

static TRANSACTION_MAX_RETRY: u32 = 50;

pub mod transaction;

#[derive(Debug)]
pub enum NebClientError {
    RaftClientError(ClientError),
    ConsistentHashtableError(CHError)
}

pub struct Client {
    pub conshash: Arc<ConsistentHashing>,
    pub raft_client: Arc<RaftClient>,
    pub schema_client: SchemaClient
}

impl Client {
    pub fn new(subscription_server: &Arc<RPCServer>, meta_servers: &Vec<String>, group: &String) -> Result<Client, NebClientError> {
        match RaftClient::new(meta_servers, raft::DEFAULT_SERVICE_ID) {
            Ok(raft_client) => {
                RaftClient::prepare_subscription(subscription_server);
                assert!(RaftClient::can_callback());
                match ConsistentHashing::new_client(group, &raft_client) {
                    Ok(chash) => Ok(Client {
                        conshash: chash,
                        raft_client: raft_client.clone(),
                        schema_client: SchemaClient::new(DEFAULT_SM_ID, &raft_client)
                    }),
                    Err(err) => Err(NebClientError::ConsistentHashtableError(err))
                }
            },
            Err(err) => Err(NebClientError::RaftClientError(err))
        }
    }
    pub fn locate_server_address(&self, id: &Id) -> Result<String, RPCError> {
        match self.conshash.get_server(id.higher) {
            Some(n) => Ok(n),
            None => Err(RPCError::IOError(io::Error::new(io::ErrorKind::NotFound, "cannot locate")))
        }
    }
    pub fn locate_plain_server(&self, id: &Id) -> Result<Arc<plain_server::SyncServiceClient>, RPCError> {
        let address = self.locate_server_address(id)?;
        let client = match DEFAULT_CLIENT_POOL.get(&address) {
            Ok(c) => c,
            Err(e) => return Err(RPCError::IOError(e))
        };
        Ok(plain_server::SyncServiceClient::new(plain_server::DEFAULT_SERVICE_ID, &client))
    }
    pub fn read_cell(&self, id: &Id) -> Result<Result<Cell, ReadError>, RPCError> {
        let client = self.locate_plain_server(id)?;
        client.read_cell(id)
    }
    pub fn write_cell(&self, cell: &Cell) -> Result<Result<Header, WriteError>, RPCError> {
        let client = self.locate_plain_server(&cell.id())?;
        client.write_cell(cell)
    }
    pub fn update_cell(&self, cell: &Cell) -> Result<Result<Header, WriteError>, RPCError> {
        let client = self.locate_plain_server(&cell.id())?;
        client.update_cell(cell)
    }
    pub fn remove_cell(&self, id: &Id) -> Result<Result<(), WriteError>, RPCError> {
        let client = self.locate_plain_server(id)?;
        client.remove_cell(id)
    }
    pub fn transaction<TFN>(&self, mut func: TFN) -> Result<(), TxnError>
        where TFN: FnMut(&mut Transaction) -> Result<(), TxnError> {
        let server_name = match self.conshash.rand_server() {
            Some(name) => name,
            None => return Err(TxnError::CannotFindAServer)
        };
        let txn_client = match txn_server::new_client(&server_name) {
            Ok(client) => client,
            Err(e) => return Err(TxnError::IoError(e))
        };
        let mut txn_id: txn_server::TxnId;
        let mut retried = 0;
        while retried < TRANSACTION_MAX_RETRY {
            txn_id = match txn_client.begin() {
                Ok(Ok(id)) => id,
                _ => return Err(TxnError::CannotBegin)
            };
            let mut txn = Transaction{
                tid: txn_id,
                state: txn_server::TxnState::Started,
                client: txn_client.clone()
            };
            let mut exec_result = func(&mut txn);
            if exec_result.is_ok() && txn.state == txn_server::TxnState::Started {
                exec_result = txn.prepare();
            }
            if exec_result.is_ok() && txn.state == txn_server::TxnState::Prepared {
                exec_result = txn.commit();
            }
            match exec_result {
                Ok(()) => {
                    return Ok(());
                },
                Err(TxnError::NotRealizable) => {
                    txn.abort();  // continue the loop to retry
                },
                Err(e) => {
                    // abort will always be an error to achieve early break
                    txn.abort();
                    return Err(e);
                }
            }
            retried += 1;
        }
        Err(TxnError::TooManyRetry)
    }
    pub fn new_schema(&self, schema: &mut Schema) -> Result<(), ExecError> {
        let schema_id = self.schema_client.next_id()?.unwrap();
        schema.id = schema_id;
        self.schema_client.new_schema(schema)?;
        Ok(())
    }
    pub fn del_schema(&self, schema_id: &String) -> Result<(), ExecError> {
        self.schema_client.del_schema(schema_id)?;
        Ok(())
    }
    pub fn get_all_schema(&self) -> Result<Vec<Schema>, ExecError> {
        Ok(self.schema_client.get_all()?.unwrap())
    }
}<commit_msg>reset to larger retry times for transactions<commit_after>use std::sync::Arc;
use std::io;
use bifrost::conshash::{ConsistentHashing, CHError};
use bifrost::raft::client::{RaftClient, ClientError};
use bifrost::raft;
use bifrost::rpc::{RPCError, DEFAULT_CLIENT_POOL, Server as RPCServer};
use bifrost::raft::state_machine::master::ExecError;

use server::{transactions as txn_server, cell_rpc as plain_server};
use ram::types::Id;
use ram::cell::{Cell, Header, ReadError, WriteError};
use ram::schema::sm::client::{SMClient as SchemaClient};
use ram::schema::sm::{DEFAULT_SM_ID};
use ram::schema::Schema;
use self::transaction::*;

static TRANSACTION_MAX_RETRY: u32 = 100;

pub mod transaction;

#[derive(Debug)]
pub enum NebClientError {
    RaftClientError(ClientError),
    ConsistentHashtableError(CHError)
}

pub struct Client {
    pub conshash: Arc<ConsistentHashing>,
    pub raft_client: Arc<RaftClient>,
    pub schema_client: SchemaClient
}

impl Client {
    pub fn new(subscription_server: &Arc<RPCServer>, meta_servers: &Vec<String>, group: &String) -> Result<Client, NebClientError> {
        match RaftClient::new(meta_servers, raft::DEFAULT_SERVICE_ID) {
            Ok(raft_client) => {
                RaftClient::prepare_subscription(subscription_server);
                assert!(RaftClient::can_callback());
                match ConsistentHashing::new_client(group, &raft_client) {
                    Ok(chash) => Ok(Client {
                        conshash: chash,
                        raft_client: raft_client.clone(),
                        schema_client: SchemaClient::new(DEFAULT_SM_ID, &raft_client)
                    }),
                    Err(err) => Err(NebClientError::ConsistentHashtableError(err))
                }
            },
            Err(err) => Err(NebClientError::RaftClientError(err))
        }
    }
    pub fn locate_server_address(&self, id: &Id) -> Result<String, RPCError> {
        match self.conshash.get_server(id.higher) {
            Some(n) => Ok(n),
            None => Err(RPCError::IOError(io::Error::new(io::ErrorKind::NotFound, "cannot locate")))
        }
    }
    pub fn locate_plain_server(&self, id: &Id) -> Result<Arc<plain_server::SyncServiceClient>, RPCError> {
        let address = self.locate_server_address(id)?;
        let client = match DEFAULT_CLIENT_POOL.get(&address) {
            Ok(c) => c,
            Err(e) => return Err(RPCError::IOError(e))
        };
        Ok(plain_server::SyncServiceClient::new(plain_server::DEFAULT_SERVICE_ID, &client))
    }
    pub fn read_cell(&self, id: &Id) -> Result<Result<Cell, ReadError>, RPCError> {
        let client = self.locate_plain_server(id)?;
        client.read_cell(id)
    }
    pub fn write_cell(&self, cell: &Cell) -> Result<Result<Header, WriteError>, RPCError> {
        let client = self.locate_plain_server(&cell.id())?;
        client.write_cell(cell)
    }
    pub fn update_cell(&self, cell: &Cell) -> Result<Result<Header, WriteError>, RPCError> {
        let client = self.locate_plain_server(&cell.id())?;
        client.update_cell(cell)
    }
    pub fn remove_cell(&self, id: &Id) -> Result<Result<(), WriteError>, RPCError> {
        let client = self.locate_plain_server(id)?;
        client.remove_cell(id)
    }
    pub fn transaction<TFN>(&self, mut func: TFN) -> Result<(), TxnError>
        where TFN: FnMut(&mut Transaction) -> Result<(), TxnError> {
        let server_name = match self.conshash.rand_server() {
            Some(name) => name,
            None => return Err(TxnError::CannotFindAServer)
        };
        let txn_client = match txn_server::new_client(&server_name) {
            Ok(client) => client,
            Err(e) => return Err(TxnError::IoError(e))
        };
        let mut txn_id: txn_server::TxnId;
        let mut retried = 0;
        while retried < TRANSACTION_MAX_RETRY {
            txn_id = match txn_client.begin() {
                Ok(Ok(id)) => id,
                _ => return Err(TxnError::CannotBegin)
            };
            let mut txn = Transaction{
                tid: txn_id,
                state: txn_server::TxnState::Started,
                client: txn_client.clone()
            };
            let mut exec_result = func(&mut txn);
            if exec_result.is_ok() && txn.state == txn_server::TxnState::Started {
                exec_result = txn.prepare();
            }
            if exec_result.is_ok() && txn.state == txn_server::TxnState::Prepared {
                exec_result = txn.commit();
            }
            match exec_result {
                Ok(()) => {
                    return Ok(());
                },
                Err(TxnError::NotRealizable) => {
                    txn.abort();  // continue the loop to retry
                },
                Err(e) => {
                    // abort will always be an error to achieve early break
                    txn.abort();
                    return Err(e);
                }
            }
            retried += 1;
            debug!("Client retry transaction, {:?} times", retried);
        }
        Err(TxnError::TooManyRetry)
    }
    pub fn new_schema(&self, schema: &mut Schema) -> Result<(), ExecError> {
        let schema_id = self.schema_client.next_id()?.unwrap();
        schema.id = schema_id;
        self.schema_client.new_schema(schema)?;
        Ok(())
    }
    pub fn del_schema(&self, schema_id: &String) -> Result<(), ExecError> {
        self.schema_client.del_schema(schema_id)?;
        Ok(())
    }
    pub fn get_all_schema(&self) -> Result<Vec<Schema>, ExecError> {
        Ok(self.schema_client.get_all()?.unwrap())
    }
}<|endoftext|>


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

//! Library module for the key holder functionalities provided by Grin.

extern crate blake2_rfc as blake2;
extern crate byteorder;
extern crate grin_util as util;
extern crate rand;
extern crate serde;
extern crate serde_derive;
extern crate serde_json;

mod blind;
mod extkey;

pub use blind::{BlindSum, BlindingFactor};
pub use extkey::{ExtendedKey, Identifier, IDENTIFIER_SIZE};
pub mod keychain;
pub use keychain::{Error, Keychain};
<commit_msg>Missing macro use in keychain<commit_after>// Copyright 2017 The Grin Developers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Library module for the key holder functionalities provided by Grin.

extern crate blake2_rfc as blake2;
extern crate byteorder;
extern crate grin_util as util;
extern crate rand;
extern crate serde;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;

mod blind;
mod extkey;

pub use blind::{BlindSum, BlindingFactor};
pub use extkey::{ExtendedKey, Identifier, IDENTIFIER_SIZE};
pub mod keychain;
pub use keychain::{Error, Keychain};
<|endoftext|>


<commit_before>use super::{Result, Error};

use std::sync::mpsc;

use websocket::ws::sender::Sender as SenderTrait;
use websocket::client::{Client, Sender, Receiver};
use websocket::stream::WebSocketStream;
use websocket::message::{Message as WsMessage, Type as MessageType};

use serde_json;
use serde_json::builder::ObjectBuilder;

use super::model::*;

/// Websocket connection to the Discord servers.
pub struct Connection {
	keepalive_channel: mpsc::Sender<Status>,
	receiver: Receiver<WebSocketStream>,
	token: String,
}

impl Connection {
	/// Establish a connection to the Discord websocket servers.
	///
	/// Returns both the `Connection` and the `ReadyEvent` which is always the
	/// first event received and contains initial state information.
	///
	/// Usually called internally by `Discord::connect`, which provides both
	/// the token and URL.
	pub fn new(url: &str, token: &str) -> Result<(Connection, ReadyEvent)> {
		debug!("Gateway: {}", url);
		// establish the websocket connection
		let url = match ::websocket::client::request::Url::parse(url) {
			Ok(url) => url,
			Err(_) => return Err(Error::Other("Invalid URL in Connection::new()"))
		};
		let response = try!(try!(Client::connect(url)).send());
		try!(response.validate());
		let (mut sender, mut receiver) = response.begin().split();

		// send the handshake
		let map = ObjectBuilder::new()
			.insert("op", 2)
			.insert_object("d", |object| object
				.insert("token", token)
				.insert_object("properties", |object| object
					.insert("$os", ::std::env::consts::OS)
					.insert("$browser", "Discord library for Rust")
					.insert("$device", "discord-rs")
					.insert("$referring_domain", "")
					.insert("$referrer", "")
				)
				.insert("v", 3)
			)
			.unwrap();
		try!(sender.send_message(&WsMessage::text(try!(serde_json::to_string(&map)))));

		// read the Ready event
		let event = try!(recv_message(&mut receiver));
		let ready = match event {
			Event::Ready(ready) => ready,
			_ => return Err(Error::Other("First event was not READY"))
		};
		let heartbeat_interval = ready.heartbeat_interval;

		// spawn the keepalive thread
		let (tx, rx) = mpsc::channel();
		try!(::std::thread::Builder::new()
			.name("Discord Keepalive".into())
			.spawn(move || keepalive(heartbeat_interval, sender, rx)));

		// return the connection
		Ok((Connection {
			keepalive_channel: tx,
			receiver: receiver,
			token: token.to_owned(),
		}, ready))
	}

	/// Change the game that this client reports as playing. Games are referred
	/// to by a numeric id which is interpreted by the official Discord client.
	pub fn set_game(&self, game: Option<Game>) {
		let _ = self.keepalive_channel.send(Status::SetGame(game));
	}

	/// Connect to the specified voice channel. Any previous channel will be
	/// disconnected from.
	pub fn voice_connect(&self, server_id: &ServerId, channel_id: &ChannelId) {
		let _ = self.keepalive_channel.send(Status::SendMessage(ObjectBuilder::new()
			.insert("op", 4)
			.insert_object("d", |object| object
				.insert("guild_id", &server_id.0)
				.insert("channel_id", &channel_id.0)
				.insert("self_mute", false)
				.insert("self_deaf", false)
			)
			.unwrap()
		));
	}

	/// Disconnect from the current voice channel, if any.
	pub fn voice_disconnect(&self) {
		let _ = self.keepalive_channel.send(Status::SendMessage(ObjectBuilder::new()
			.insert("op", 4)
			.insert_object("d", |object| object
				.insert("guild_id", serde_json::Value::Null)
				.insert("channel_id", serde_json::Value::Null)
				.insert("self_mute", false)
				.insert("self_deaf", false)
			)
			.unwrap()
		));
	}

	/// Receive an event over the websocket, blocking until one is available.
	pub fn recv_event(&mut self) -> Result<Event> {
		match recv_message(&mut self.receiver) {
			Ok(Event::_ChangeGateway(url)) => {
				let (conn, ready) = try!(Connection::new(&url, &self.token));
				try!(::std::mem::replace(self, conn).shutdown());
				Ok(Event::GatewayChanged(url, ready))
			}
			e => e,
		}
	}

	/// Cleanly shut down the websocket connection. Optional.
	pub fn shutdown(mut self) -> Result<()> {
		try!(self.receiver.get_mut().get_mut().shutdown(::std::net::Shutdown::Both));
		Ok(())
	}
}

fn recv_message(receiver: &mut Receiver<WebSocketStream>) -> Result<Event> {
	use websocket::ws::receiver::Receiver;
	let message: WsMessage = try!(receiver.recv_message());
	if message.opcode == MessageType::Close {
		Ok(Event::Closed(message.cd_status_code.unwrap_or(0xffff)))
	} else if message.opcode != MessageType::Text {
		warn!("Unexpected message: {:?}", message);
		Ok(Event::Closed(0xfffe))
	} else {
		let json: serde_json::Value = try!(serde_json::from_reader(&message.payload[..]));
		let original = format!("{:?}", json);
		match Event::decode(json) {
			Ok(event) => Ok(event),
			Err(err) => {
				// If there was a decode failure, print the original json for debugging
				warn!("Error decoding: {}", original);
				Err(err)
			}
		}
	}
}

enum Status {
	SetGame(Option<Game>),
	SendMessage(serde_json::Value),
}

fn keepalive(interval: u64, mut sender: Sender<WebSocketStream>, channel: mpsc::Receiver<Status>) {
	let duration = ::time::Duration::milliseconds(interval as i64);
	let mut timer = ::utils::Timer::new(duration);
	let mut game = None;

	'outer: loop {
		::std::thread::sleep_ms(100);

		loop {
			match channel.try_recv() {
				Ok(Status::SetGame(new_game)) => {
					game = new_game;
					timer.immediately();
				},
				Ok(Status::SendMessage(val)) => {
					let json = match serde_json::to_string(&val) {
						Ok(json) => json,
						Err(e) => return warn!("Error encoding message: {:?}", e)
					};
					match sender.send_message(&WsMessage::text(json)) {
						Ok(()) => {},
						Err(e) => return warn!("Error sending message: {:?}", e)
					}
				},
				Err(mpsc::TryRecvError::Empty) => break,
				Err(mpsc::TryRecvError::Disconnected) => break 'outer,
			}
		}

		if timer.check_and_add(duration) {
			let map = ObjectBuilder::new()
				.insert("op", 3)
				.insert_object("d", |mut object| {
					object = object.insert("idle_since", serde_json::Value::Null);
					match game {
						Some(ref game) => object.insert_object("game", |object| object
							.insert("name", &game.name)
						),
						None => object.insert("game", serde_json::Value::Null),
					}
				})
				.unwrap();
			let json = match serde_json::to_string(&map) {
				Ok(json) => json,
				Err(e) => return warn!("Error encoding keepalive: {:?}", e)
			};
			match sender.send_message(&WsMessage::text(json)) {
				Ok(()) => {},
				Err(e) => return warn!("Error sending keepalive: {:?}", e)
			}
		}
	}
	let _ = sender.get_mut().shutdown(::std::net::Shutdown::Both);
}
<commit_msg>Add convenience Connection::set_game_name method<commit_after>use super::{Result, Error};

use std::sync::mpsc;

use websocket::ws::sender::Sender as SenderTrait;
use websocket::client::{Client, Sender, Receiver};
use websocket::stream::WebSocketStream;
use websocket::message::{Message as WsMessage, Type as MessageType};

use serde_json;
use serde_json::builder::ObjectBuilder;

use super::model::*;

/// Websocket connection to the Discord servers.
pub struct Connection {
	keepalive_channel: mpsc::Sender<Status>,
	receiver: Receiver<WebSocketStream>,
	token: String,
}

impl Connection {
	/// Establish a connection to the Discord websocket servers.
	///
	/// Returns both the `Connection` and the `ReadyEvent` which is always the
	/// first event received and contains initial state information.
	///
	/// Usually called internally by `Discord::connect`, which provides both
	/// the token and URL.
	pub fn new(url: &str, token: &str) -> Result<(Connection, ReadyEvent)> {
		debug!("Gateway: {}", url);
		// establish the websocket connection
		let url = match ::websocket::client::request::Url::parse(url) {
			Ok(url) => url,
			Err(_) => return Err(Error::Other("Invalid URL in Connection::new()"))
		};
		let response = try!(try!(Client::connect(url)).send());
		try!(response.validate());
		let (mut sender, mut receiver) = response.begin().split();

		// send the handshake
		let map = ObjectBuilder::new()
			.insert("op", 2)
			.insert_object("d", |object| object
				.insert("token", token)
				.insert_object("properties", |object| object
					.insert("$os", ::std::env::consts::OS)
					.insert("$browser", "Discord library for Rust")
					.insert("$device", "discord-rs")
					.insert("$referring_domain", "")
					.insert("$referrer", "")
				)
				.insert("v", 3)
			)
			.unwrap();
		try!(sender.send_message(&WsMessage::text(try!(serde_json::to_string(&map)))));

		// read the Ready event
		let event = try!(recv_message(&mut receiver));
		let ready = match event {
			Event::Ready(ready) => ready,
			_ => return Err(Error::Other("First event was not READY"))
		};
		let heartbeat_interval = ready.heartbeat_interval;

		// spawn the keepalive thread
		let (tx, rx) = mpsc::channel();
		try!(::std::thread::Builder::new()
			.name("Discord Keepalive".into())
			.spawn(move || keepalive(heartbeat_interval, sender, rx)));

		// return the connection
		Ok((Connection {
			keepalive_channel: tx,
			receiver: receiver,
			token: token.to_owned(),
		}, ready))
	}

	/// Change the game information that this client reports as playing.
	pub fn set_game(&self, game: Option<Game>) {
		let _ = self.keepalive_channel.send(Status::SetGame(game));
	}

	/// Set the client to be playing this game, with defaults used for any
	/// extended information.
	pub fn set_game_name(&self, name: String) {
		self.set_game(Some(Game { name: name }));
	}

	/// Connect to the specified voice channel. Any previous channel will be
	/// disconnected from.
	pub fn voice_connect(&self, server_id: &ServerId, channel_id: &ChannelId) {
		let _ = self.keepalive_channel.send(Status::SendMessage(ObjectBuilder::new()
			.insert("op", 4)
			.insert_object("d", |object| object
				.insert("guild_id", &server_id.0)
				.insert("channel_id", &channel_id.0)
				.insert("self_mute", false)
				.insert("self_deaf", false)
			)
			.unwrap()
		));
	}

	/// Disconnect from the current voice channel, if any.
	pub fn voice_disconnect(&self) {
		let _ = self.keepalive_channel.send(Status::SendMessage(ObjectBuilder::new()
			.insert("op", 4)
			.insert_object("d", |object| object
				.insert("guild_id", serde_json::Value::Null)
				.insert("channel_id", serde_json::Value::Null)
				.insert("self_mute", false)
				.insert("self_deaf", false)
			)
			.unwrap()
		));
	}

	/// Receive an event over the websocket, blocking until one is available.
	pub fn recv_event(&mut self) -> Result<Event> {
		match recv_message(&mut self.receiver) {
			Ok(Event::_ChangeGateway(url)) => {
				let (conn, ready) = try!(Connection::new(&url, &self.token));
				try!(::std::mem::replace(self, conn).shutdown());
				Ok(Event::GatewayChanged(url, ready))
			}
			e => e,
		}
	}

	/// Cleanly shut down the websocket connection. Optional.
	pub fn shutdown(mut self) -> Result<()> {
		try!(self.receiver.get_mut().get_mut().shutdown(::std::net::Shutdown::Both));
		Ok(())
	}
}

fn recv_message(receiver: &mut Receiver<WebSocketStream>) -> Result<Event> {
	use websocket::ws::receiver::Receiver;
	let message: WsMessage = try!(receiver.recv_message());
	if message.opcode == MessageType::Close {
		Ok(Event::Closed(message.cd_status_code.unwrap_or(0xffff)))
	} else if message.opcode != MessageType::Text {
		warn!("Unexpected message: {:?}", message);
		Ok(Event::Closed(0xfffe))
	} else {
		let json: serde_json::Value = try!(serde_json::from_reader(&message.payload[..]));
		let original = format!("{:?}", json);
		match Event::decode(json) {
			Ok(event) => Ok(event),
			Err(err) => {
				// If there was a decode failure, print the original json for debugging
				warn!("Error decoding: {}", original);
				Err(err)
			}
		}
	}
}

enum Status {
	SetGame(Option<Game>),
	SendMessage(serde_json::Value),
}

fn keepalive(interval: u64, mut sender: Sender<WebSocketStream>, channel: mpsc::Receiver<Status>) {
	let duration = ::time::Duration::milliseconds(interval as i64);
	let mut timer = ::utils::Timer::new(duration);
	let mut game = None;

	'outer: loop {
		::std::thread::sleep_ms(100);

		loop {
			match channel.try_recv() {
				Ok(Status::SetGame(new_game)) => {
					game = new_game;
					timer.immediately();
				},
				Ok(Status::SendMessage(val)) => {
					let json = match serde_json::to_string(&val) {
						Ok(json) => json,
						Err(e) => return warn!("Error encoding message: {:?}", e)
					};
					match sender.send_message(&WsMessage::text(json)) {
						Ok(()) => {},
						Err(e) => return warn!("Error sending message: {:?}", e)
					}
				},
				Err(mpsc::TryRecvError::Empty) => break,
				Err(mpsc::TryRecvError::Disconnected) => break 'outer,
			}
		}

		if timer.check_and_add(duration) {
			let map = ObjectBuilder::new()
				.insert("op", 3)
				.insert_object("d", |mut object| {
					object = object.insert("idle_since", serde_json::Value::Null);
					match game {
						Some(ref game) => object.insert_object("game", |object| object
							.insert("name", &game.name)
						),
						None => object.insert("game", serde_json::Value::Null),
					}
				})
				.unwrap();
			let json = match serde_json::to_string(&map) {
				Ok(json) => json,
				Err(e) => return warn!("Error encoding keepalive: {:?}", e)
			};
			match sender.send_message(&WsMessage::text(json)) {
				Ok(()) => {},
				Err(e) => return warn!("Error sending keepalive: {:?}", e)
			}
		}
	}
	let _ = sender.get_mut().shutdown(::std::net::Shutdown::Both);
}
<|endoftext|>


<commit_before>use gl;
use gl::types::*;
use types::{Color, Viewport, GLError};
use buffer::{ArrayBufferBinder, ElementArrayBufferBinder};
use program::{ProgramBinder, ProgramAttrib};
use framebuffer::FramebufferBinder;
use renderbuffer::RenderbufferBinder;
use texture_units::TextureUnits;

pub struct Context {
    pub array_buffer: ArrayBufferBinder,
    pub element_array_buffer: ElementArrayBufferBinder,
    pub program: ProgramBinder,
    pub framebuffer: FramebufferBinder,
    pub renderbuffer: RenderbufferBinder,
    pub tex_units: TextureUnits
}

impl Context {
    pub unsafe fn current_context() -> Self {
        Context {
            array_buffer: ArrayBufferBinder,
            element_array_buffer: ElementArrayBufferBinder,
            program: ProgramBinder,
            framebuffer: FramebufferBinder,
            renderbuffer: RenderbufferBinder,
            tex_units: TextureUnits::current()
        }
    }

    pub fn clear_color(&mut self, color: Color) {
        unsafe {
            gl::ClearColor(color.r, color.g, color.b, color.a);
        }
    }

    pub fn enable_vertex_attrib_array(&self, attrib: ProgramAttrib) {
        unsafe {
            gl::EnableVertexAttribArray(attrib.gl_index);
            dbg_gl_error! {
                GLError::InvalidValue => "`index` is >= GL_MAX_VERTEX_ATTRIBS",
                _ => "Unknown error"
            }
        }
    }

    pub fn viewport(&self, viewport: Viewport) {
        unsafe {
            gl::Viewport(viewport.x as GLint,
                         viewport.y as GLint,
                         viewport.width as GLsizei,
                         viewport.height as GLsizei);
            dbg_gl_sanity_check! {
                GLError::InvalidValue => "`width` or `height` is negative",
                _ => "Unknown error"
            }
        }
    }

    pub fn get_error() -> Option<GLError> {
        unsafe {
            match gl::GetError() {
                gl::INVALID_ENUM =>
                    Some(GLError::InvalidEnum),
                gl::INVALID_VALUE =>
                    Some(GLError::InvalidValue),
                gl::INVALID_OPERATION =>
                    Some(GLError::InvalidOperation),
                gl::INVALID_FRAMEBUFFER_OPERATION =>
                    Some(GLError::InvalidFramebufferOperation),
                gl::OUT_OF_MEMORY =>
                    Some(GLError::OutOfMemory),
                _ =>
                    None
            }
        }
    }
}

#[macro_export]
macro_rules! bind_array_buffer {
    ($gl:expr, $buffer:expr) => {
        $gl.array_buffer.bind($buffer)
    }
}

#[macro_export]
macro_rules! bind_element_array_buffer {
    ($gl:expr, $buffer:expr) => {
        $gl.element_array_buffer.bind($buffer)
    }
}

#[macro_export]
macro_rules! use_program {
    ($gl:expr, $program:expr) => {
        $gl.program.bind($program)
    }
}

#[macro_export]
macro_rules! bind_framebuffer {
    ($gl:expr, $fbo:expr) => {
        $gl.framebuffer.bind($fbo)
    }
}

#[macro_export]
macro_rules! current_framebuffer_binding {
    ($gl:expr) => {
        $gl.framebuffer.current_binding()
    }
}

#[macro_export]
macro_rules! bind_renderbuffer {
    ($gl:expr, $renderbuffer:expr) => {
        $gl.renderbuffer.bind($renderbuffer)
    }
}

// HACK: Workaround for issue described here:
// https://www.reddit.com/r/rust/comments/339yj3/tuple_indexing_in_a_macro/cqiyv4n
#[macro_export]
macro_rules! _glitter_expr {
    ($x:expr) => ($x)
}

#[macro_export]
macro_rules! active_texture {
    ($gl:expr, $idx:tt) => {
        _glitter_expr!($gl.tex_units.$idx.active())
    }
}

#[macro_export]
macro_rules! active_texture_n {
    ($gl:expr, $idx:expr) => {
        $gl.tex_units.nth_unit($idx).active()
    }
}
<commit_msg>Add `Context.enable()` and `Context.disable()`<commit_after>use gl;
use gl::types::*;
use types::{Color, Viewport, Capability, GLError};
use buffer::{ArrayBufferBinder, ElementArrayBufferBinder};
use program::{ProgramBinder, ProgramAttrib};
use framebuffer::FramebufferBinder;
use renderbuffer::RenderbufferBinder;
use texture_units::TextureUnits;

pub struct Context {
    pub array_buffer: ArrayBufferBinder,
    pub element_array_buffer: ElementArrayBufferBinder,
    pub program: ProgramBinder,
    pub framebuffer: FramebufferBinder,
    pub renderbuffer: RenderbufferBinder,
    pub tex_units: TextureUnits
}

impl Context {
    pub unsafe fn current_context() -> Self {
        Context {
            array_buffer: ArrayBufferBinder,
            element_array_buffer: ElementArrayBufferBinder,
            program: ProgramBinder,
            framebuffer: FramebufferBinder,
            renderbuffer: RenderbufferBinder,
            tex_units: TextureUnits::current()
        }
    }

    pub fn clear_color(&mut self, color: Color) {
        unsafe {
            gl::ClearColor(color.r, color.g, color.b, color.a);
        }
    }

    pub fn enable(&mut self, cap: Capability) {
        unsafe {
            gl::Enable(cap.gl_enum());
            dbg_gl_sanity_check! {
                GLError::InvalidEnum => "`cap` is not a valid OpenGL capability",
                _ => "Unknown error"
            }
        }
    }

    pub fn disable(&mut self, cap: Capability) {
        unsafe {
            gl::Disable(cap.gl_enum());
            dbg_gl_sanity_check! {
                GLError::InvalidEnum => "`cap` is not a valid OpenGL capability",
                _ => "Unknown error"
            }
        }
    }

    pub fn enable_vertex_attrib_array(&self, attrib: ProgramAttrib) {
        unsafe {
            gl::EnableVertexAttribArray(attrib.gl_index);
            dbg_gl_error! {
                GLError::InvalidValue => "`index` is >= GL_MAX_VERTEX_ATTRIBS",
                _ => "Unknown error"
            }
        }
    }

    pub fn viewport(&self, viewport: Viewport) {
        unsafe {
            gl::Viewport(viewport.x as GLint,
                         viewport.y as GLint,
                         viewport.width as GLsizei,
                         viewport.height as GLsizei);
            dbg_gl_sanity_check! {
                GLError::InvalidValue => "`width` or `height` is negative",
                _ => "Unknown error"
            }
        }
    }

    pub fn get_error() -> Option<GLError> {
        unsafe {
            match gl::GetError() {
                gl::INVALID_ENUM =>
                    Some(GLError::InvalidEnum),
                gl::INVALID_VALUE =>
                    Some(GLError::InvalidValue),
                gl::INVALID_OPERATION =>
                    Some(GLError::InvalidOperation),
                gl::INVALID_FRAMEBUFFER_OPERATION =>
                    Some(GLError::InvalidFramebufferOperation),
                gl::OUT_OF_MEMORY =>
                    Some(GLError::OutOfMemory),
                _ =>
                    None
            }
        }
    }
}

#[macro_export]
macro_rules! bind_array_buffer {
    ($gl:expr, $buffer:expr) => {
        $gl.array_buffer.bind($buffer)
    }
}

#[macro_export]
macro_rules! bind_element_array_buffer {
    ($gl:expr, $buffer:expr) => {
        $gl.element_array_buffer.bind($buffer)
    }
}

#[macro_export]
macro_rules! use_program {
    ($gl:expr, $program:expr) => {
        $gl.program.bind($program)
    }
}

#[macro_export]
macro_rules! bind_framebuffer {
    ($gl:expr, $fbo:expr) => {
        $gl.framebuffer.bind($fbo)
    }
}

#[macro_export]
macro_rules! current_framebuffer_binding {
    ($gl:expr) => {
        $gl.framebuffer.current_binding()
    }
}

#[macro_export]
macro_rules! bind_renderbuffer {
    ($gl:expr, $renderbuffer:expr) => {
        $gl.renderbuffer.bind($renderbuffer)
    }
}

// HACK: Workaround for issue described here:
// https://www.reddit.com/r/rust/comments/339yj3/tuple_indexing_in_a_macro/cqiyv4n
#[macro_export]
macro_rules! _glitter_expr {
    ($x:expr) => ($x)
}

#[macro_export]
macro_rules! active_texture {
    ($gl:expr, $idx:tt) => {
        _glitter_expr!($gl.tex_units.$idx.active())
    }
}

#[macro_export]
macro_rules! active_texture_n {
    ($gl:expr, $idx:expr) => {
        $gl.tex_units.nth_unit($idx).active()
    }
}
<|endoftext|>


<commit_before>extern crate cursive;

use cursive::Cursive;
use cursive::view::{Boxable, Identifiable};
use cursive::views::{LinearLayout, EditView, TextView, Dialog};

fn main() {
    let mut siv = Cursive::new();

    // Create a dialog with 2 edit fields, and a text view.
    // The text view indicates when the 2 fields content match.
    siv.add_layer(Dialog::around(LinearLayout::vertical()
            .child(EditView::new().on_edit(on_edit).with_id_mut("1"))
            .child(EditView::new().on_edit(on_edit).with_id_mut("2"))
            .child(TextView::new("match").with_id_mut("match"))
            .fixed_width(10))
        .button("Quit", Cursive::quit));

    siv.run();
}

// Compare the content of the two edit views,
// and update the TextView accordingly.
//
// We'll ignore the `content` and `cursor` arguments,
// and directly retrieve the content from the `Cursive` root.
fn on_edit(siv: &mut Cursive, _content: &str, _cursor: usize) {
    // Get handles for each view.
    let edit_1 = siv.find_id_mut::<EditView>("1").unwrap();
    let edit_2 = siv.find_id_mut::<EditView>("2").unwrap();
    let mut text = siv.find_id_mut::<TextView>("match").unwrap();

    // Directly compare references to edit_1 and edit_2.
    text.set_content(if edit_1.get_content() == edit_2.get_content() {
        "match"
    } else {
        "no match"
    });
}
<commit_msg>Edit refcell_view example to use `find_id`<commit_after>extern crate cursive;

use cursive::Cursive;
use cursive::view::{Boxable, Identifiable};
use cursive::views::{LinearLayout, EditView, TextView, Dialog};

fn main() {
    let mut siv = Cursive::new();

    // Create a dialog with 2 edit fields, and a text view.
    // The text view indicates when the 2 fields content match.
    siv.add_layer(Dialog::around(LinearLayout::vertical()
            .child(EditView::new().on_edit(on_edit).with_id_mut("1"))
            .child(EditView::new().on_edit(on_edit).with_id_mut("2"))
            .child(TextView::new("match").with_id_mut("match"))
            .fixed_width(10))
        .button("Quit", Cursive::quit));

    siv.run();
}

// Compare the content of the two edit views,
// and update the TextView accordingly.
//
// We'll ignore the `content` and `cursor` arguments,
// and directly retrieve the content from the `Cursive` root.
fn on_edit(siv: &mut Cursive, _content: &str, _cursor: usize) {
    // Get handles for each view.
    let edit_1 = siv.find_id_mut::<EditView>("1").unwrap();
    let edit_2 = siv.find_id_mut::<EditView>("2").unwrap();

    // Directly compare references to edit_1 and edit_2.
    let matches = edit_1.get_content() == edit_2.get_content();
    siv.find_id("match", |v: &mut TextView| v.set_content(if matches {
        "match"
    } else {
        "no match"
    }));
}
<|endoftext|>


<commit_before>extern crate rustbox;

use rusoto::{
    DefaultCredentialsProvider,
    Region
};
use rusoto::kinesis::Record;

use hyper::Client;
use self::rustbox::Key;
use super::kinesis::KinesisHelper;
use super::screen::Screen;

enum State {
    Root,
    End,
    StreamList(Vec<String>),
    ShardList(String, Vec<String>),
    FetchRecordList(String, Vec<Record>),  // stream_name, iterator_id
    RecordList(Vec<Record>),  // stream_name, iterator_id
}

pub struct Controller {
    screen: Screen
}

impl Controller {

    pub fn new() -> Controller {

        Controller {
            screen: Screen::new()
        }
    }

    pub fn run(&self) {

        let credentials = DefaultCredentialsProvider::new().unwrap();
        let client = Client::new();
        let kinesis_helper = KinesisHelper::new(client, credentials, Region::ApNortheast1);

        let mut state: State = State::Root;

        loop {

            state = match state {
                State::StreamList(streams) => {

                    self.screen.draw_strem_names(&streams);

                    match self.screen.poll_event() {
                        Key::Char('q') => State::End,
                        Key::Char(i) => {

                            let n = i.to_digit(10).unwrap();
                            let ref stream_name = streams[n as usize];

                            match kinesis_helper.describe_shards(stream_name) {
                                Ok(shards) => State::ShardList(stream_name.to_string(), shards),
                                Err(e) => State::Root,
                            }
                        }
                        _ => State::Root

                    }
                },
                State::ShardList(stream_name, shards) => {
                    self.screen.draw_shards(&shards);

                    match self.screen.poll_event() {
                        Key::Char('q') => State::End,
                        Key::Char(i) => {

                            let n = i.to_digit(10).unwrap();
                            let ref shard_id = shards[n as usize];

                            match kinesis_helper.get_shard_iterator(&stream_name, shard_id) {
                                Ok(shard_iterator) => State::FetchRecordList(shard_iterator, vec!()),
                                Err(e) => State::Root,
                            }
                        }
                        _ => State::Root
                    }
                },
                State::FetchRecordList(shard_iterator, records) => {

                    match kinesis_helper.get_records(&shard_iterator) {
                        Ok((r, i)) =>
                            match i {
                                Some(i) => State::FetchRecordList(i, records.push(r)),
                                None =>State::RecordList(r),
                            },
                        Err(e) => State::Root,
                    }

                },
                State::RecordList(records) => {

                    println!("{:?}", records);

                    match self.screen.poll_event() {
                        Key::Char('q') => State::End,
                        _ => State::Root
                    }

                },
                State::Root => {

                    self.screen.draw_help();

                    match self.screen.poll_event() {
                        Key::Char('q') => State::End,
                        Key::Char('l') => {
                            match kinesis_helper.list_streams() {
                                Ok(streams) => State::StreamList(streams),
                                Err(e) => {
                                    println!("{:?}", e);
                                    State::Root
                                }
                            }
                        },
                        _ => State::Root

                    }

                },
                State::End => {
                    break;
                }
            };

        }
    }
}

<commit_msg>Print record<commit_after>extern crate rustbox;

use rusoto::{
    DefaultCredentialsProvider,
    Region
};
use rusoto::kinesis::Record;

use hyper::Client;
use self::rustbox::Key;
use super::kinesis::KinesisHelper;
use super::screen::Screen;

enum State {
    Root,
    End,
    StreamList(Vec<String>),
    ShardList(String, Vec<String>),
    RecordList(String, Vec<Record>),  // stream_name, iterator_id
}

pub struct Controller {
    screen: Screen
}

impl Controller {

    pub fn new() -> Controller {

        Controller {
            screen: Screen::new()
        }
    }

    pub fn run(&self) {

        let credentials = DefaultCredentialsProvider::new().unwrap();
        let client = Client::new();
        let kinesis_helper = KinesisHelper::new(client, credentials, Region::ApNortheast1);

        let mut state: State = State::Root;

        loop {

            state = match state {
                State::StreamList(streams) => {

                    self.screen.draw_strem_names(&streams);

                    match self.screen.poll_event() {
                        Key::Char('q') => State::End,
                        Key::Char(i) => {

                            let n = i.to_digit(10).unwrap();
                            let ref stream_name = streams[n as usize];

                            match kinesis_helper.describe_shards(stream_name) {
                                Ok(shards) => State::ShardList(stream_name.to_string(), shards),
                                Err(e) => State::Root,
                            }
                        }
                        _ => State::Root

                    }
                },
                State::ShardList(stream_name, shards) => {

                    self.screen.draw_shards(&shards);

                    match self.screen.poll_event() {
                        Key::Char('q') => State::End,
                        Key::Char(i) => {

                            let n = i.to_digit(10).unwrap();
                            let ref shard_id = shards[n as usize];

                            match kinesis_helper.get_shard_iterator(&stream_name, shard_id) {
                                Ok(shard_iterator) => State::RecordList(shard_iterator, vec!()),
                                Err(e) => State::Root,
                            }
                        }
                        _ => State::Root
                    }
                },
                State::RecordList(shard_iterator, records) => {

                    if records.len() != 0 {
                        // println!("{:?}", records[0].data);
                        println!("{:?}", String::from_utf8_lossy(&records[0].data));
                    }

                    // let a = kinesis_helper.convert(&records[0]);

                    match kinesis_helper.get_records(&shard_iterator) {
                        Ok((r, i)) =>
                            match i {
                                Some(i) => State::RecordList(i, r),
                                None => {
                                    match self.screen.poll_event() {
                                        Key::Char('q') => State::End,
                                        _ => State::Root
                                    }
                                }
                            },
                        Err(e) => State::Root,
                    }

                },
                State::Root => {

                    self.screen.draw_help();

                    match self.screen.poll_event() {
                        Key::Char('q') => State::End,
                        Key::Char('l') => {
                            match kinesis_helper.list_streams() {
                                Ok(streams) => State::StreamList(streams),
                                Err(e) =>  State::Root
                            }
                        },
                        _ => State::Root

                    }

                },
                State::End => {
                    break;
                }
            };

        }
    }
}

<|endoftext|>


<commit_before>// This file is not compiled or tested! It is kept in this repository in case
// future libtock_runtime developers want to use it. To use this file, copy it
// into libtock_runtime's src/ directory and add mod start_prototype; to
// libtock_runtime's lib.rs.

// The `start` symbol must be written purely in assembly, because it has an ABI
// that the Rust compiler doesn't know (e.g. it does not expect the stack to be
// set up). One way to write a correct `start` implementation is to write it in
// Rust using the C ABI, compile that implementation, then tweak the assembly by
// hand. This is a Rust version of `start` for developers who are working on
// `start`.

#[repr(C)]
struct RtHeader {
    start: usize,
    initial_break: *mut (),
    stack_top: usize,
    data_size: usize,
    data_flash_start: *const u32,
    data_ram_start: *mut u32,
    bss_size: usize,
    bss_start: *mut u8,
}

#[link_section = ".start"]
#[no_mangle]
extern fn start_prototype(
    rt_header: &RtHeader,
    _memory_start: usize,
    _memory_len: usize,
    _app_break: usize,
) -> ! {
    use crate::TockSyscalls;
    use libtock_platform::RawSyscalls;

    let pc: usize;
    unsafe {
        #[cfg(target_arch = "arm")]
        asm!("mov {}, pc", lateout(reg) pc, options(nomem, nostack, preserves_flags));
        #[cfg(target_arch = "riscv32")]
        asm!("auipc {}, 0", lateout(reg) pc, options(nomem, nostack, preserves_flags));
    }
    if pc != rt_header.start {
        // Binary is in an incorrect location: report an error via
        // LowLevelDebug then exit.
        unsafe {
            TockSyscalls::syscall4::<2>([8 as *mut (), 1 as *mut (), 2 as *mut (), 0 as *mut ()]);
            TockSyscalls::syscall2::<6>([0 as *mut (), 0 as *mut ()]);
        }
    }

    // Set the app break.
    // TODO: Replace with Syscalls::memop_brk() when that is implemented.
    unsafe { TockSyscalls::syscall2::<5>([0 as *mut (), rt_header.initial_break]); }

    // Set the stack pointer.
    unsafe {
        #[cfg(target_arch = "arm")]
        asm!("mov sp, {}", in(reg) rt_header.stack_top, options(nomem, preserves_flags));
        #[cfg(target_arch = "riscv32")]
        asm!("mv sp, {}", in(reg) rt_header.stack_top, options(nomem, preserves_flags));
    }

    // Copy .data into place. Uses a manual loop rather than
    // `core::ptr::copy*()` to avoid relying on `memcopy` or `memmove`.
    let mut remaining = rt_header.data_size;
    let mut src = rt_header.data_flash_start;
    let mut dest = rt_header.data_ram_start;
    while remaining > 0 {
        unsafe {
            core::ptr::write(dest, *(src));
            src = src.add(1);
            dest = dest.add(1);
        }
        remaining -= 4;
    }

    // Zero .bss. Uses a manual loop and volatile write to avoid relying on
    // `memset`.
    let mut remaining = rt_header.bss_size;
    let mut dest = rt_header.bss_start;
    while remaining > 0 {
        unsafe {
            core::ptr::write_volatile(dest, 0);
            dest = dest.add(1);
        }
        remaining -= 1;
    }

    extern {
        fn rust_start() -> !;
    }

    unsafe { rust_start(); }
}
<commit_msg>start_prototype: replace hardcoded class IDs with constants, + rustfmt<commit_after>// This file is not compiled or tested! It is kept in this repository in case
// future libtock_runtime developers want to use it. To use this file, copy it
// into libtock_runtime's src/ directory and add mod start_prototype; to
// libtock_runtime's lib.rs.

// The `start` symbol must be written purely in assembly, because it has an ABI
// that the Rust compiler doesn't know (e.g. it does not expect the stack to be
// set up). One way to write a correct `start` implementation is to write it in
// Rust using the C ABI, compile that implementation, then tweak the assembly by
// hand. This is a Rust version of `start` for developers who are working on
// `start`.

#[repr(C)]
struct RtHeader {
    start: usize,
    initial_break: *mut (),
    stack_top: usize,
    data_size: usize,
    data_flash_start: *const u32,
    data_ram_start: *mut u32,
    bss_size: usize,
    bss_start: *mut u8,
}

mod class_id {
    pub const COMMAND: usize = 2;
    pub const MEMOP: usize = 5;
    pub const EXIT: usize = 6;
}

#[link_section = ".start"]
#[no_mangle]
extern "C" fn start_prototype(
    rt_header: &RtHeader,
    _memory_start: usize,
    _memory_len: usize,
    _app_break: usize,
) -> ! {
    use crate::TockSyscalls;
    use libtock_platform::RawSyscalls;

    let pc: usize;
    unsafe {
        #[cfg(target_arch = "arm")]
        asm!("mov {}, pc", lateout(reg) pc, options(nomem, nostack, preserves_flags));
        #[cfg(target_arch = "riscv32")]
        asm!("auipc {}, 0", lateout(reg) pc, options(nomem, nostack, preserves_flags));
    }
    if pc != rt_header.start {
        // Binary is in an incorrect location: report an error via
        // LowLevelDebug then exit.
        unsafe {
            TockSyscalls::syscall4::<class_id::COMMAND>([
                8 as *mut (),
                1 as *mut (),
                2 as *mut (),
                0 as *mut (),
            ]);
            TockSyscalls::syscall2::<class_id::EXIT>([0 as *mut (), 0 as *mut ()]);
        }
    }

    // Set the app break.
    // TODO: Replace with Syscalls::memop_brk() when that is implemented.
    unsafe {
        TockSyscalls::syscall2::<class_id::MEMOP>([0 as *mut (), rt_header.initial_break]);
    }

    // Set the stack pointer.
    unsafe {
        #[cfg(target_arch = "arm")]
        asm!("mov sp, {}", in(reg) rt_header.stack_top, options(nomem, preserves_flags));
        #[cfg(target_arch = "riscv32")]
        asm!("mv sp, {}", in(reg) rt_header.stack_top, options(nomem, preserves_flags));
    }

    // Copy .data into place. Uses a manual loop rather than
    // `core::ptr::copy*()` to avoid relying on `memcopy` or `memmove`.
    let mut remaining = rt_header.data_size;
    let mut src = rt_header.data_flash_start;
    let mut dest = rt_header.data_ram_start;
    while remaining > 0 {
        unsafe {
            core::ptr::write(dest, *(src));
            src = src.add(1);
            dest = dest.add(1);
        }
        remaining -= 4;
    }

    // Zero .bss. Uses a manual loop and volatile write to avoid relying on
    // `memset`.
    let mut remaining = rt_header.bss_size;
    let mut dest = rt_header.bss_start;
    while remaining > 0 {
        unsafe {
            core::ptr::write_volatile(dest, 0);
            dest = dest.add(1);
        }
        remaining -= 1;
    }

    extern "C" {
        fn rust_start() -> !;
    }

    unsafe {
        rust_start();
    }
}
<|endoftext|>


<commit_before>use std::slice::bytes::copy_memory;

static START_ADDR: u16 = 0x200;
static MEM_SIZE: uint = 4096;
pub static DISPLAY_WIDTH: uint = 64;
pub static DISPLAY_HEIGHT: uint = 32;

static fontset: [[u8, .. 5], .. 0x10] = [
    [0xF0, 0x90, 0x90, 0x90, 0xF0], // 0
    [0x20, 0x60, 0x20, 0x20, 0x70], // 1
    [0xF0, 0x10, 0xF0, 0x80, 0xF0], // 2
    [0xF0, 0x10, 0xF0, 0x10, 0xF0], // 3
    [0x90, 0x90, 0xF0, 0x10, 0x10], // 4
    [0xF0, 0x80, 0xF0, 0x10, 0xF0], // 5
    [0xF0, 0x80, 0xF0, 0x90, 0xF0], // 6
    [0xF0, 0x10, 0x20, 0x40, 0x40], // 7
    [0xF0, 0x90, 0xF0, 0x90, 0xF0], // 8
    [0xF0, 0x90, 0xF0, 0x10, 0xF0], // 9
    [0xF0, 0x90, 0xF0, 0x90, 0x90], // A
    [0xE0, 0x90, 0xE0, 0x90, 0xE0], // B
    [0xF0, 0x80, 0x80, 0x80, 0xF0], // C
    [0xE0, 0x90, 0x90, 0x90, 0xE0], // D
    [0xF0, 0x80, 0xF0, 0x80, 0xF0], // E
    [0xF0, 0x80, 0xF0, 0x80, 0x80], // F
];

pub struct Chip8 {
    ram: [u8, .. MEM_SIZE],
    v: [u8, .. 16],
    i: u16,
    delay_timer: u8,
    sound_timer: u8,
    pc: u16,
    sp: u8,
    stack: [u16, .. 16],
    display: [u8, .. DISPLAY_WIDTH * DISPLAY_HEIGHT]
}

impl Chip8 {

    pub fn new() -> Chip8 {
        Chip8 {
            ram: [0u8, .. MEM_SIZE],
            v: [0u8, .. 16],
            i: 0u16,
            delay_timer: 0u8,
            sound_timer: 0u8,
            pc: START_ADDR,
            sp: 0,
            stack: [0u16, .. 16],
            display: [0u8, .. DISPLAY_WIDTH * DISPLAY_HEIGHT]
        }
    }

    pub fn load_rom(&mut self, rom: &[u8]) {
        let len = self.ram.len();
        copy_memory(self.ram.mut_slice(START_ADDR as uint, len), rom);
    }

    pub fn get_display(&self) -> [u8, .. DISPLAY_WIDTH * DISPLAY_HEIGHT] {
        self.display
    }

    pub fn do_cycle(&mut self) {
        let ins = self.get_ins();
        self.pc += 2;

        match ins & 0xF000 {
            0x1000 => self.jump_addr(ins & 0x0FFF),
            0x3000 => self.skip_next_vx_eq(((ins & 0x0F00) >> 8) as uint, (ins & 0x00FF) as u8),
            0x5000 => match ins & 0x000F {
                0x0000 => self.skip_next_vx_eq_vy(((ins & 0x0F00) >> 8) as uint, ((ins & 0x00F0) >> 4) as uint),
                _ => fail!("Unknown 0x5XXX instruction: {:x}", ins)
            },
            0x6000 => self.set_vx_byte(((ins & 0x0F00) >> 8) as uint, (ins & 0x00FF) as u8),
            0x7000 => self.add_vx_byte(((ins & 0x0F00) >> 8) as uint, (ins & 0x00FF) as u8),
            0x8000 => match ins & 0x000F {
                0x0000 => self.set_vx_to_vy(((ins & 0x0F00) >> 8) as uint, ((ins & 0x00F0) >> 4) as uint),
                0x0004 => self.add_vx_vy(((ins & 0x0F00) >> 8) as uint, ((ins & 0x00F0) >> 4) as uint),
                0x0005 => self.sub_vx_vy(((ins & 0x0F00) >> 8) as uint, ((ins & 0x00F0) >> 4) as uint),
                _ => fail!("Unknown 0x8XXX instruction: {:x}", ins)
            },
            0x9000 => match ins & 0x000F {
                0x0000 => self.skip_next_vx_ne_vy(((ins & 0x0F00) >> 8) as uint, ((ins & 0x00F0) >> 4) as uint),
                _ => fail!("Unknown 0x9XXX instruction: {:x}", ins)
            },
            0xA000 => self.set_i(ins & 0x0FFF),
            0xC000 => self.set_vx_rand_and(((ins & 0x0F00) >> 8) as uint, (ins & 0x00FF) as u8),
            0xD000 => self.display_sprite(((ins & 0x0F00) >> 8) as uint, ((ins & 0x00F0) >> 4) as uint, ((ins & 0x000F)) as uint),
            0xF000 => match ins & 0x00FF {
                0x0055 => self.copy_v0_through_vx_to_mem(((ins & 0x0F00) >> 8) as uint),
                0x0065 => self.read_v0_through_vx_from_mem(((ins & 0x0F00) >> 8) as uint),
                0x001E => self.add_vx_to_i(((ins & 0x0F00) >> 8) as uint),
                _ => fail!("Unknown 0xFXXX instruction: {:x}", ins)
            },
            _ => fail!("Unknown instruction: {:x}", ins)
        }
    }

    fn get_ins(&self) -> u16 {
        let b1 = self.ram[self.pc as uint];
        let b2 = self.ram[(self.pc + 1) as uint];
        b1 as u16 << 8 | b2 as u16
    }

    fn set_i(&mut self, to: u16) {
        self.i = to;
    }

    fn set_vx_rand_and(&mut self, x: uint, to: u8) {
        use std::rand::{task_rng, Rng};
        let mut rgen = task_rng();
        self.v[x] = rgen.gen::<u8>() & to;
    }

    fn skip_next_vx_eq(&mut self, x: uint, to: u8) {
        if self.v[x] == to {
            self.pc += 2;
        }
    }

    fn display_sprite(&mut self, vx: uint, vy: uint, n: uint) {
        let x_off = self.v[vx] as uint;
        let y_off = self.v[vy] as uint * DISPLAY_WIDTH;
        let offset = x_off + y_off;

        for mut y in range(0u, n) {
            if y >= DISPLAY_HEIGHT {
                y = y - DISPLAY_HEIGHT;
            }
            let b = self.ram[self.i as uint + y];
            for mut x in range(0u, 8) {
                if x >= DISPLAY_WIDTH {
                    x = x - DISPLAY_WIDTH;
                }
                let idx = offset + (y * DISPLAY_WIDTH) + x;
                if idx < DISPLAY_WIDTH * DISPLAY_HEIGHT {
                    self.display[idx] ^= b & (0b10000000 >> x);
                } else {
                    println!("Warning: Out of bounds VRAM write: {}", idx);
                }
            }
        }
    }

    fn add_vx_byte(&mut self, x: uint, byte: u8) {
        self.v[x] += byte;
    }

    fn set_vx_byte(&mut self, x: uint, byte: u8) {
        self.v[x] = byte;
    }

    fn jump_addr(&mut self, addr: u16) {
        self.pc = addr;
    }

    fn set_vx_to_vy(&mut self, x: uint, y: uint) {
        self.v[x] = self.v[y];
    }

    fn add_vx_vy(&mut self, x: uint, y: uint) {
        let result = (self.v[x] + self.v[y]) as u16;
        self.v[0xF] = (result > 255) as u8;
        self.v[x] = result as u8;
    }

    fn sub_vx_vy(&mut self, x: uint, y: uint) {
        self.v[0xF] = (self.v[x] > self.v[y]) as u8;
        self.v[x] -= self.v[y];
    }

    fn add_vx_to_i(&mut self, x: uint) {
        self.i += x as u16;
    }

    fn copy_v0_through_vx_to_mem(&mut self, x: uint) {
        if x == 0 {
            return;
        }
        copy_memory(self.ram.mut_slice(self.i as uint, x), self.v.slice(0, x));
    }

    fn read_v0_through_vx_from_mem(&mut self, x: uint) {
        if x == 0 {
            return;
        }
        copy_memory(self.v.mut_slice(0, x), self.ram.slice(self.i as uint, x));
    }

    fn skip_next_vx_eq_vy(&mut self, x: uint, y: uint) {
        if self.v[x] == self.v[y] {
            self.pc += 2;
        }
    }

    fn skip_next_vx_ne_vy(&mut self, x: uint, y: uint) {
        if self.v[x] != self.v[y] {
            self.pc += 2;
        }
    }
}
<commit_msg>Use callback for drawing<commit_after>use std::slice::bytes::copy_memory;

static START_ADDR: u16 = 0x200;
static MEM_SIZE: uint = 4096;
pub static DISPLAY_WIDTH: uint = 64;
pub static DISPLAY_HEIGHT: uint = 32;

static fontset: [[u8, .. 5], .. 0x10] = [
    [0xF0, 0x90, 0x90, 0x90, 0xF0], // 0
    [0x20, 0x60, 0x20, 0x20, 0x70], // 1
    [0xF0, 0x10, 0xF0, 0x80, 0xF0], // 2
    [0xF0, 0x10, 0xF0, 0x10, 0xF0], // 3
    [0x90, 0x90, 0xF0, 0x10, 0x10], // 4
    [0xF0, 0x80, 0xF0, 0x10, 0xF0], // 5
    [0xF0, 0x80, 0xF0, 0x90, 0xF0], // 6
    [0xF0, 0x10, 0x20, 0x40, 0x40], // 7
    [0xF0, 0x90, 0xF0, 0x90, 0xF0], // 8
    [0xF0, 0x90, 0xF0, 0x10, 0xF0], // 9
    [0xF0, 0x90, 0xF0, 0x90, 0x90], // A
    [0xE0, 0x90, 0xE0, 0x90, 0xE0], // B
    [0xF0, 0x80, 0x80, 0x80, 0xF0], // C
    [0xE0, 0x90, 0x90, 0x90, 0xE0], // D
    [0xF0, 0x80, 0xF0, 0x80, 0xF0], // E
    [0xF0, 0x80, 0xF0, 0x80, 0x80], // F
];

type DrawCallback<'a> = |pixels: &[u8], w: uint, h: uint|: 'a;

pub struct Chip8<'a> {
    ram: [u8, .. MEM_SIZE],
    v: [u8, .. 16],
    i: u16,
    delay_timer: u8,
    sound_timer: u8,
    pc: u16,
    sp: u8,
    stack: [u16, .. 16],
    display: [u8, .. DISPLAY_WIDTH * DISPLAY_HEIGHT],
    draw_callback: DrawCallback<'a>
}

impl <'a> Chip8 <'a> {

    pub fn new(draw_callback: DrawCallback<'a>) -> Chip8<'a> {
        Chip8 {
            ram: [0u8, .. MEM_SIZE],
            v: [0u8, .. 16],
            i: 0u16,
            delay_timer: 0u8,
            sound_timer: 0u8,
            pc: START_ADDR,
            sp: 0,
            stack: [0u16, .. 16],
            display: [0u8, .. DISPLAY_WIDTH * DISPLAY_HEIGHT],
            draw_callback: draw_callback
        }
    }

    pub fn load_rom(&mut self, rom: &[u8]) {
        let len = self.ram.len();
        copy_memory(self.ram.mut_slice(START_ADDR as uint, len), rom);
    }

    pub fn get_display(&self) -> [u8, .. DISPLAY_WIDTH * DISPLAY_HEIGHT] {
        self.display
    }

    pub fn do_cycle(&mut self) {
        let ins = self.get_ins();
        self.pc += 2;

        match ins & 0xF000 {
            0x1000 => self.jump_addr(ins & 0x0FFF),
            0x3000 => self.skip_next_vx_eq(((ins & 0x0F00) >> 8) as uint, (ins & 0x00FF) as u8),
            0x5000 => match ins & 0x000F {
                0x0000 => self.skip_next_vx_eq_vy(((ins & 0x0F00) >> 8) as uint, ((ins & 0x00F0) >> 4) as uint),
                _ => fail!("Unknown 0x5XXX instruction: {:x}", ins)
            },
            0x6000 => self.set_vx_byte(((ins & 0x0F00) >> 8) as uint, (ins & 0x00FF) as u8),
            0x7000 => self.add_vx_byte(((ins & 0x0F00) >> 8) as uint, (ins & 0x00FF) as u8),
            0x8000 => match ins & 0x000F {
                0x0000 => self.set_vx_to_vy(((ins & 0x0F00) >> 8) as uint, ((ins & 0x00F0) >> 4) as uint),
                0x0004 => self.add_vx_vy(((ins & 0x0F00) >> 8) as uint, ((ins & 0x00F0) >> 4) as uint),
                0x0005 => self.sub_vx_vy(((ins & 0x0F00) >> 8) as uint, ((ins & 0x00F0) >> 4) as uint),
                _ => fail!("Unknown 0x8XXX instruction: {:x}", ins)
            },
            0x9000 => match ins & 0x000F {
                0x0000 => self.skip_next_vx_ne_vy(((ins & 0x0F00) >> 8) as uint, ((ins & 0x00F0) >> 4) as uint),
                _ => fail!("Unknown 0x9XXX instruction: {:x}", ins)
            },
            0xA000 => self.set_i(ins & 0x0FFF),
            0xC000 => self.set_vx_rand_and(((ins & 0x0F00) >> 8) as uint, (ins & 0x00FF) as u8),
            0xD000 => self.display_sprite(((ins & 0x0F00) >> 8) as uint, ((ins & 0x00F0) >> 4) as uint, ((ins & 0x000F)) as uint),
            0xF000 => match ins & 0x00FF {
                0x0055 => self.copy_v0_through_vx_to_mem(((ins & 0x0F00) >> 8) as uint),
                0x0065 => self.read_v0_through_vx_from_mem(((ins & 0x0F00) >> 8) as uint),
                0x001E => self.add_vx_to_i(((ins & 0x0F00) >> 8) as uint),
                _ => fail!("Unknown 0xFXXX instruction: {:x}", ins)
            },
            _ => fail!("Unknown instruction: {:x}", ins)
        }
    }

    fn get_ins(&self) -> u16 {
        let b1 = self.ram[self.pc as uint];
        let b2 = self.ram[(self.pc + 1) as uint];
        b1 as u16 << 8 | b2 as u16
    }

    fn set_i(&mut self, to: u16) {
        self.i = to;
    }

    fn set_vx_rand_and(&mut self, x: uint, to: u8) {
        use std::rand::{task_rng, Rng};
        let mut rgen = task_rng();
        self.v[x] = rgen.gen::<u8>() & to;
    }

    fn skip_next_vx_eq(&mut self, x: uint, to: u8) {
        if self.v[x] == to {
            self.pc += 2;
        }
    }

    fn display_sprite(&mut self, vx: uint, vy: uint, n: uint) {
        let x_off = self.v[vx] as uint;
        let y_off = self.v[vy] as uint * DISPLAY_WIDTH;
        let offset = x_off + y_off;

        for mut y in range(0u, n) {
            if y >= DISPLAY_HEIGHT {
                y = y - DISPLAY_HEIGHT;
            }
            let b = self.ram[self.i as uint + y];
            for mut x in range(0u, 8) {
                if x >= DISPLAY_WIDTH {
                    x = x - DISPLAY_WIDTH;
                }
                let idx = offset + (y * DISPLAY_WIDTH) + x;
                if idx < DISPLAY_WIDTH * DISPLAY_HEIGHT {
                    self.display[idx] ^= b & (0b10000000 >> x);
                } else {
                    println!("Warning: Out of bounds VRAM write: {}", idx);
                }
            }
        }

        (self.draw_callback)(self.display, DISPLAY_WIDTH, DISPLAY_HEIGHT);
    }

    fn add_vx_byte(&mut self, x: uint, byte: u8) {
        self.v[x] += byte;
    }

    fn set_vx_byte(&mut self, x: uint, byte: u8) {
        self.v[x] = byte;
    }

    fn jump_addr(&mut self, addr: u16) {
        self.pc = addr;
    }

    fn set_vx_to_vy(&mut self, x: uint, y: uint) {
        self.v[x] = self.v[y];
    }

    fn add_vx_vy(&mut self, x: uint, y: uint) {
        let result = (self.v[x] + self.v[y]) as u16;
        self.v[0xF] = (result > 255) as u8;
        self.v[x] = result as u8;
    }

    fn sub_vx_vy(&mut self, x: uint, y: uint) {
        self.v[0xF] = (self.v[x] > self.v[y]) as u8;
        self.v[x] -= self.v[y];
    }

    fn add_vx_to_i(&mut self, x: uint) {
        self.i += x as u16;
    }

    fn copy_v0_through_vx_to_mem(&mut self, x: uint) {
        if x == 0 {
            return;
        }
        copy_memory(self.ram.mut_slice(self.i as uint, x), self.v.slice(0, x));
    }

    fn read_v0_through_vx_from_mem(&mut self, x: uint) {
        if x == 0 {
            return;
        }
        copy_memory(self.v.mut_slice(0, x), self.ram.slice(self.i as uint, x));
    }

    fn skip_next_vx_eq_vy(&mut self, x: uint, y: uint) {
        if self.v[x] == self.v[y] {
            self.pc += 2;
        }
    }

    fn skip_next_vx_ne_vy(&mut self, x: uint, y: uint) {
        if self.v[x] != self.v[y] {
            self.pc += 2;
        }
    }
}
<|endoftext|>