<commit_before>// Copyright (c) 2016 Anatoly Ikorsky
//
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. All files in the project carrying such notice may not be copied,
// modified, or distributed except according to those terms.

//! ## mysql-async
//! Tokio based asynchronous MySql client library for rust programming language.
//!
//! ### Installation
//! Library hosted on [crates.io](https://crates.io/crates/mysql_async/).
//!
//! ```toml
//! [dependencies]
//! mysql_async = "<desired version>"
//! ```
//!
//! ### Example
//!
//! ```rust
//! # use mysql_async::{Result, test_misc::get_opts};
//! use mysql_async::prelude::*;
//! # use std::env;
//!
//! #[derive(Debug, PartialEq, Eq, Clone)]
//! struct Payment {
//!     customer_id: i32,
//!     amount: i32,
//!     account_name: Option<String>,
//! }
//!
//! #[tokio::main]
//! async fn main() -> Result<()> {
//!     let payments = vec![
//!         Payment { customer_id: 1, amount: 2, account_name: None },
//!         Payment { customer_id: 3, amount: 4, account_name: Some("foo".into()) },
//!         Payment { customer_id: 5, amount: 6, account_name: None },
//!         Payment { customer_id: 7, amount: 8, account_name: None },
//!         Payment { customer_id: 9, amount: 10, account_name: Some("bar".into()) },
//!     ];
//!
//!     let database_url = /* ... */
//!     # get_opts();
//!
//!     let pool = mysql_async::Pool::new(database_url);
//!     let mut conn = pool.get_conn().await?;
//!
//!     // Create temporary table
//!     conn.query_drop(
//!         r"CREATE TEMPORARY TABLE payment (
//!             customer_id int not null,
//!             amount int not null,
//!             account_name text
//!         )"
//!     ).await?;
//!
//!     // Save payments
//!     let params = payments.clone().into_iter().map(|payment| {
//!         params! {
//!             "customer_id" => payment.customer_id,
//!             "amount" => payment.amount,
//!             "account_name" => payment.account_name,
//!         }
//!     });
//!
//!     conn.exec_batch(
//!         r"INSERT INTO payment (customer_id, amount, account_name)
//!           VALUES (:customer_id, :amount, :account_name)",
//!         params,
//!     ).await?;
//!
//!     // Load payments from database. Type inference will work here.
//!     let loaded_payments = conn.exec_map(
//!         "SELECT customer_id, amount, account_name FROM payment",
//!         (),
//!         |(customer_id, amount, account_name)| Payment { customer_id, amount, account_name },
//!     ).await?;
//!
//!     // Dropped connection will go to the pool
//!     drop(conn);
//!
//!     // Pool must be disconnected explicitly because
//!     // it's an asynchronous operation.
//!     pool.disconnect().await?;
//!
//!     assert_eq!(loaded_payments, payments);
//!
//!     // the async fn returns Result, so
//!     Ok(())
//! }
//! ```

#![recursion_limit = "1024"]
#![cfg_attr(feature = "nightly", feature(test, const_fn))]

#[cfg(feature = "nightly")]
extern crate test;

pub use mysql_common::{chrono, constants as consts, params, time, uuid};

use std::sync::Arc;

mod buffer_pool;

#[macro_use]
mod macros;
mod conn;
mod connection_like;
/// Errors used in this crate
mod error;
mod io;
mod local_infile_handler;
mod opts;
mod query;
mod queryable;

type BoxFuture<'a, T> = futures_core::future::BoxFuture<'a, Result<T>>;

static BUFFER_POOL: once_cell::sync::Lazy<Arc<crate::buffer_pool::BufferPool>> =
    once_cell::sync::Lazy::new(|| Default::default());

#[doc(inline)]
pub use self::conn::{binlog_stream::BinlogStream, Conn};

#[doc(inline)]
pub use self::conn::pool::Pool;

#[doc(inline)]
pub use self::error::{DriverError, Error, IoError, ParseError, Result, ServerError, UrlError};

#[doc(inline)]
pub use self::query::QueryWithParams;

#[doc(inline)]
pub use self::queryable::transaction::IsolationLevel;

#[doc(inline)]
pub use self::opts::{
    Opts, OptsBuilder, PoolConstraints, PoolOpts, SslOpts, DEFAULT_INACTIVE_CONNECTION_TTL,
    DEFAULT_POOL_CONSTRAINTS, DEFAULT_STMT_CACHE_SIZE, DEFAULT_TTL_CHECK_INTERVAL,
};

#[doc(inline)]
pub use self::local_infile_handler::{builtin::WhiteListFsLocalInfileHandler, InfileHandlerFuture};

#[doc(inline)]
pub use mysql_common::packets::{
    binlog_request::BinlogRequest,
    session_state_change::{
        Gtids, Schema, SessionStateChange, SystemVariable, TransactionCharacteristics,
        TransactionState, Unsupported,
    },
    BinlogDumpFlags, Column, Interval, OkPacket, SessionStateInfo, Sid,
};

#[doc(inline)]
pub use mysql_common::proto::codec::Compression;

#[doc(inline)]
pub use mysql_common::row::Row;

#[doc(inline)]
pub use mysql_common::params::Params;

#[doc(inline)]
pub use mysql_common::value::Value;

#[doc(inline)]
pub use mysql_common::row::convert::{from_row, from_row_opt, FromRowError};

#[doc(inline)]
pub use mysql_common::value::convert::{from_value, from_value_opt, FromValueError};

#[doc(inline)]
pub use mysql_common::value::json::{Deserialized, Serialized};

#[doc(inline)]
pub use self::queryable::query_result::QueryResult;

#[doc(inline)]
pub use self::queryable::transaction::{Transaction, TxOpts};

#[doc(inline)]
pub use self::queryable::{BinaryProtocol, TextProtocol};

#[doc(inline)]
pub use self::queryable::stmt::Statement;

/// Futures used in this crate
pub mod futures {
    pub use crate::conn::pool::futures::{DisconnectPool, GetConn};
}

/// Traits used in this crate
pub mod prelude {
    #[doc(inline)]
    pub use crate::local_infile_handler::LocalInfileHandler;
    #[doc(inline)]
    pub use crate::query::{BatchQuery, Query, WithParams};
    #[doc(inline)]
    pub use crate::queryable::Queryable;
    #[doc(inline)]
    pub use mysql_common::row::convert::FromRow;
    #[doc(inline)]
    pub use mysql_common::value::convert::{ConvIr, FromValue, ToValue};

    /// Everything that is a statement.
    ///
    /// ```no_run
    /// # use std::{borrow::Cow, sync::Arc};
    /// # use mysql_async::{Statement, prelude::StatementLike};
    /// fn type_is_a_stmt<T: StatementLike>() {}
    ///
    /// type_is_a_stmt::<Cow<'_, str>>();
    /// type_is_a_stmt::<&'_ str>();
    /// type_is_a_stmt::<String>();
    /// type_is_a_stmt::<Box<str>>();
    /// type_is_a_stmt::<Arc<str>>();
    /// type_is_a_stmt::<Statement>();
    ///
    /// fn ref_to_a_clonable_stmt_is_also_a_stmt<T: StatementLike + Clone>() {
    ///     type_is_a_stmt::<&T>();
    /// }
    /// ```
    pub trait StatementLike: crate::queryable::stmt::StatementLike {}
    impl<T: crate::queryable::stmt::StatementLike> StatementLike for T {}

    /// Everything that is a connection.
    pub trait ToConnection<'a, 't: 'a>: crate::connection_like::ToConnection<'a, 't> {}
    // explicitly implemented because of rusdoc
    impl<'a> ToConnection<'a, 'static> for &'a crate::Pool {}
    impl ToConnection<'static, 'static> for crate::Conn {}
    impl<'a> ToConnection<'a, 'static> for &'a mut crate::Conn {}
    impl<'a, 't> ToConnection<'a, 't> for &'a mut crate::Transaction<'t> {}

    /// Trait for protocol markers [`crate::TextProtocol`] and [`crate::BinaryProtocol`].
    pub trait Protocol: crate::queryable::Protocol {}
    impl Protocol for crate::BinaryProtocol {}
    impl Protocol for crate::TextProtocol {}

    pub use mysql_common::params;
}

#[doc(hidden)]
pub mod test_misc {
    use lazy_static::lazy_static;

    use std::env;

    use crate::opts::{Opts, OptsBuilder, SslOpts};

    #[allow(dead_code)]
    fn error_should_implement_send_and_sync() {
        fn _dummy<T: Send + Sync>(_: T) {}
        _dummy(crate::error::Error::from("foo"));
    }

    lazy_static! {
        pub static ref DATABASE_URL: String = {
            if let Ok(url) = env::var("DATABASE_URL") {
                let opts = Opts::from_url(&url).expect("DATABASE_URL invalid");
                if opts
                    .db_name()
                    .expect("a database name is required")
                    .is_empty()
                {
                    panic!("database name is empty");
                }
                url
            } else {
                "mysql://root:password@127.0.0.1:3307/mysql".into()
            }
        };
    }

    pub fn get_opts() -> OptsBuilder {
        let mut builder = OptsBuilder::from_opts(&**DATABASE_URL);
        if test_ssl() {
            let ssl_opts = SslOpts::default()
                .with_danger_skip_domain_validation(true)
                .with_danger_accept_invalid_certs(true);
            builder = builder.prefer_socket(false).ssl_opts(ssl_opts);
        }
        if test_compression() {
            builder = builder.compression(crate::Compression::default());
        }
        builder
    }

    pub fn test_compression() -> bool {
        ["true", "1"].contains(&&*env::var("COMPRESS").unwrap_or_default())
    }

    pub fn test_ssl() -> bool {
        ["true", "1"].contains(&&*env::var("SSL").unwrap_or_default())
    }
}
<commit_msg>Reexport binlog related definitions from myc<commit_after>// Copyright (c) 2016 Anatoly Ikorsky
//
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. All files in the project carrying such notice may not be copied,
// modified, or distributed except according to those terms.

//! ## mysql-async
//! Tokio based asynchronous MySql client library for rust programming language.
//!
//! ### Installation
//! Library hosted on [crates.io](https://crates.io/crates/mysql_async/).
//!
//! ```toml
//! [dependencies]
//! mysql_async = "<desired version>"
//! ```
//!
//! ### Example
//!
//! ```rust
//! # use mysql_async::{Result, test_misc::get_opts};
//! use mysql_async::prelude::*;
//! # use std::env;
//!
//! #[derive(Debug, PartialEq, Eq, Clone)]
//! struct Payment {
//!     customer_id: i32,
//!     amount: i32,
//!     account_name: Option<String>,
//! }
//!
//! #[tokio::main]
//! async fn main() -> Result<()> {
//!     let payments = vec![
//!         Payment { customer_id: 1, amount: 2, account_name: None },
//!         Payment { customer_id: 3, amount: 4, account_name: Some("foo".into()) },
//!         Payment { customer_id: 5, amount: 6, account_name: None },
//!         Payment { customer_id: 7, amount: 8, account_name: None },
//!         Payment { customer_id: 9, amount: 10, account_name: Some("bar".into()) },
//!     ];
//!
//!     let database_url = /* ... */
//!     # get_opts();
//!
//!     let pool = mysql_async::Pool::new(database_url);
//!     let mut conn = pool.get_conn().await?;
//!
//!     // Create temporary table
//!     conn.query_drop(
//!         r"CREATE TEMPORARY TABLE payment (
//!             customer_id int not null,
//!             amount int not null,
//!             account_name text
//!         )"
//!     ).await?;
//!
//!     // Save payments
//!     let params = payments.clone().into_iter().map(|payment| {
//!         params! {
//!             "customer_id" => payment.customer_id,
//!             "amount" => payment.amount,
//!             "account_name" => payment.account_name,
//!         }
//!     });
//!
//!     conn.exec_batch(
//!         r"INSERT INTO payment (customer_id, amount, account_name)
//!           VALUES (:customer_id, :amount, :account_name)",
//!         params,
//!     ).await?;
//!
//!     // Load payments from database. Type inference will work here.
//!     let loaded_payments = conn.exec_map(
//!         "SELECT customer_id, amount, account_name FROM payment",
//!         (),
//!         |(customer_id, amount, account_name)| Payment { customer_id, amount, account_name },
//!     ).await?;
//!
//!     // Dropped connection will go to the pool
//!     drop(conn);
//!
//!     // Pool must be disconnected explicitly because
//!     // it's an asynchronous operation.
//!     pool.disconnect().await?;
//!
//!     assert_eq!(loaded_payments, payments);
//!
//!     // the async fn returns Result, so
//!     Ok(())
//! }
//! ```

#![recursion_limit = "1024"]
#![cfg_attr(feature = "nightly", feature(test, const_fn))]

#[cfg(feature = "nightly")]
extern crate test;

pub use mysql_common::{chrono, constants as consts, params, time, uuid};

use std::sync::Arc;

mod buffer_pool;

#[macro_use]
mod macros;
mod conn;
mod connection_like;
/// Errors used in this crate
mod error;
mod io;
mod local_infile_handler;
mod opts;
mod query;
mod queryable;

type BoxFuture<'a, T> = futures_core::future::BoxFuture<'a, Result<T>>;

static BUFFER_POOL: once_cell::sync::Lazy<Arc<crate::buffer_pool::BufferPool>> =
    once_cell::sync::Lazy::new(|| Default::default());

#[doc(inline)]
pub use self::conn::{binlog_stream::BinlogStream, Conn};

#[doc(inline)]
pub use self::conn::pool::Pool;

#[doc(inline)]
pub use self::error::{DriverError, Error, IoError, ParseError, Result, ServerError, UrlError};

#[doc(inline)]
pub use self::query::QueryWithParams;

#[doc(inline)]
pub use self::queryable::transaction::IsolationLevel;

#[doc(inline)]
pub use self::opts::{
    Opts, OptsBuilder, PoolConstraints, PoolOpts, SslOpts, DEFAULT_INACTIVE_CONNECTION_TTL,
    DEFAULT_POOL_CONSTRAINTS, DEFAULT_STMT_CACHE_SIZE, DEFAULT_TTL_CHECK_INTERVAL,
};

#[doc(inline)]
pub use self::local_infile_handler::{builtin::WhiteListFsLocalInfileHandler, InfileHandlerFuture};

#[doc(inline)]
pub use mysql_common::packets::{
    binlog_request::BinlogRequest,
    session_state_change::{
        Gtids, Schema, SessionStateChange, SystemVariable, TransactionCharacteristics,
        TransactionState, Unsupported,
    },
    BinlogDumpFlags, Column, Interval, OkPacket, SessionStateInfo, Sid,
};

pub mod binlog {
    #[doc(inline)]
    pub use mysql_common::binlog::consts::*;

    #[doc(inline)]
    pub use mysql_common::binlog::{events, jsonb, jsondiff, row, value};
}

#[doc(inline)]
pub use mysql_common::proto::codec::Compression;

#[doc(inline)]
pub use mysql_common::row::Row;

#[doc(inline)]
pub use mysql_common::params::Params;

#[doc(inline)]
pub use mysql_common::value::Value;

#[doc(inline)]
pub use mysql_common::row::convert::{from_row, from_row_opt, FromRowError};

#[doc(inline)]
pub use mysql_common::value::convert::{from_value, from_value_opt, FromValueError};

#[doc(inline)]
pub use mysql_common::value::json::{Deserialized, Serialized};

#[doc(inline)]
pub use self::queryable::query_result::QueryResult;

#[doc(inline)]
pub use self::queryable::transaction::{Transaction, TxOpts};

#[doc(inline)]
pub use self::queryable::{BinaryProtocol, TextProtocol};

#[doc(inline)]
pub use self::queryable::stmt::Statement;

/// Futures used in this crate
pub mod futures {
    pub use crate::conn::pool::futures::{DisconnectPool, GetConn};
}

/// Traits used in this crate
pub mod prelude {
    #[doc(inline)]
    pub use crate::local_infile_handler::LocalInfileHandler;
    #[doc(inline)]
    pub use crate::query::{BatchQuery, Query, WithParams};
    #[doc(inline)]
    pub use crate::queryable::Queryable;
    #[doc(inline)]
    pub use mysql_common::row::convert::FromRow;
    #[doc(inline)]
    pub use mysql_common::value::convert::{ConvIr, FromValue, ToValue};

    /// Everything that is a statement.
    ///
    /// ```no_run
    /// # use std::{borrow::Cow, sync::Arc};
    /// # use mysql_async::{Statement, prelude::StatementLike};
    /// fn type_is_a_stmt<T: StatementLike>() {}
    ///
    /// type_is_a_stmt::<Cow<'_, str>>();
    /// type_is_a_stmt::<&'_ str>();
    /// type_is_a_stmt::<String>();
    /// type_is_a_stmt::<Box<str>>();
    /// type_is_a_stmt::<Arc<str>>();
    /// type_is_a_stmt::<Statement>();
    ///
    /// fn ref_to_a_clonable_stmt_is_also_a_stmt<T: StatementLike + Clone>() {
    ///     type_is_a_stmt::<&T>();
    /// }
    /// ```
    pub trait StatementLike: crate::queryable::stmt::StatementLike {}
    impl<T: crate::queryable::stmt::StatementLike> StatementLike for T {}

    /// Everything that is a connection.
    pub trait ToConnection<'a, 't: 'a>: crate::connection_like::ToConnection<'a, 't> {}
    // explicitly implemented because of rusdoc
    impl<'a> ToConnection<'a, 'static> for &'a crate::Pool {}
    impl ToConnection<'static, 'static> for crate::Conn {}
    impl<'a> ToConnection<'a, 'static> for &'a mut crate::Conn {}
    impl<'a, 't> ToConnection<'a, 't> for &'a mut crate::Transaction<'t> {}

    /// Trait for protocol markers [`crate::TextProtocol`] and [`crate::BinaryProtocol`].
    pub trait Protocol: crate::queryable::Protocol {}
    impl Protocol for crate::BinaryProtocol {}
    impl Protocol for crate::TextProtocol {}

    pub use mysql_common::params;
}

#[doc(hidden)]
pub mod test_misc {
    use lazy_static::lazy_static;

    use std::env;

    use crate::opts::{Opts, OptsBuilder, SslOpts};

    #[allow(dead_code)]
    fn error_should_implement_send_and_sync() {
        fn _dummy<T: Send + Sync>(_: T) {}
        _dummy(crate::error::Error::from("foo"));
    }

    lazy_static! {
        pub static ref DATABASE_URL: String = {
            if let Ok(url) = env::var("DATABASE_URL") {
                let opts = Opts::from_url(&url).expect("DATABASE_URL invalid");
                if opts
                    .db_name()
                    .expect("a database name is required")
                    .is_empty()
                {
                    panic!("database name is empty");
                }
                url
            } else {
                "mysql://root:password@127.0.0.1:3307/mysql".into()
            }
        };
    }

    pub fn get_opts() -> OptsBuilder {
        let mut builder = OptsBuilder::from_opts(&**DATABASE_URL);
        if test_ssl() {
            let ssl_opts = SslOpts::default()
                .with_danger_skip_domain_validation(true)
                .with_danger_accept_invalid_certs(true);
            builder = builder.prefer_socket(false).ssl_opts(ssl_opts);
        }
        if test_compression() {
            builder = builder.compression(crate::Compression::default());
        }
        builder
    }

    pub fn test_compression() -> bool {
        ["true", "1"].contains(&&*env::var("COMPRESS").unwrap_or_default())
    }

    pub fn test_ssl() -> bool {
        ["true", "1"].contains(&&*env::var("SSL").unwrap_or_default())
    }
}
<|endoftext|>


<commit_before>#![feature(associated_consts)]
#![feature(box_patterns)]
#![feature(box_syntax)]
#![feature(question_mark)]
//#![feature(plugin)]

//#![plugin(clippy)]

extern crate jsrs_common;

#[macro_use] extern crate matches;

pub mod alloc;
mod scope;
mod test_utils;

use std::cell::RefCell;
use std::collections::hash_map::HashMap;
use std::rc::Rc;

use jsrs_common::ast::Exp;
use jsrs_common::backend::Backend;
use jsrs_common::types::js_var::{JsPtrEnum, JsVar};
use jsrs_common::types::binding::{Binding, UniqueBinding};

use alloc::AllocBox;
use jsrs_common::gc_error::{GcError, Result};
use scope::{LookupError, Scope, ScopeTag};

pub struct ScopeManager {
    scopes: Vec<Scope>,
    closures: HashMap<UniqueBinding, Scope>,
    pub alloc_box: Rc<RefCell<AllocBox>>,
}

impl ScopeManager {
    fn new(alloc_box: Rc<RefCell<AllocBox>>) -> ScopeManager {
        ScopeManager {
            scopes: vec![Scope::new(ScopeTag::Call, &alloc_box)],
            closures: HashMap::new(),
            alloc_box: alloc_box,
        }
    }

    #[allow(dead_code)]
    #[inline]
    fn curr_scope(&self) -> &Scope {
        self.scopes.last().expect("Tried to access current scope, but none existed")
    }

    #[inline]
    fn curr_scope_mut(&mut self) -> &mut Scope {
        self.scopes.last_mut().expect("Tried to access current scope, but none existed")
    }

    #[inline]
    fn global_scope(&self) -> &Scope {
        self.scopes.get(0).expect("Tried to access global scope, but none existed")
    }

    #[inline]
    fn global_scope_mut(&mut self) -> &mut Scope {
        self.scopes.get_mut(0).expect("Tried to access global scope, but none existed")
    }

    pub fn push_closure_scope(&mut self, closure: &UniqueBinding) -> Result<()> {
        let closure_scope = self.closures.remove(closure).ok_or(GcError::Scope)?;
        self.scopes.push(closure_scope);
        Ok(())
    }

    pub fn push_scope(&mut self, exp: &Exp) {
        let tag = match *exp {
            Exp::Call(..) => ScopeTag::Call,
            _ => ScopeTag::Block,
        };
        self.scopes.push(Scope::new(tag, &self.alloc_box));
    }

    pub fn pop_scope(&mut self, returning_closure: Option<UniqueBinding>, gc_yield: bool) -> Result<()> {
        if let Some(mut scope) = self.scopes.pop() {
            // Clean up the dying scope's stack and take ownership of its heap-allocated data for
            // later collection
            if self.scopes.is_empty() {
                // The global scope was popped and the program is ending.
                scope.trigger_gc();
                return Err(GcError::Scope);
            }
            if let Some(unique) = returning_closure {
                let mut closure_scope = Scope::new(ScopeTag::Closure(unique.clone()), &self.alloc_box);
                scope.transfer_stack(&mut closure_scope, true)?;
                self.closures.insert(unique, closure_scope);
            } else {
                if !matches!(scope.tag, ScopeTag::Closure(_)) {
                    scope.transfer_stack(self.curr_scope_mut(), false)?
                }
            }
            // Potentially trigger the garbage collector
            if gc_yield {
                self.curr_scope_mut().trigger_gc();
            }
            if let ScopeTag::Closure(unique) = scope.tag.clone() {
                self.closures.insert(unique.clone(), scope);
            }
            Ok(())
        } else {
            Err(GcError::Scope)
        }
    }

    pub fn rename_closure(&mut self, old: &UniqueBinding, new: &UniqueBinding) -> bool {
        if self.closures.contains_key(old) {
            let mut scope = self.closures.remove(old).unwrap();
            scope.tag = ScopeTag::Closure(new.clone());
            self.closures.insert(new.clone(), scope);
            true
        } else {
            false
        }
    }
}

impl Backend for ScopeManager {
    fn alloc(&mut self, var: JsVar, ptr: Option<JsPtrEnum>) -> Result<Binding> {
        let binding = var.binding.clone();
        let is_allocated = self.alloc_box.borrow().is_allocated(&var.unique);

        // If the ptr is already allocated in the heap, just push it onto the stack
        if is_allocated && ptr.is_some() {
            self.curr_scope_mut().bind_var(var);
        } else {
            self.curr_scope_mut().push_var(var, ptr)?;
        }
        Ok(binding)
    }

    /// Try to load the variable behind a binding
    fn load(&mut self, bnd: &Binding) -> Result<(JsVar, Option<JsPtrEnum>)> {
        let lookup = || {
            for scope in self.scopes.iter().rev() {
                match scope.get_var_copy(bnd) {
                    Ok(v) => { return Ok(v); },
                    Err(LookupError::FnBoundary) => {
                        return Err(GcError::Load(bnd.clone()));
                    },
                    Err(LookupError::CheckParent) => {},
                    Err(LookupError::Unreachable) => unreachable!(),
                }
            }
            Err(GcError::Load(bnd.clone()))
        };
        match lookup() {
            Ok(v) => Ok(v),
            Err(GcError::Load(bnd)) =>
                self.global_scope().get_var_copy(&bnd)
                .map_err(|_| GcError::Load(bnd.clone())),
            _ => unreachable!(),
        }
    }

    fn store(&mut self, var: JsVar, ptr: Option<JsPtrEnum>) -> Result<()> {
        let res = self.curr_scope_mut().update_var(var, ptr);
        if let Err(GcError::Store(var, ptr)) = res {
            self.global_scope_mut().update_var(var, ptr)
        } else {
            res
        }
    }
}

pub fn init_gc() -> ScopeManager {
    let alloc_box = Rc::new(RefCell::new(AllocBox::new()));
    ScopeManager::new(alloc_box)
}

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

    use jsrs_common::ast::Exp;
    use jsrs_common::backend::Backend;
    use jsrs_common::types::js_var::{JsKey, JsPtrEnum, JsType, JsVar};
    use jsrs_common::types::binding::Binding;

    use jsrs_common::gc_error::GcError;
    use test_utils;

    #[test]
    fn test_push_closure_scope() {
        let alloc_box = test_utils::make_alloc_box();
        let mut mgr = ScopeManager::new(alloc_box);
        mgr.push_scope(&Exp::Undefined);
        let (fn_var, fn_ptr) = test_utils::make_fn(&None, &Vec::new());
        let unique = fn_var.unique.clone();
        mgr.alloc(fn_var, Some(fn_ptr)).unwrap();
        mgr.pop_scope(Some(unique.clone()), false).unwrap();
        assert_eq!(mgr.closures.len(), 1);
        mgr.push_closure_scope(&unique).unwrap();
        assert_eq!(mgr.closures.len(), 0);
        mgr.pop_scope(None, false).unwrap();
        assert_eq!(mgr.closures.len(), 1);
    }

    #[test]
    fn test_pop_scope() {
        let alloc_box = test_utils::make_alloc_box();
        let mut mgr = ScopeManager::new(alloc_box);
        mgr.push_scope(&Exp::Undefined);
        assert_eq!(mgr.scopes.len(), 2);
        mgr.pop_scope(None, false).unwrap();
        assert_eq!(mgr.scopes.len(), 1);
    }

    #[test]
    fn test_pop_scope_fail() {
        let alloc_box = test_utils::make_alloc_box();
        let mut mgr = ScopeManager::new(alloc_box);
        let res = mgr.pop_scope(None, false);
        assert!(res.is_err());
        assert!(matches!(res, Err(GcError::Scope)));
    }

    #[test]
    fn test_alloc() {
        let alloc_box = test_utils::make_alloc_box();
        let mut mgr = ScopeManager::new(alloc_box);
        mgr.alloc(test_utils::make_num(1.), None).unwrap();
        mgr.push_scope(&Exp::Undefined);
        mgr.alloc(test_utils::make_num(2.), None).unwrap();
        assert!(mgr.alloc_box.borrow().is_empty());
    }

    #[test]
    fn test_load() {
        let alloc_box = test_utils::make_alloc_box();
        let mut mgr = ScopeManager::new(alloc_box);
        let x = test_utils::make_num(1.);
        let x_bnd = mgr.alloc(x, None).unwrap();
        let load = mgr.load(&x_bnd);
        assert!(load.is_ok());
        let load = load.unwrap();
        match load.0.t {
            JsType::JsNum(n) => assert!(f64::abs(n - 1.) < 0.0001),
            _ => unreachable!(),
        }
        assert!(load.1.is_none());
    }

    #[test]
    fn test_load_fail() {
        let alloc_box = test_utils::make_alloc_box();
        let mut mgr = ScopeManager::new(alloc_box);
        let bnd = Binding::new("".to_owned());
        let res = mgr.load(&bnd);
        assert!(res.is_err());
        assert!(matches!(res, Err(GcError::Load(_))));
        if let Err(GcError::Load(res_bnd)) = res {
            assert_eq!(bnd, res_bnd);
        }
    }

    #[test]
    fn test_store() {
        let alloc_box = test_utils::make_alloc_box();
        let mut mgr = ScopeManager::new(alloc_box,);
        mgr.push_scope(&Exp::Undefined);

        let x = test_utils::make_num(1.);
        let x_bnd = mgr.alloc(x, None).unwrap();

        let (mut var, _) = mgr.load(&x_bnd).unwrap();
        var.t = JsType::JsNum(2.);

        assert!(mgr.store(var, None).is_ok());
    }

    #[test]
    fn test_store_fail() {
        let alloc_box = test_utils::make_alloc_box();
        let mut mgr = ScopeManager::new(alloc_box);
        let x = test_utils::make_num(1.);
        assert!(mgr.store(x, None).is_err());
    }

    #[test]
    fn test_load_from_parent_scope_across_fn_boundary() {
        let heap = test_utils::make_alloc_box();
        let mut mgr = ScopeManager::new(heap);

        // Avoids having just the global scope available
        mgr.push_scope(&Exp::Call(box Exp::Undefined, vec![]));
        let (x, x_ptr) = test_utils::make_str("x");
        let x_bnd = mgr.alloc(x, Some(x_ptr)).unwrap();

        mgr.push_scope(&Exp::Call(box Exp::Undefined, vec![]));
        let copy = mgr.load(&x_bnd);

        assert!(copy.is_err());
        assert!(matches!(copy, Err(GcError::Load(_))));
    }

    #[test]
    fn test_load_from_parent_scope_no_fn_call() {
        let heap = test_utils::make_alloc_box();
        let mut mgr = ScopeManager::new(heap);

        // Avoids having just the global scope available
        mgr.push_scope(&Exp::Call(box Exp::Undefined, vec![]));
        let (x, x_ptr) = test_utils::make_str("x");
        let x_bnd = mgr.alloc(x, Some(x_ptr)).unwrap();

        mgr.push_scope(&Exp::Undefined);
        let copy = mgr.load(&x_bnd);

        assert!(copy.is_ok());
        let (var_copy, ptr_copy) = copy.unwrap();
        assert!(matches!(var_copy, JsVar { t: JsType::JsPtr(_), .. }));
        assert!(ptr_copy.is_some());
    }

    #[test]
    fn test_transfer_stack_with_yield() {
        let heap = test_utils::make_alloc_box();
        let mut mgr = ScopeManager::new(heap);
        // Make some scopes
        mgr.push_scope(&Exp::Undefined);
        {
            // Push a child scope
            mgr.push_scope(&Exp::Undefined);
            // Allocate some non-root variables (numbers)
            mgr.alloc(test_utils::make_num(0.), None).unwrap();
            mgr.alloc(test_utils::make_num(1.), None).unwrap();
            mgr.alloc(test_utils::make_num(2.), None).unwrap();

            // Make a string to put into an object
            // (so it's heap-allocated and we can lose its ref from the object)
            let (var, ptr) = test_utils::make_str("test");

            // Create an obj of { true: 1.0, false: heap("test") }
            let kvs = vec![(JsKey::JsSym("true".to_string()),
                            test_utils::make_num(1.), None),
                           (JsKey::JsSym("false".to_string()),
                            var, Some(ptr))];
            let (var, ptr) = test_utils::make_obj(kvs, mgr.alloc_box.clone());

            // Push the obj into the current scope
            let bnd = mgr.alloc(var, Some(ptr)).unwrap();
            // The heap should now have 2 things in it: an object and a string
            assert_eq!(mgr.alloc_box.borrow().len(), 2);

            // Replace the string in the object with something else so it's no longer live
            let copy = mgr.load(&bnd);
            let (var_cp, mut ptr_cp) = copy.unwrap();
            let key = JsKey::JsSym("false".to_string());
            match *&mut ptr_cp {
                Some(JsPtrEnum::JsObj(ref mut obj)) => {
                    obj.add_key(key, test_utils::make_num(-1.), None, &mut *(mgr.alloc_box.borrow_mut()));
                },
                _ => unreachable!()
            }
            mgr.store(var_cp, ptr_cp).unwrap();
            // The heap should still have 2 things in it: an object and a string
            assert_eq!(mgr.alloc_box.borrow().len(), 2);

            // Kill the current scope & give its refs to the parent,
            // allowing the GC to kick in beforehand.
            mgr.pop_scope(None, true).unwrap();
        }
        // The object we created above should still exist
        assert_eq!(mgr.curr_scope().len(), 1);
        // But the string it had allocated shouldn't, since we leaked it into the void
        assert_eq!(mgr.alloc_box.borrow().len(), 1);
    }
}
<commit_msg>add `get_allocator` method to Backend implementation for ScopeManager<commit_after>#![feature(associated_consts)]
#![feature(box_patterns)]
#![feature(box_syntax)]
#![feature(question_mark)]
//#![feature(plugin)]

//#![plugin(clippy)]

extern crate jsrs_common;

#[macro_use] extern crate matches;

pub mod alloc;
mod scope;
mod test_utils;

use std::cell::RefCell;
use std::collections::hash_map::HashMap;
use std::rc::Rc;

use jsrs_common::ast::Exp;
use jsrs_common::backend::Backend;
use jsrs_common::types::allocator::Allocator;
use jsrs_common::types::js_var::{JsPtrEnum, JsVar};
use jsrs_common::types::binding::{Binding, UniqueBinding};

use alloc::AllocBox;
use jsrs_common::gc_error::{GcError, Result};
use scope::{LookupError, Scope, ScopeTag};

pub struct ScopeManager {
    scopes: Vec<Scope>,
    closures: HashMap<UniqueBinding, Scope>,
    pub alloc_box: Rc<RefCell<AllocBox>>,
}

impl ScopeManager {
    fn new(alloc_box: Rc<RefCell<AllocBox>>) -> ScopeManager {
        ScopeManager {
            scopes: vec![Scope::new(ScopeTag::Call, &alloc_box)],
            closures: HashMap::new(),
            alloc_box: alloc_box,
        }
    }

    #[allow(dead_code)]
    #[inline]
    fn curr_scope(&self) -> &Scope {
        self.scopes.last().expect("Tried to access current scope, but none existed")
    }

    #[inline]
    fn curr_scope_mut(&mut self) -> &mut Scope {
        self.scopes.last_mut().expect("Tried to access current scope, but none existed")
    }

    #[inline]
    fn global_scope(&self) -> &Scope {
        self.scopes.get(0).expect("Tried to access global scope, but none existed")
    }

    #[inline]
    fn global_scope_mut(&mut self) -> &mut Scope {
        self.scopes.get_mut(0).expect("Tried to access global scope, but none existed")
    }

    pub fn push_closure_scope(&mut self, closure: &UniqueBinding) -> Result<()> {
        let closure_scope = self.closures.remove(closure).ok_or(GcError::Scope)?;
        self.scopes.push(closure_scope);
        Ok(())
    }

    pub fn push_scope(&mut self, exp: &Exp) {
        let tag = match *exp {
            Exp::Call(..) => ScopeTag::Call,
            _ => ScopeTag::Block,
        };
        self.scopes.push(Scope::new(tag, &self.alloc_box));
    }

    pub fn pop_scope(&mut self, returning_closure: Option<UniqueBinding>, gc_yield: bool) -> Result<()> {
        if let Some(mut scope) = self.scopes.pop() {
            // Clean up the dying scope's stack and take ownership of its heap-allocated data for
            // later collection
            if self.scopes.is_empty() {
                // The global scope was popped and the program is ending.
                scope.trigger_gc();
                return Err(GcError::Scope);
            }
            if let Some(unique) = returning_closure {
                let mut closure_scope = Scope::new(ScopeTag::Closure(unique.clone()), &self.alloc_box);
                scope.transfer_stack(&mut closure_scope, true)?;
                self.closures.insert(unique, closure_scope);
            } else {
                if !matches!(scope.tag, ScopeTag::Closure(_)) {
                    scope.transfer_stack(self.curr_scope_mut(), false)?
                }
            }
            // Potentially trigger the garbage collector
            if gc_yield {
                self.curr_scope_mut().trigger_gc();
            }
            if let ScopeTag::Closure(unique) = scope.tag.clone() {
                self.closures.insert(unique.clone(), scope);
            }
            Ok(())
        } else {
            Err(GcError::Scope)
        }
    }

    pub fn rename_closure(&mut self, old: &UniqueBinding, new: &UniqueBinding) -> bool {
        if self.closures.contains_key(old) {
            let mut scope = self.closures.remove(old).unwrap();
            scope.tag = ScopeTag::Closure(new.clone());
            self.closures.insert(new.clone(), scope);
            true
        } else {
            false
        }
    }
}

impl Backend for ScopeManager {
    fn alloc(&mut self, var: JsVar, ptr: Option<JsPtrEnum>) -> Result<Binding> {
        let binding = var.binding.clone();
        let is_allocated = self.alloc_box.borrow().is_allocated(&var.unique);

        // If the ptr is already allocated in the heap, just push it onto the stack
        if is_allocated && ptr.is_some() {
            self.curr_scope_mut().bind_var(var);
        } else {
            self.curr_scope_mut().push_var(var, ptr)?;
        }
        Ok(binding)
    }

    fn get_allocator(&self) -> Rc<RefCell<Allocator<Error=GcError>>> {
        self.alloc_box.clone()
    }

    /// Try to load the variable behind a binding
    fn load(&mut self, bnd: &Binding) -> Result<(JsVar, Option<JsPtrEnum>)> {
        let lookup = || {
            for scope in self.scopes.iter().rev() {
                match scope.get_var_copy(bnd) {
                    Ok(v) => { return Ok(v); },
                    Err(LookupError::FnBoundary) => {
                        return Err(GcError::Load(bnd.clone()));
                    },
                    Err(LookupError::CheckParent) => {},
                    Err(LookupError::Unreachable) => unreachable!(),
                }
            }
            Err(GcError::Load(bnd.clone()))
        };
        match lookup() {
            Ok(v) => Ok(v),
            Err(GcError::Load(bnd)) =>
                self.global_scope().get_var_copy(&bnd)
                .map_err(|_| GcError::Load(bnd.clone())),
            _ => unreachable!(),
        }
    }

    fn store(&mut self, var: JsVar, ptr: Option<JsPtrEnum>) -> Result<()> {
        let res = self.curr_scope_mut().update_var(var, ptr);
        if let Err(GcError::Store(var, ptr)) = res {
            self.global_scope_mut().update_var(var, ptr)
        } else {
            res
        }
    }
}

pub fn init_gc() -> ScopeManager {
    let alloc_box = Rc::new(RefCell::new(AllocBox::new()));
    ScopeManager::new(alloc_box)
}

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

    use jsrs_common::ast::Exp;
    use jsrs_common::backend::Backend;
    use jsrs_common::types::js_var::{JsKey, JsPtrEnum, JsType, JsVar};
    use jsrs_common::types::binding::Binding;

    use jsrs_common::gc_error::GcError;
    use test_utils;

    #[test]
    fn test_push_closure_scope() {
        let alloc_box = test_utils::make_alloc_box();
        let mut mgr = ScopeManager::new(alloc_box);
        mgr.push_scope(&Exp::Undefined);
        let (fn_var, fn_ptr) = test_utils::make_fn(&None, &Vec::new());
        let unique = fn_var.unique.clone();
        mgr.alloc(fn_var, Some(fn_ptr)).unwrap();
        mgr.pop_scope(Some(unique.clone()), false).unwrap();
        assert_eq!(mgr.closures.len(), 1);
        mgr.push_closure_scope(&unique).unwrap();
        assert_eq!(mgr.closures.len(), 0);
        mgr.pop_scope(None, false).unwrap();
        assert_eq!(mgr.closures.len(), 1);
    }

    #[test]
    fn test_pop_scope() {
        let alloc_box = test_utils::make_alloc_box();
        let mut mgr = ScopeManager::new(alloc_box);
        mgr.push_scope(&Exp::Undefined);
        assert_eq!(mgr.scopes.len(), 2);
        mgr.pop_scope(None, false).unwrap();
        assert_eq!(mgr.scopes.len(), 1);
    }

    #[test]
    fn test_pop_scope_fail() {
        let alloc_box = test_utils::make_alloc_box();
        let mut mgr = ScopeManager::new(alloc_box);
        let res = mgr.pop_scope(None, false);
        assert!(res.is_err());
        assert!(matches!(res, Err(GcError::Scope)));
    }

    #[test]
    fn test_alloc() {
        let alloc_box = test_utils::make_alloc_box();
        let mut mgr = ScopeManager::new(alloc_box);
        mgr.alloc(test_utils::make_num(1.), None).unwrap();
        mgr.push_scope(&Exp::Undefined);
        mgr.alloc(test_utils::make_num(2.), None).unwrap();
        assert!(mgr.alloc_box.borrow().is_empty());
    }

    #[test]
    fn test_load() {
        let alloc_box = test_utils::make_alloc_box();
        let mut mgr = ScopeManager::new(alloc_box);
        let x = test_utils::make_num(1.);
        let x_bnd = mgr.alloc(x, None).unwrap();
        let load = mgr.load(&x_bnd);
        assert!(load.is_ok());
        let load = load.unwrap();
        match load.0.t {
            JsType::JsNum(n) => assert!(f64::abs(n - 1.) < 0.0001),
            _ => unreachable!(),
        }
        assert!(load.1.is_none());
    }

    #[test]
    fn test_load_fail() {
        let alloc_box = test_utils::make_alloc_box();
        let mut mgr = ScopeManager::new(alloc_box);
        let bnd = Binding::new("".to_owned());
        let res = mgr.load(&bnd);
        assert!(res.is_err());
        assert!(matches!(res, Err(GcError::Load(_))));
        if let Err(GcError::Load(res_bnd)) = res {
            assert_eq!(bnd, res_bnd);
        }
    }

    #[test]
    fn test_store() {
        let alloc_box = test_utils::make_alloc_box();
        let mut mgr = ScopeManager::new(alloc_box,);
        mgr.push_scope(&Exp::Undefined);

        let x = test_utils::make_num(1.);
        let x_bnd = mgr.alloc(x, None).unwrap();

        let (mut var, _) = mgr.load(&x_bnd).unwrap();
        var.t = JsType::JsNum(2.);

        assert!(mgr.store(var, None).is_ok());
    }

    #[test]
    fn test_store_fail() {
        let alloc_box = test_utils::make_alloc_box();
        let mut mgr = ScopeManager::new(alloc_box);
        let x = test_utils::make_num(1.);
        assert!(mgr.store(x, None).is_err());
    }

    #[test]
    fn test_load_from_parent_scope_across_fn_boundary() {
        let heap = test_utils::make_alloc_box();
        let mut mgr = ScopeManager::new(heap);

        // Avoids having just the global scope available
        mgr.push_scope(&Exp::Call(box Exp::Undefined, vec![]));
        let (x, x_ptr) = test_utils::make_str("x");
        let x_bnd = mgr.alloc(x, Some(x_ptr)).unwrap();

        mgr.push_scope(&Exp::Call(box Exp::Undefined, vec![]));
        let copy = mgr.load(&x_bnd);

        assert!(copy.is_err());
        assert!(matches!(copy, Err(GcError::Load(_))));
    }

    #[test]
    fn test_load_from_parent_scope_no_fn_call() {
        let heap = test_utils::make_alloc_box();
        let mut mgr = ScopeManager::new(heap);

        // Avoids having just the global scope available
        mgr.push_scope(&Exp::Call(box Exp::Undefined, vec![]));
        let (x, x_ptr) = test_utils::make_str("x");
        let x_bnd = mgr.alloc(x, Some(x_ptr)).unwrap();

        mgr.push_scope(&Exp::Undefined);
        let copy = mgr.load(&x_bnd);

        assert!(copy.is_ok());
        let (var_copy, ptr_copy) = copy.unwrap();
        assert!(matches!(var_copy, JsVar { t: JsType::JsPtr(_), .. }));
        assert!(ptr_copy.is_some());
    }

    #[test]
    fn test_transfer_stack_with_yield() {
        let heap = test_utils::make_alloc_box();
        let mut mgr = ScopeManager::new(heap);
        // Make some scopes
        mgr.push_scope(&Exp::Undefined);
        {
            // Push a child scope
            mgr.push_scope(&Exp::Undefined);
            // Allocate some non-root variables (numbers)
            mgr.alloc(test_utils::make_num(0.), None).unwrap();
            mgr.alloc(test_utils::make_num(1.), None).unwrap();
            mgr.alloc(test_utils::make_num(2.), None).unwrap();

            // Make a string to put into an object
            // (so it's heap-allocated and we can lose its ref from the object)
            let (var, ptr) = test_utils::make_str("test");

            // Create an obj of { true: 1.0, false: heap("test") }
            let kvs = vec![(JsKey::JsSym("true".to_string()),
                            test_utils::make_num(1.), None),
                           (JsKey::JsSym("false".to_string()),
                            var, Some(ptr))];
            let (var, ptr) = test_utils::make_obj(kvs, mgr.alloc_box.clone());

            // Push the obj into the current scope
            let bnd = mgr.alloc(var, Some(ptr)).unwrap();
            // The heap should now have 2 things in it: an object and a string
            assert_eq!(mgr.alloc_box.borrow().len(), 2);

            // Replace the string in the object with something else so it's no longer live
            let copy = mgr.load(&bnd);
            let (var_cp, mut ptr_cp) = copy.unwrap();
            let key = JsKey::JsSym("false".to_string());
            match *&mut ptr_cp {
                Some(JsPtrEnum::JsObj(ref mut obj)) => {
                    obj.add_key(key, test_utils::make_num(-1.), None, &mut *(mgr.alloc_box.borrow_mut()));
                },
                _ => unreachable!()
            }
            mgr.store(var_cp, ptr_cp).unwrap();
            // The heap should still have 2 things in it: an object and a string
            assert_eq!(mgr.alloc_box.borrow().len(), 2);

            // Kill the current scope & give its refs to the parent,
            // allowing the GC to kick in beforehand.
            mgr.pop_scope(None, true).unwrap();
        }
        // The object we created above should still exist
        assert_eq!(mgr.curr_scope().len(), 1);
        // But the string it had allocated shouldn't, since we leaked it into the void
        assert_eq!(mgr.alloc_box.borrow().len(), 1);
    }
}
<|endoftext|>


<commit_before>use core::ops::Mul;
use {One, CheckedMul};

/// Binary operator for raising a value to a power.
pub trait Pow<RHS> {
    /// The result after applying the operator.
    type Output;

    /// Returns `self` to the power `rhs`.
    ///
    /// # Examples
    ///
    /// ```
    /// use num_traits::Pow;
    /// assert_eq!(Pow::pow(10u32, 2u32), 100);
    /// ```
    fn pow(self, rhs: RHS) -> Self::Output;
}

macro_rules! pow_impl {
    ($t:ty, $rhs:ty, $method:ident) => {
        impl Pow<$rhs> for $t {
            type Output = $t;

            #[inline]
            fn pow(self, rhs: $rhs) -> $t {
                <$t>::$method(self, rhs)
            }
        }

        impl<'a> Pow<&'a $rhs> for $t {
            type Output = $t;
            #[inline]
            fn pow(self, rhs: &'a $rhs) -> $t {
                <$t>::$method(self, *rhs)
            }
        }

        impl<'a> Pow<$rhs> for &'a $t {
            type Output = $t;
            #[inline]
            fn pow(self, rhs: $rhs) -> $t {
                <$t>::$method(*self, rhs)
            }
        }

        impl<'a, 'b> Pow<&'a $rhs> for &'b $t {
            type Output = $t;
            #[inline]
            fn pow(self, rhs: &'a $rhs) -> $t {
                <$t>::$method(*self, *rhs)
            }
        }
    }
}

pow_impl!(u8, u32, pow);
pow_impl!(i8, u32, pow);
pow_impl!(u16, u32, pow);
pow_impl!(i16, u32, pow);
pow_impl!(u32, u32, pow);
pow_impl!(i32, u32, pow);
pow_impl!(u64, u32, pow);
pow_impl!(i64, u32, pow);
pow_impl!(usize, u32, pow);
pow_impl!(isize, u32, pow);

#[cfg(feature = "std")]
mod float_impls {
    use super::Pow;

    pow_impl!(f32, i32, powi);
    pow_impl!(f64, i32, powi);
    pow_impl!(f32, f32, powf);
    pow_impl!(f64, f64, powf);
}

/// Raises a value to the power of exp, using exponentiation by squaring.
///
/// # Example
///
/// ```rust
/// use num_traits::pow;
///
/// assert_eq!(pow(2i8, 4), 16);
/// assert_eq!(pow(6u8, 3), 216);
/// ```
#[inline]
pub fn pow<T: Clone + One + Mul<T, Output = T>>(mut base: T, mut exp: usize) -> T {
    if exp == 0 { return T::one() }

    while exp & 1 == 0 {
        base = base.clone() * base;
        exp >>= 1;
    }
    if exp == 1 { return base }

    let mut acc = base.clone();
    while exp > 1 {
        exp >>= 1;
        base = base.clone() * base;
        if exp & 1 == 1 {
            acc = acc * base.clone();
        }
    }
    acc
}

/// Raises a value to the power of exp, returning `None` if an overflow occurred.
///
/// Otherwise same as the `pow` function.
///
/// # Example
///
/// ```rust
/// use num_traits::checked_pow;
///
/// assert_eq!(checked_pow(2i8, 4), Some(16));
/// assert_eq!(checked_pow(7i8, 8), None);
/// assert_eq!(checked_pow(7u32, 8), Some(5_764_801));
/// ```
#[inline]
pub fn checked_pow<T: Clone + One + CheckedMul>(mut base: T, mut exp: usize) -> Option<T> {
    if exp == 0 { return Some(T::one()) }

    macro_rules! optry {
        ( $ expr : expr ) => {
            if let Some(val) = $expr { val } else { return None }
        }
    }

    while exp & 1 == 0 {
        base = optry!(base.checked_mul(&base));
        exp >>= 1;
    }
    if exp == 1 { return Some(base) }

    let mut acc = base.clone();
    while exp > 1 {
        exp >>= 1;
        base = optry!(base.checked_mul(&base));
        if exp & 1 == 1 {
            acc = optry!(acc.checked_mul(&base));
        }
    }
    Some(acc)
}
<commit_msg>Add more Pow implementations.<commit_after>use core::ops::Mul;
use core::num::Wrapping;
use {One, CheckedMul};

/// Binary operator for raising a value to a power.
pub trait Pow<RHS> {
    /// The result after applying the operator.
    type Output;

    /// Returns `self` to the power `rhs`.
    ///
    /// # Examples
    ///
    /// ```
    /// use num_traits::Pow;
    /// assert_eq!(Pow::pow(10u32, 2u32), 100);
    /// ```
    fn pow(self, rhs: RHS) -> Self::Output;
}

macro_rules! pow_impl {
    ($t:ty) => {
        pow_impl!($t, u8);
        pow_impl!($t, u16);
        pow_impl!($t, u32);
        pow_impl!($t, u64);
        pow_impl!($t, usize);
    };
    ($t:ty, $rhs:ty) => {
        pow_impl!($t, $rhs, |x, p| pow(x, p as usize));
    };
    ($t:ty, $rhs:ty, $method:expr) => {
        impl Pow<$rhs> for $t {
            type Output = $t;
            #[inline]
            fn pow(self, rhs: $rhs) -> $t {
                ($method)(self, rhs)
            }
        }

        impl<'a> Pow<&'a $rhs> for $t {
            type Output = $t;
            #[inline]
            fn pow(self, rhs: &'a $rhs) -> $t {
                ($method)(self, *rhs)
            }
        }

        impl<'a> Pow<$rhs> for &'a $t {
            type Output = $t;
            #[inline]
            fn pow(self, rhs: $rhs) -> $t {
                ($method)(*self, rhs)
            }
        }

        impl<'a, 'b> Pow<&'a $rhs> for &'b $t {
            type Output = $t;
            #[inline]
            fn pow(self, rhs: &'a $rhs) -> $t {
                ($method)(*self, *rhs)
            }
        }
    };
}

pow_impl!(u8, u8, |x: u8, p| x.pow(p as u32));
pow_impl!(u8, u16, |x: u8, p| x.pow(p as u32));
pow_impl!(u8, u32, u8::pow);
pow_impl!(u8, usize);
pow_impl!(i8, u8, |x: i8, p| x.pow(p as u32));
pow_impl!(i8, u16, |x: i8, p| x.pow(p as u32));
pow_impl!(i8, u32, i8::pow);
pow_impl!(i8, usize);
pow_impl!(u16, u8, |x: u16, p| x.pow(p as u32));
pow_impl!(u16, u16, |x: u16, p| x.pow(p as u32));
pow_impl!(u16, u32, u16::pow);
pow_impl!(u16, usize);
pow_impl!(i16, u8, |x: i16, p| x.pow(p as u32));
pow_impl!(i16, u16, |x: i16, p| x.pow(p as u32));
pow_impl!(i16, u32, i16::pow);
pow_impl!(i16, usize);
pow_impl!(u32, u8, |x: u32, p| x.pow(p as u32));
pow_impl!(u32, u16, |x: u32, p| x.pow(p as u32));
pow_impl!(u32, u32, u32::pow);
pow_impl!(u32, usize);
pow_impl!(i32, u8, |x: i32, p| x.pow(p as u32));
pow_impl!(i32, u16, |x: i32, p| x.pow(p as u32));
pow_impl!(i32, u32, i32::pow);
pow_impl!(i32, usize);
pow_impl!(u64, u8, |x: u64, p| x.pow(p as u32));
pow_impl!(u64, u16, |x: u64, p| x.pow(p as u32));
pow_impl!(u64, u32, u64::pow);
pow_impl!(u64, usize);
pow_impl!(i64, u8, |x: i64, p| x.pow(p as u32));
pow_impl!(i64, u16, |x: i64, p| x.pow(p as u32));
pow_impl!(i64, u32, i64::pow);
pow_impl!(i64, usize);
pow_impl!(usize, u8, |x: usize, p| x.pow(p as u32));
pow_impl!(usize, u16, |x: usize, p| x.pow(p as u32));
pow_impl!(usize, u32, usize::pow);
pow_impl!(usize, usize);
pow_impl!(isize, u8, |x: isize, p| x.pow(p as u32));
pow_impl!(isize, u16, |x: isize, p| x.pow(p as u32));
pow_impl!(isize, u32, isize::pow);
pow_impl!(isize, usize);
pow_impl!(Wrapping<u8>);
pow_impl!(Wrapping<i8>);
pow_impl!(Wrapping<u16>);
pow_impl!(Wrapping<i16>);
pow_impl!(Wrapping<u32>);
pow_impl!(Wrapping<i32>);
pow_impl!(Wrapping<u64>);
pow_impl!(Wrapping<i64>);
pow_impl!(Wrapping<usize>);
pow_impl!(Wrapping<isize>);

#[cfg(feature = "std")]
mod float_impls {
    use super::Pow;

    pow_impl!(f32, i8, |x: f32, p| x.powi(p as i32));
    pow_impl!(f32, u8, |x: f32, p| x.powi(p as i32));
    pow_impl!(f32, i16, |x: f32, p| x.powi(p as i32));
    pow_impl!(f32, u16, |x: f32, p| x.powi(p as i32));
    pow_impl!(f32, i32, f32::powi);
    pow_impl!(f64, i8, |x: f64, p| x.powi(p as i32));
    pow_impl!(f64, u8, |x: f64, p| x.powi(p as i32));
    pow_impl!(f64, i16, |x: f64, p| x.powi(p as i32));
    pow_impl!(f64, u16, |x: f64, p| x.powi(p as i32));
    pow_impl!(f64, i32, f64::powi);
    pow_impl!(f32, f32, f32::powf);
    pow_impl!(f64, f32, |x: f64, p| x.powf(p as f64));
    pow_impl!(f64, f64, f64::powf);
}

/// Raises a value to the power of exp, using exponentiation by squaring.
///
/// # Example
///
/// ```rust
/// use num_traits::pow;
///
/// assert_eq!(pow(2i8, 4), 16);
/// assert_eq!(pow(6u8, 3), 216);
/// ```
#[inline]
pub fn pow<T: Clone + One + Mul<T, Output = T>>(mut base: T, mut exp: usize) -> T {
    if exp == 0 { return T::one() }

    while exp & 1 == 0 {
        base = base.clone() * base;
        exp >>= 1;
    }
    if exp == 1 { return base }

    let mut acc = base.clone();
    while exp > 1 {
        exp >>= 1;
        base = base.clone() * base;
        if exp & 1 == 1 {
            acc = acc * base.clone();
        }
    }
    acc
}

/// Raises a value to the power of exp, returning `None` if an overflow occurred.
///
/// Otherwise same as the `pow` function.
///
/// # Example
///
/// ```rust
/// use num_traits::checked_pow;
///
/// assert_eq!(checked_pow(2i8, 4), Some(16));
/// assert_eq!(checked_pow(7i8, 8), None);
/// assert_eq!(checked_pow(7u32, 8), Some(5_764_801));
/// ```
#[inline]
pub fn checked_pow<T: Clone + One + CheckedMul>(mut base: T, mut exp: usize) -> Option<T> {
    if exp == 0 { return Some(T::one()) }

    macro_rules! optry {
        ( $ expr : expr ) => {
            if let Some(val) = $expr { val } else { return None }
        }
    }

    while exp & 1 == 0 {
        base = optry!(base.checked_mul(&base));
        exp >>= 1;
    }
    if exp == 1 { return Some(base) }

    let mut acc = base.clone();
    while exp > 1 {
        exp >>= 1;
        base = optry!(base.checked_mul(&base));
        if exp & 1 == 1 {
            acc = optry!(acc.checked_mul(&base));
        }
    }
    Some(acc)
}
<|endoftext|>


<commit_before>// Copyright (c) 2017-2018 Rene van der Meer
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

//! Interface for the SPI peripherals.
//!
//! RPPAL provides access to the available SPI peripherals by using the `/dev/spidevB.C`
//! devices, where B points to an SPI bus (0, 1, 2), and C to a Chip Enable pin (0, 1, 2).
//!
//! The Raspberry Pi's GPIO header exposes several SPI buses. SPI0 is available
//! on all Raspberry Pi models. SPI1 is available on models with a 40-pin
//! header. SPI2 is only available on the Compute and Compute 3.
//!
//! SPI0 is disabled by default. You can enable it by running
//! `sudo raspi-config`, or by manually adding `dtparam=spi=on` to
//! `/boot/config.txt`. The associated pins are listed below.
//!
//! * MISO: BCM GPIO 9 (physical pin 21)
//! * MOSI: BCM GPIO 10 (physical pin 19)
//! * SCLK: BCM GPIO 11 (physical pin 23)
//! * SS: CE0: BCM GPIO 8 (physical pin 24), CE1: BCM GPIO 7 (physical pin 26)
//!
//! SPI1 is an auxiliary peripheral that's referred to as mini SPI. According
//! to the documentation, using higher clock speeds on SPI1 requires additional
//! CPU time compared to SPI0, caused by shallow FIFOs and no DMA support. SPI1
//! can be enabled by adding `dtoverlay=spi1-3cs` to `/boot/config.txt`. Replace
//! `3cs` with either `2cs` or `1cs` if you only require 2 or 1 Slave Select pins.
//! The associated pins are listed below.
//!
//! * MISO: BCM GPIO 19 (physical pin 35)
//! * MOSI: BCM GPIO 20 (physical pin 38)
//! * SCLK: BCM GPIO 21 (physical pin 40)
//! * SS: CE0: BCM GPIO 18 (physical pin 12), CE1: BCM GPIO 17 (physical pin 11), CE2: BCM GPIO 16 (physical pin 36)
//!
//! SPI2 shares the same characteristics as SPI1. It can be enabled by adding
//! `dtoverlay=spi2-3cs` to `/boot/config.txt`. Replace `3cs` with either `2cs` or
//! `1cs` if you only require 2 or 1 Slave Select pins. The associated pins are
//! listed below.
//!
//! * MISO: BCM GPIO 40
//! * MOSI: BCM GPIO 41
//! * SCLK: BCM GPIO 42
//! * SS: CE0: BCM GPIO 43, CE1: BCM GPIO 44, CE2: BCM GPIO 45
//!
//! The GPIO pin numbers mentioned above are part of the default configuration. Some of
//! their functionality can be moved to different pins. Read `/boot/overlays/README`
//! for more information.

use std::fs::{File, OpenOptions};
use std::io;
use std::io::{Read, Write};
use std::os::unix::io::AsRawFd;
use std::result;

use nix;

pub use nix::errno::Errno;

quick_error! {
    #[derive(Debug)]
/// Errors that can occur when accessing the SPI peripherals.
    pub enum Error {
/// IO error.
        Io(err: io::Error) { description(err.description()) from() }
/// System call error.
        Sys(errno: Errno) { display("System call error: {}", errno) }
/// Invalid path.
        InvalidPath {}
/// Invalid Utf8.
        InvalidUtf8 {}
/// Unsupported operation.
        UnsupportedOperation {}
    }
}

impl From<nix::Error> for Error {
    fn from(err: nix::Error) -> Error {
        match err {
            nix::Error::Sys(errno) => Error::Sys(errno),
            nix::Error::InvalidPath => Error::InvalidPath,
            nix::Error::InvalidUtf8 => Error::InvalidUtf8,
            nix::Error::UnsupportedOperation => Error::UnsupportedOperation,
        }
    }
}

/// Result type returned from methods that can have `spi::Error`s.
pub type Result<T> = result::Result<T, Error>;

mod ioctl {
    const SPI_IOC_MAGIC: u8 = b'k';

    const SPI_IOC_TYPE_MESSAGE: u8 = 0;
    const SPI_IOC_TYPE_LSB_FIRST: u8 = 2;
    const SPI_IOC_TYPE_BITS_PER_WORD: u8 = 3;
    const SPI_IOC_TYPE_MAX_SPEED_HZ: u8 = 4;
    const SPI_IOC_TYPE_MODE32: u8 = 5;

    #[derive(Debug, PartialEq, Copy, Clone)]
    #[repr(C)]
    pub struct TransferSegment {
        // Pointer to transmit buffer, or 0.
        tx_buf: u64,
        // Pointer to receive buffer, or 0.
        rx_buf: u64,
        // Number of bytes to transfer in this segment.
        len: u32,
        // Set a different clock speed for this segment. 0 = default.
        speed_hz: u32,
        // Add a delay before the (optional) SS change and the next segment.
        delay_usecs: u16,
        // Not used, since we only support 8 bits. 0 = default.
        bits_per_word: u8,
        // Set to 1 to briefly set SS inactive between this segment and the next, and keep SS active after the final segment.
        cs_change: u8,
        // Used for dual/quad SPI.
        tx_nbits: u8,
        // Used for dual/quad SPI.
        rx_nbits: u8,
        // Padding. Set to 0 for forward compatibility.
        pad: u16,
    }

    // TODO: Doublecheck cs_change behavior after the final transfer segment. Some SPI drivers may have
    // interpreted the documentation incorrectly.

    impl TransferSegment {
        pub fn new(read_buffer: Option<&mut [u8]>, write_buffer: Option<&[u8]>) -> TransferSegment {
            // Len will contain the length of the shortest of the supplied buffers
            let mut len: u32 = 0;

            let tx_buf = if let Some(buffer) = write_buffer {
                len = buffer.len() as u32;
                buffer.as_ptr() as u64
            } else {
                0
            };

            let rx_buf = if let Some(buffer) = read_buffer {
                if len > buffer.len() as u32 {
                    len = buffer.len() as u32;
                }
                buffer.as_ptr() as u64
            } else {
                0
            };

            TransferSegment {
                tx_buf,
                rx_buf,
                len,
                speed_hz: 0,
                delay_usecs: 0,
                bits_per_word: 0,
                cs_change: 0,
                tx_nbits: 0,
                rx_nbits: 0,
                pad: 0,
            }
        }

        pub fn len(&self) -> u32 {
            self.len
        }
    }

    ioctl!(write_buf spi_transfer with SPI_IOC_MAGIC, SPI_IOC_TYPE_MESSAGE; TransferSegment);
    ioctl!(write_int spi_write_mode with SPI_IOC_MAGIC, SPI_IOC_TYPE_MODE32);
    ioctl!(write_int spi_write_lsb_first with SPI_IOC_MAGIC, SPI_IOC_TYPE_LSB_FIRST);
    ioctl!(write_int spi_write_bits_per_word with SPI_IOC_MAGIC, SPI_IOC_TYPE_BITS_PER_WORD);
    ioctl!(write_int spi_write_max_speed_hz with SPI_IOC_MAGIC, SPI_IOC_TYPE_MAX_SPEED_HZ);
}

/// SPI buses.
///
/// The Raspberry Pi supports up to three SPI buses, depending on the model and
/// your `/boot/config.txt` configuration. More information can be found [here].
///
/// [here]: index.html
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum Bus {
    Spi0 = 0,
    Spi1 = 1,
    Spi2 = 2,
}

/// Chip Enable (Slave Select) pins.
///
/// The Chip Enable pin is used to signal which device should
/// pay attention to the SPI bus. Chip Enable is more commonly
/// known as Slave Select or Chip Select.
///
/// The number of available Chip Enable pins for the selected SPI
/// bus depends on your `/boot/config.txt` configuration. More
/// information can be found [here].
///
/// [here]: index.html
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum ChipEnable {
    Ce0 = 0,
    Ce1 = 1,
    Ce2 = 2,
}

/// SPI modes.
///
/// Select the appropriate SPI mode for your device. Each mode
/// configures the clock polarity (CPOL) and clock phase (CPHA)
/// as shown below:
///
/// * Mode0: CPOL 0, CPHA 0
/// * Mode1: CPOL 0, CPHA 1
/// * Mode2: CPOL 1, CPHA 0
/// * Mode3: CPOL 1, CPHA 1
///
/// More information on clock polarity and phase can be found on [Wikipedia].
///
/// [Wikipedia]: https://en.wikipedia.org/wiki/Serial_Peripheral_Interface_Bus#Clock_polarity_and_phase
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum Mode {
    Mode0 = 0,
    Mode1 = 1,
    Mode2 = 2,
    Mode3 = 3,
}

/// Bit order.
///
/// The bit order determines in what order data is shifted out and shifted in.
/// Select the bit order that's appropriate for the device you're communicating with.
///
/// `MsbFirst` will transfer the most-significant bit first. `LsbFirst` will transfer the
/// least-significant bit first.
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum BitOrder {
    MsbFirst = 0,
    LsbFirst = 1,
}

/// Provides access to the Raspberry Pi's SPI peripherals.
///
/// Before using `Spi`, make sure your Raspberry Pi has the necessary SPI buses and Chip Enable pins
/// enabled. More information can be found [here].
///
/// [here]: index.html
pub struct Spi {
    spidev: File,
}

impl Spi {
    /// Creates a new instance of `Spi`.
    ///
    /// `bus` and `chip_enable` specify the selected SPI bus and one of its associated Chip Enable pins.
    ///
    /// `clock_speed` defines the maximum clock speed in Hz. The SPI driver will automatically select
    /// the closest valid frequency.
    ///
    /// `mode` selects the clock polarity and phase.
    ///
    /// `bit_order` sets the order in which bits are shifted out and in to most-significant bit first or
    /// least-significant bit first.
    pub fn new(
        bus: Bus,
        chip_enable: ChipEnable,
        clock_speed: u32,
        mode: Mode,
        bit_order: BitOrder,
    ) -> Result<Spi> {
        // We don't ask for bits per word, because the driver only supports
        // 8 bits (or 9 bits in LoSSI mode). Changing the SS polarity from
        // active-low to active-high isn't supported either.
        // Based on https://www.raspberrypi.org/documentation/hardware/raspberrypi/spi/README.md
        // and https://www.kernel.org/doc/Documentation/spi/spidev.

        let spidev = OpenOptions::new()
            .read(true)
            .write(true)
            .open(format!("/dev/spidev{}.{}", bus as u8, chip_enable as u8))?;

        // Configure SPI bus through ioctl calls
        unsafe {
            ioctl::spi_write_mode(spidev.as_raw_fd(), mode as i32)?;
            ioctl::spi_write_max_speed_hz(spidev.as_raw_fd(), clock_speed as i32)?;
            ioctl::spi_write_bits_per_word(spidev.as_raw_fd(), 8)?;
            ioctl::spi_write_lsb_first(spidev.as_raw_fd(), bit_order as i32)?;
        }

        Ok(Spi { spidev })
    }

    /// Receives incoming data from the slave device and writes it to `buffer`.
    ///
    /// The SPI protocol doesn't indicate how much incoming data is waiting,
    /// so the maximum number of bytes read depends on the length of `buffer`.
    ///
    /// During the read, the MOSI line is kept in a state that results in a zero
    /// value byte shifted out for every byte `read` receives on the MISO line.
    ///
    /// Chip Enable is set to Low (active) at the start of the read, and
    /// High (inactive) when the read completes.
    ///
    /// Returns how many bytes were read.
    pub fn read(&mut self, buffer: &mut [u8]) -> Result<usize> {
        Ok(self.spidev.read(buffer)?)
    }

    /// Sends the outgoing data contained in `buffer` to the slave device.
    ///
    /// Any data received on the MISO line from the slave is ignored.
    ///
    /// Chip Enable is set to Low (active) at the start of the write, and
    /// High (inactive) when the write completes.
    ///
    /// Returns how many bytes were written.
    pub fn write(&mut self, buffer: &[u8]) -> Result<usize> {
        Ok(self.spidev.write(buffer)?)
    }

    /// Sends and receives data at the same time.
    ///
    /// SPI is a full-duplex protocol that shifts out bits to the slave device on the MOSI
    /// line while simultaneously shifting in bits it receives on the MISO line.
    /// `transfer` stores the incoming data in `read_buffer`, and sends the outgoing data
    /// contained in `write_buffer`.
    ///
    /// Because data is sent and received simultaneously, `transfer` only transfers
    /// as many bytes as the shortest of the two buffers contains.
    ///
    /// Chip Enable is set to Low (active) at the start of the transfer, and
    /// High (inactive) when the transfer completes.
    ///
    /// Returns how many bytes were transferred.
    pub fn transfer(&mut self, read_buffer: &mut [u8], write_buffer: &[u8]) -> Result<usize> {
        let segment = ioctl::TransferSegment::new(Some(read_buffer), Some(write_buffer));

        unsafe {
            ioctl::spi_transfer(self.spidev.as_raw_fd(), &[segment])?;
        }

        Ok(segment.len() as usize)
    }
}
<commit_msg>Add buffer size limit information<commit_after>// Copyright (c) 2017-2018 Rene van der Meer
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

//! Interface for the SPI peripherals.
//!
//! RPPAL provides access to the available SPI peripherals by using the `/dev/spidevB.C`
//! devices, where B points to an SPI bus (0, 1, 2), and C to a Chip Enable pin (0, 1, 2).
//!
//! ## SPI buses
//!
//! The Raspberry Pi's GPIO header exposes several SPI buses. SPI0 is available
//! on all Raspberry Pi models. SPI1 is available on models with a 40-pin
//! header. SPI2 is only available on the Compute and Compute 3.
//!
//! SPI0 is disabled by default. You can enable it by running
//! `sudo raspi-config`, or by manually adding `dtparam=spi=on` to
//! `/boot/config.txt`. The associated pins are listed below.
//!
//! * MISO: BCM GPIO 9 (physical pin 21)
//! * MOSI: BCM GPIO 10 (physical pin 19)
//! * SCLK: BCM GPIO 11 (physical pin 23)
//! * SS: CE0: BCM GPIO 8 (physical pin 24), CE1: BCM GPIO 7 (physical pin 26)
//!
//! SPI1 is an auxiliary peripheral that's referred to as mini SPI. According
//! to the documentation, using higher clock speeds on SPI1 requires additional
//! CPU time compared to SPI0, caused by shallow FIFOs and no DMA support. SPI1
//! can be enabled by adding `dtoverlay=spi1-3cs` to `/boot/config.txt`. Replace
//! `3cs` with either `2cs` or `1cs` if you only require 2 or 1 Slave Select pins.
//! The associated pins are listed below.
//!
//! * MISO: BCM GPIO 19 (physical pin 35)
//! * MOSI: BCM GPIO 20 (physical pin 38)
//! * SCLK: BCM GPIO 21 (physical pin 40)
//! * SS: CE0: BCM GPIO 18 (physical pin 12), CE1: BCM GPIO 17 (physical pin 11), CE2: BCM GPIO 16 (physical pin 36)
//!
//! SPI2 shares the same characteristics as SPI1. It can be enabled by adding
//! `dtoverlay=spi2-3cs` to `/boot/config.txt`. Replace `3cs` with either `2cs` or
//! `1cs` if you only require 2 or 1 Slave Select pins. The associated pins are
//! listed below.
//!
//! * MISO: BCM GPIO 40
//! * MOSI: BCM GPIO 41
//! * SCLK: BCM GPIO 42
//! * SS: CE0: BCM GPIO 43, CE1: BCM GPIO 44, CE2: BCM GPIO 45
//!
//! The GPIO pin numbers mentioned above are part of the default configuration. Some of
//! their functionality can be moved to different pins. Read `/boot/overlays/README`
//! for more information.
//!
//! ## Buffer size limits
//!
//! By default, the Linux SPI driver can handle up to 4096 bytes in a single transfer. You can
//! increase this limit to a maximum of 65536 bytes by adding `spidev.bufsiz=65536` to the single line
//! of parameters in `/boot/cmdline.txt`. Remember to reboot the Raspberry Pi afterwards. The current
//! value of bufsiz can be checked with `cat /sys/module/spidev/parameters/bufsiz`.

use std::fs::{File, OpenOptions};
use std::io;
use std::io::{Read, Write};
use std::os::unix::io::AsRawFd;
use std::result;

use nix;

pub use nix::errno::Errno;

quick_error! {
    #[derive(Debug)]
/// Errors that can occur when accessing the SPI peripherals.
    pub enum Error {
/// IO error.
        Io(err: io::Error) { description(err.description()) from() }
/// System call error.
        Sys(errno: Errno) { display("System call error: {}", errno) }
/// Invalid path.
        InvalidPath {}
/// Invalid Utf8.
        InvalidUtf8 {}
/// Unsupported operation.
        UnsupportedOperation {}
    }
}

impl From<nix::Error> for Error {
    fn from(err: nix::Error) -> Error {
        match err {
            nix::Error::Sys(errno) => Error::Sys(errno),
            nix::Error::InvalidPath => Error::InvalidPath,
            nix::Error::InvalidUtf8 => Error::InvalidUtf8,
            nix::Error::UnsupportedOperation => Error::UnsupportedOperation,
        }
    }
}

/// Result type returned from methods that can have `spi::Error`s.
pub type Result<T> = result::Result<T, Error>;

mod ioctl {
    const SPI_IOC_MAGIC: u8 = b'k';

    const SPI_IOC_TYPE_MESSAGE: u8 = 0;
    const SPI_IOC_TYPE_LSB_FIRST: u8 = 2;
    const SPI_IOC_TYPE_BITS_PER_WORD: u8 = 3;
    const SPI_IOC_TYPE_MAX_SPEED_HZ: u8 = 4;
    const SPI_IOC_TYPE_MODE32: u8 = 5;

    #[derive(Debug, PartialEq, Copy, Clone)]
    #[repr(C)]
    pub struct TransferSegment {
        // Pointer to transmit buffer, or 0.
        tx_buf: u64,
        // Pointer to receive buffer, or 0.
        rx_buf: u64,
        // Number of bytes to transfer in this segment.
        len: u32,
        // Set a different clock speed for this segment. 0 = default.
        speed_hz: u32,
        // Add a delay before the (optional) SS change and the next segment.
        delay_usecs: u16,
        // Not used, since we only support 8 bits. 0 = default.
        bits_per_word: u8,
        // Set to 1 to briefly set SS inactive between this segment and the next, and keep SS active after the final segment.
        cs_change: u8,
        // Used for dual/quad SPI.
        tx_nbits: u8,
        // Used for dual/quad SPI.
        rx_nbits: u8,
        // Padding. Set to 0 for forward compatibility.
        pad: u16,
    }

    // TODO: Doublecheck cs_change behavior after the final transfer segment. Some SPI drivers may have
    // interpreted the documentation incorrectly.

    impl TransferSegment {
        pub fn new(read_buffer: Option<&mut [u8]>, write_buffer: Option<&[u8]>) -> TransferSegment {
            // Len will contain the length of the shortest of the supplied buffers
            let mut len: u32 = 0;

            let tx_buf = if let Some(buffer) = write_buffer {
                len = buffer.len() as u32;
                buffer.as_ptr() as u64
            } else {
                0
            };

            let rx_buf = if let Some(buffer) = read_buffer {
                if len > buffer.len() as u32 {
                    len = buffer.len() as u32;
                }
                buffer.as_ptr() as u64
            } else {
                0
            };

            TransferSegment {
                tx_buf,
                rx_buf,
                len,
                speed_hz: 0,
                delay_usecs: 0,
                bits_per_word: 0,
                cs_change: 0,
                tx_nbits: 0,
                rx_nbits: 0,
                pad: 0,
            }
        }

        pub fn len(&self) -> u32 {
            self.len
        }
    }

    ioctl!(write_buf spi_transfer with SPI_IOC_MAGIC, SPI_IOC_TYPE_MESSAGE; TransferSegment);
    ioctl!(write_int spi_write_mode with SPI_IOC_MAGIC, SPI_IOC_TYPE_MODE32);
    ioctl!(write_int spi_write_lsb_first with SPI_IOC_MAGIC, SPI_IOC_TYPE_LSB_FIRST);
    ioctl!(write_int spi_write_bits_per_word with SPI_IOC_MAGIC, SPI_IOC_TYPE_BITS_PER_WORD);
    ioctl!(write_int spi_write_max_speed_hz with SPI_IOC_MAGIC, SPI_IOC_TYPE_MAX_SPEED_HZ);
}

/// SPI buses.
///
/// The Raspberry Pi supports up to three SPI buses, depending on the model and
/// your `/boot/config.txt` configuration. More information can be found [here].
///
/// [here]: index.html
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum Bus {
    Spi0 = 0,
    Spi1 = 1,
    Spi2 = 2,
}

/// Chip Enable (Slave Select) pins.
///
/// The Chip Enable pin is used to signal which device should
/// pay attention to the SPI bus. Chip Enable is more commonly
/// known as Slave Select or Chip Select.
///
/// The number of available Chip Enable pins for the selected SPI
/// bus depends on your `/boot/config.txt` configuration. More
/// information can be found [here].
///
/// [here]: index.html
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum ChipEnable {
    Ce0 = 0,
    Ce1 = 1,
    Ce2 = 2,
}

/// SPI modes.
///
/// Select the appropriate SPI mode for your device. Each mode
/// configures the clock polarity (CPOL) and clock phase (CPHA)
/// as shown below:
///
/// * Mode0: CPOL 0, CPHA 0
/// * Mode1: CPOL 0, CPHA 1
/// * Mode2: CPOL 1, CPHA 0
/// * Mode3: CPOL 1, CPHA 1
///
/// More information on clock polarity and phase can be found on [Wikipedia].
///
/// [Wikipedia]: https://en.wikipedia.org/wiki/Serial_Peripheral_Interface_Bus#Clock_polarity_and_phase
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum Mode {
    Mode0 = 0,
    Mode1 = 1,
    Mode2 = 2,
    Mode3 = 3,
}

/// Bit order.
///
/// The bit order determines in what order data is shifted out and shifted in.
/// Select the bit order that's appropriate for the device you're communicating with.
///
/// `MsbFirst` will transfer the most-significant bit first. `LsbFirst` will transfer the
/// least-significant bit first.
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum BitOrder {
    MsbFirst = 0,
    LsbFirst = 1,
}

/// Provides access to the Raspberry Pi's SPI peripherals.
///
/// Before using `Spi`, make sure your Raspberry Pi has the necessary SPI buses and Chip Enable pins
/// enabled. More information can be found [here].
///
/// [here]: index.html
pub struct Spi {
    spidev: File,
}

impl Spi {
    /// Creates a new instance of `Spi`.
    ///
    /// `bus` and `chip_enable` specify the selected SPI bus and one of its associated Chip Enable pins.
    ///
    /// `clock_speed` defines the maximum clock speed in Hz. The SPI driver will automatically select
    /// the closest valid frequency.
    ///
    /// `mode` selects the clock polarity and phase.
    ///
    /// `bit_order` sets the order in which bits are shifted out and in to most-significant bit first or
    /// least-significant bit first.
    pub fn new(
        bus: Bus,
        chip_enable: ChipEnable,
        clock_speed: u32,
        mode: Mode,
        bit_order: BitOrder,
    ) -> Result<Spi> {
        // We don't ask for bits per word, because the driver only supports
        // 8 bits (or 9 bits in LoSSI mode). Changing the SS polarity from
        // active-low to active-high isn't supported either.
        // Based on https://www.raspberrypi.org/documentation/hardware/raspberrypi/spi/README.md
        // and https://www.kernel.org/doc/Documentation/spi/spidev.

        let spidev = OpenOptions::new()
            .read(true)
            .write(true)
            .open(format!("/dev/spidev{}.{}", bus as u8, chip_enable as u8))?;

        // Configure SPI bus through ioctl calls
        unsafe {
            ioctl::spi_write_mode(spidev.as_raw_fd(), mode as i32)?;
            ioctl::spi_write_max_speed_hz(spidev.as_raw_fd(), clock_speed as i32)?;
            ioctl::spi_write_bits_per_word(spidev.as_raw_fd(), 8)?;
            ioctl::spi_write_lsb_first(spidev.as_raw_fd(), bit_order as i32)?;
        }

        Ok(Spi { spidev })
    }

    /// Receives incoming data from the slave device and writes it to `buffer`.
    ///
    /// The SPI protocol doesn't indicate how much incoming data is waiting,
    /// so the maximum number of bytes read depends on the length of `buffer`.
    ///
    /// During the read, the MOSI line is kept in a state that results in a zero
    /// value byte shifted out for every byte `read` receives on the MISO line.
    ///
    /// Chip Enable is set to Low (active) at the start of the read, and
    /// High (inactive) when the read completes.
    ///
    /// Returns how many bytes were read.
    pub fn read(&mut self, buffer: &mut [u8]) -> Result<usize> {
        Ok(self.spidev.read(buffer)?)
    }

    /// Sends the outgoing data contained in `buffer` to the slave device.
    ///
    /// Any data received on the MISO line from the slave is ignored.
    ///
    /// Chip Enable is set to Low (active) at the start of the write, and
    /// High (inactive) when the write completes.
    ///
    /// Returns how many bytes were written.
    pub fn write(&mut self, buffer: &[u8]) -> Result<usize> {
        Ok(self.spidev.write(buffer)?)
    }

    /// Sends and receives data at the same time.
    ///
    /// SPI is a full-duplex protocol that shifts out bits to the slave device on the MOSI
    /// line while simultaneously shifting in bits it receives on the MISO line.
    /// `transfer` stores the incoming data in `read_buffer`, and sends the outgoing data
    /// contained in `write_buffer`.
    ///
    /// Because data is sent and received simultaneously, `transfer` only transfers
    /// as many bytes as the shortest of the two buffers contains.
    ///
    /// Chip Enable is set to Low (active) at the start of the transfer, and
    /// High (inactive) when the transfer completes.
    ///
    /// Returns how many bytes were transferred.
    pub fn transfer(&mut self, read_buffer: &mut [u8], write_buffer: &[u8]) -> Result<usize> {
        let segment = ioctl::TransferSegment::new(Some(read_buffer), Some(write_buffer));

        unsafe {
            ioctl::spi_transfer(self.spidev.as_raw_fd(), &[segment])?;
        }

        Ok(segment.len() as usize)
    }
}
<|endoftext|>