<commit_before>use log::{Level, LevelFilter};

pub use chrono::offset::{FixedOffset, Local, Offset, TimeZone, Utc};
#[cfg(feature = "termcolor")]
use termcolor::Color;
use std::borrow::Cow;

#[derive(Debug, Clone, Copy)]
/// Padding to be used for logging the level
pub enum LevelPadding {
    /// Add spaces on the left side
    Left,
    /// Add spaces on the right side
    Right,
    /// Do not pad the level
    Off,
}

#[derive(Debug, Clone, Copy)]
/// Padding to be used for logging the thread id/name
pub enum ThreadPadding {
    /// Add spaces on the left side, up to usize many
    Left(usize),
    /// Add spaces on the right side, up to usize many
    Right(usize),
    /// Do not pad the thread id/name
    Off,
}

#[derive(Debug, Clone, Copy, PartialEq)]
/// Mode for logging the thread name or id or both.
pub enum ThreadLogMode {
    /// Log thread ids only
    IDs,
    /// Log the thread names only
    Names,
    /// If this thread is named, log the name. Otherwise, log the thread id.
    Both,
}

/// Configuration for the Loggers
///
/// All loggers print the message in the following form:
/// `00:00:00 [LEVEL] crate::module: [lib.rs::100] your_message`
/// Every space delimited part except the actual message is optional.
///
/// Pass this struct to your logger to change when these information shall
/// be logged.
///
/// Construct using `Default` or using `ConfigBuilder`
#[derive(Debug, Clone)]
pub struct Config {
    pub(crate) time: LevelFilter,
    pub(crate) level: LevelFilter,
    pub(crate) level_padding: LevelPadding,
    pub(crate) thread: LevelFilter,
    pub(crate) thread_log_mode: ThreadLogMode,
    pub(crate) thread_padding: ThreadPadding,
    pub(crate) target: LevelFilter,
    pub(crate) location: LevelFilter,
    pub(crate) time_format: Cow<'static, str>,
    pub(crate) time_offset: FixedOffset,
    pub(crate) time_local: bool,
    pub(crate) filter_allow: Cow<'static, [Cow<'static, str>]>,
    pub(crate) filter_ignore: Cow<'static, [Cow<'static, str>]>,
    #[cfg(feature = "termcolor")]
    pub(crate) level_color: [Color; 6],
}

/// Builder for the Logger Configurations (`Config`)
///
/// All loggers print the message in the following form:
/// `00:00:00 [LEVEL] crate::module: [lib.rs::100] your_message`
/// Every space delimited part except the actual message is optional.
///
/// Use this struct to create a custom `Config` changing when these information shall
/// be logged. Every part can be enabled for a specific Level and is then
/// automatically enable for all lower levels as well.
///
/// The Result is that the logging gets more detailed the more verbose it gets.
/// E.g. to have one part shown always use `Level::Error`. But if you
/// want to show the source line only on `Trace` use that.
#[derive(Debug, Clone)]
pub struct ConfigBuilder(Config);

impl ConfigBuilder {
    /// Create a new default ConfigBuilder
    pub fn new() -> ConfigBuilder {
        ConfigBuilder(Config::default())
    }

    /// Set at which level and below the level itself shall be logged (default is Error)
    pub fn set_max_level<'a>(&'a mut self, level: LevelFilter) -> &'a mut ConfigBuilder {
        self.0.level = level;
        self
    }

    /// Set at which level and below the current time shall be logged (default is Error)
    pub fn set_time_level<'a>(&'a mut self, time: LevelFilter) -> &'a mut ConfigBuilder {
        self.0.time = time;
        self
    }

    /// Set at which level and below the thread id shall be logged. (default is Debug)
    pub fn set_thread_level<'a>(&'a mut self, thread: LevelFilter) -> &'a mut ConfigBuilder {
        self.0.thread = thread;
        self
    }

    /// Set at which level and below the target shall be logged. (default is Debug)
    pub fn set_target_level<'a>(&'a mut self, target: LevelFilter) -> &'a mut ConfigBuilder {
        self.0.target = target;
        self
    }

    /// Set at which level and below a source code reference shall be logged (default is Trace)
    pub fn set_location_level<'a>(&'a mut self, location: LevelFilter) -> &'a mut ConfigBuilder {
        self.0.location = location;
        self
    }

    /// Set how the levels should be padded, when logging (default is Left)
    pub fn set_level_padding<'a>(&'a mut self, padding: LevelPadding) -> &'a mut ConfigBuilder {
        self.0.level_padding = padding;
        self
    }

    /// Set how the thread should be padded
    pub fn set_thread_padding<'a>(&'a mut self, padding: ThreadPadding) -> &'a mut ConfigBuilder {
        self.0.thread_padding = padding;
        self
    }

    /// Set the mode for logging the thread
    pub fn set_thread_mode<'a>(&'a mut self, mode: ThreadLogMode) -> &'a mut ConfigBuilder {
        self.0.thread_log_mode = mode;
        self
    }

    /// Set the color used for printing the level (if the logger supports it)
    #[cfg(feature = "termcolor")]
    pub fn set_level_color<'a>(&'a mut self, level: Level, color: Color) -> &'a mut ConfigBuilder {
        self.0.level_color[level as usize] = color;
        self
    }

    /// Set time chrono [strftime] format string.
    ///
    /// [strftime]: https://docs.rs/chrono/0.4.0/chrono/format/strftime/index.html#specifiers
    pub fn set_time_format_str<'a>(
        &'a mut self,
        time_format: &'static str,
    ) -> &'a mut ConfigBuilder {
        self.0.time_format = Cow::Borrowed(time_format);
        self
    }

    /// Set time chrono [strftime] format string.
    ///
    /// [strftime]: https://docs.rs/chrono/0.4.0/chrono/format/strftime/index.html#specifiers
    pub fn set_time_format<'a>(&'a mut self, time_format: String) -> &'a mut ConfigBuilder {
        self.0.time_format = Cow::Owned(time_format);
        self
    }

    /// Set offset used for logging time (default is 0)
    pub fn set_time_offset<'a>(&'a mut self, time_offset: FixedOffset) -> &'a mut ConfigBuilder {
        self.0.time_offset = time_offset;
        self
    }

    /// set if you log in local timezone or UTC (default is UTC)
    pub fn set_time_to_local<'a>(&'a mut self, local: bool) -> &'a mut ConfigBuilder {
        self.0.time_local = local;
        self
    }

    /// Add allowed module filters.
    /// If any are specified, only records from modules starting with one of these entries will be printed
    ///
    /// For example, `add_filter_allow_str("tokio::uds")` would allow only logging from the `tokio` crates `uds` module.
    pub fn add_filter_allow_str<'a>(
        &'a mut self,
        time_format: &'static str,
    ) -> &'a mut ConfigBuilder {
        let mut list = Vec::from(&*self.0.filter_allow);
        list.push(Cow::Borrowed(time_format));
        self.0.filter_allow = Cow::Owned(list);
        self
    }

    /// Add allowed module filters.
    /// If any are specified, only records from modules starting with one of these entries will be printed
    ///
    /// For example, `add_filter_allow(format!("{}{}","tokio", "uds"))` would allow only logging from the `tokio` crates `uds` module.
    pub fn add_filter_allow<'a>(&'a mut self, time_format: String) -> &'a mut ConfigBuilder {
        let mut list = Vec::from(&*self.0.filter_allow);
        list.push(Cow::Owned(time_format));
        self.0.filter_allow = Cow::Owned(list);
        self
    }

    /// Clear allowed module filters.
    /// If none are specified, nothing is filtered out
    pub fn clear_filter_allow<'a>(&'a mut self) -> &'a mut ConfigBuilder {
        self.0.filter_allow = Cow::Borrowed(&[]);
        self
    }

    /// Add denied module filters.
    /// If any are specified, records from modules starting with one of these entries will be ignored
    ///
    /// For example, `add_filter_ignore_str("tokio::uds")` would deny logging from the `tokio` crates `uds` module.
    pub fn add_filter_ignore_str<'a>(
        &'a mut self,
        time_format: &'static str,
    ) -> &'a mut ConfigBuilder {
        let mut list = Vec::from(&*self.0.filter_ignore);
        list.push(Cow::Borrowed(time_format));
        self.0.filter_ignore = Cow::Owned(list);
        self
    }

    /// Add denied module filters.
    /// If any are specified, records from modules starting with one of these entries will be ignored
    ///
    /// For example, `add_filter_ignore(format!("{}{}","tokio", "uds"))` would deny logging from the `tokio` crates `uds` module.
    pub fn add_filter_ignore<'a>(&'a mut self, time_format: String) -> &'a mut ConfigBuilder {
        let mut list = Vec::from(&*self.0.filter_ignore);
        list.push(Cow::Owned(time_format));
        self.0.filter_ignore = Cow::Owned(list);
        self
    }

    /// Clear ignore module filters.
    /// If none are specified, nothing is filtered
    pub fn clear_filter_ignore<'a>(&'a mut self) -> &'a mut ConfigBuilder {
        self.0.filter_ignore = Cow::Borrowed(&[]);
        self
    }

    /// Build new `Config`
    pub fn build(&mut self) -> Config {
        self.0.clone()
    }
}

impl Default for Config {
    fn default() -> Config {
        Config {
            time: LevelFilter::Error,
            level: LevelFilter::Error,
            level_padding: LevelPadding::Off,
            thread: LevelFilter::Debug,
            thread_log_mode: ThreadLogMode::IDs,
            thread_padding: ThreadPadding::Off,
            target: LevelFilter::Debug,
            location: LevelFilter::Trace,
            time_format: Cow::Borrowed("%H:%M:%S"),
            time_offset: FixedOffset::east(0),
            time_local: false,
            filter_allow: Cow::Borrowed(&[]),
            filter_ignore: Cow::Borrowed(&[]),

            #[cfg(feature = "termcolor")]
            level_color: [
                Color::White,  // (dummy)
                Color::Red,    // Error
                Color::Yellow, // Warn
                Color::Blue,   // Info
                Color::Cyan,   // Debug
                Color::White,  // Trace
            ],
        }
    }
}
<commit_msg>Future-proof config by requiring ..Default::default()<commit_after>use log::{Level, LevelFilter};

pub use chrono::offset::{FixedOffset, Local, Offset, TimeZone, Utc};
#[cfg(feature = "termcolor")]
use termcolor::Color;
use std::borrow::Cow;

#[derive(Debug, Clone, Copy)]
/// Padding to be used for logging the level
pub enum LevelPadding {
    /// Add spaces on the left side
    Left,
    /// Add spaces on the right side
    Right,
    /// Do not pad the level
    Off,
}

#[derive(Debug, Clone, Copy)]
/// Padding to be used for logging the thread id/name
pub enum ThreadPadding {
    /// Add spaces on the left side, up to usize many
    Left(usize),
    /// Add spaces on the right side, up to usize many
    Right(usize),
    /// Do not pad the thread id/name
    Off,
}

#[derive(Debug, Clone, Copy, PartialEq)]
/// Mode for logging the thread name or id or both.
pub enum ThreadLogMode {
    /// Log thread ids only
    IDs,
    /// Log the thread names only
    Names,
    /// If this thread is named, log the name. Otherwise, log the thread id.
    Both,
}

/// Configuration for the Loggers
///
/// All loggers print the message in the following form:
/// `00:00:00 [LEVEL] crate::module: [lib.rs::100] your_message`
/// Every space delimited part except the actual message is optional.
///
/// Pass this struct to your logger to change when these information shall
/// be logged.
///
/// Construct using `Default` or using `ConfigBuilder`
#[derive(Debug, Clone)]
pub struct Config {
    pub(crate) time: LevelFilter,
    pub(crate) level: LevelFilter,
    pub(crate) level_padding: LevelPadding,
    pub(crate) thread: LevelFilter,
    pub(crate) thread_log_mode: ThreadLogMode,
    pub(crate) thread_padding: ThreadPadding,
    pub(crate) target: LevelFilter,
    pub(crate) location: LevelFilter,
    pub(crate) time_format: Cow<'static, str>,
    pub(crate) time_offset: FixedOffset,
    pub(crate) time_local: bool,
    pub(crate) filter_allow: Cow<'static, [Cow<'static, str>]>,
    pub(crate) filter_ignore: Cow<'static, [Cow<'static, str>]>,
    #[cfg(feature = "termcolor")]
    pub(crate) level_color: [Color; 6],
}

/// Builder for the Logger Configurations (`Config`)
///
/// All loggers print the message in the following form:
/// `00:00:00 [LEVEL] crate::module: [lib.rs::100] your_message`
/// Every space delimited part except the actual message is optional.
///
/// Use this struct to create a custom `Config` changing when these information shall
/// be logged. Every part can be enabled for a specific Level and is then
/// automatically enable for all lower levels as well.
///
/// The Result is that the logging gets more detailed the more verbose it gets.
/// E.g. to have one part shown always use `Level::Error`. But if you
/// want to show the source line only on `Trace` use that.
#[derive(Debug, Clone)]
#[non_exhaustive]
pub struct ConfigBuilder(Config);

impl ConfigBuilder {
    /// Create a new default ConfigBuilder
    pub fn new() -> ConfigBuilder {
        ConfigBuilder(Config::default())
    }

    /// Set at which level and below the level itself shall be logged (default is Error)
    pub fn set_max_level<'a>(&'a mut self, level: LevelFilter) -> &'a mut ConfigBuilder {
        self.0.level = level;
        self
    }

    /// Set at which level and below the current time shall be logged (default is Error)
    pub fn set_time_level<'a>(&'a mut self, time: LevelFilter) -> &'a mut ConfigBuilder {
        self.0.time = time;
        self
    }

    /// Set at which level and below the thread id shall be logged. (default is Debug)
    pub fn set_thread_level<'a>(&'a mut self, thread: LevelFilter) -> &'a mut ConfigBuilder {
        self.0.thread = thread;
        self
    }

    /// Set at which level and below the target shall be logged. (default is Debug)
    pub fn set_target_level<'a>(&'a mut self, target: LevelFilter) -> &'a mut ConfigBuilder {
        self.0.target = target;
        self
    }

    /// Set at which level and below a source code reference shall be logged (default is Trace)
    pub fn set_location_level<'a>(&'a mut self, location: LevelFilter) -> &'a mut ConfigBuilder {
        self.0.location = location;
        self
    }

    /// Set how the levels should be padded, when logging (default is Left)
    pub fn set_level_padding<'a>(&'a mut self, padding: LevelPadding) -> &'a mut ConfigBuilder {
        self.0.level_padding = padding;
        self
    }

    /// Set how the thread should be padded
    pub fn set_thread_padding<'a>(&'a mut self, padding: ThreadPadding) -> &'a mut ConfigBuilder {
        self.0.thread_padding = padding;
        self
    }

    /// Set the mode for logging the thread
    pub fn set_thread_mode<'a>(&'a mut self, mode: ThreadLogMode) -> &'a mut ConfigBuilder {
        self.0.thread_log_mode = mode;
        self
    }

    /// Set the color used for printing the level (if the logger supports it)
    #[cfg(feature = "termcolor")]
    pub fn set_level_color<'a>(&'a mut self, level: Level, color: Color) -> &'a mut ConfigBuilder {
        self.0.level_color[level as usize] = color;
        self
    }

    /// Set time chrono [strftime] format string.
    ///
    /// [strftime]: https://docs.rs/chrono/0.4.0/chrono/format/strftime/index.html#specifiers
    pub fn set_time_format_str<'a>(
        &'a mut self,
        time_format: &'static str,
    ) -> &'a mut ConfigBuilder {
        self.0.time_format = Cow::Borrowed(time_format);
        self
    }

    /// Set time chrono [strftime] format string.
    ///
    /// [strftime]: https://docs.rs/chrono/0.4.0/chrono/format/strftime/index.html#specifiers
    pub fn set_time_format<'a>(&'a mut self, time_format: String) -> &'a mut ConfigBuilder {
        self.0.time_format = Cow::Owned(time_format);
        self
    }

    /// Set offset used for logging time (default is 0)
    pub fn set_time_offset<'a>(&'a mut self, time_offset: FixedOffset) -> &'a mut ConfigBuilder {
        self.0.time_offset = time_offset;
        self
    }

    /// set if you log in local timezone or UTC (default is UTC)
    pub fn set_time_to_local<'a>(&'a mut self, local: bool) -> &'a mut ConfigBuilder {
        self.0.time_local = local;
        self
    }

    /// Add allowed module filters.
    /// If any are specified, only records from modules starting with one of these entries will be printed
    ///
    /// For example, `add_filter_allow_str("tokio::uds")` would allow only logging from the `tokio` crates `uds` module.
    pub fn add_filter_allow_str<'a>(
        &'a mut self,
        time_format: &'static str,
    ) -> &'a mut ConfigBuilder {
        let mut list = Vec::from(&*self.0.filter_allow);
        list.push(Cow::Borrowed(time_format));
        self.0.filter_allow = Cow::Owned(list);
        self
    }

    /// Add allowed module filters.
    /// If any are specified, only records from modules starting with one of these entries will be printed
    ///
    /// For example, `add_filter_allow(format!("{}{}","tokio", "uds"))` would allow only logging from the `tokio` crates `uds` module.
    pub fn add_filter_allow<'a>(&'a mut self, time_format: String) -> &'a mut ConfigBuilder {
        let mut list = Vec::from(&*self.0.filter_allow);
        list.push(Cow::Owned(time_format));
        self.0.filter_allow = Cow::Owned(list);
        self
    }

    /// Clear allowed module filters.
    /// If none are specified, nothing is filtered out
    pub fn clear_filter_allow<'a>(&'a mut self) -> &'a mut ConfigBuilder {
        self.0.filter_allow = Cow::Borrowed(&[]);
        self
    }

    /// Add denied module filters.
    /// If any are specified, records from modules starting with one of these entries will be ignored
    ///
    /// For example, `add_filter_ignore_str("tokio::uds")` would deny logging from the `tokio` crates `uds` module.
    pub fn add_filter_ignore_str<'a>(
        &'a mut self,
        time_format: &'static str,
    ) -> &'a mut ConfigBuilder {
        let mut list = Vec::from(&*self.0.filter_ignore);
        list.push(Cow::Borrowed(time_format));
        self.0.filter_ignore = Cow::Owned(list);
        self
    }

    /// Add denied module filters.
    /// If any are specified, records from modules starting with one of these entries will be ignored
    ///
    /// For example, `add_filter_ignore(format!("{}{}","tokio", "uds"))` would deny logging from the `tokio` crates `uds` module.
    pub fn add_filter_ignore<'a>(&'a mut self, time_format: String) -> &'a mut ConfigBuilder {
        let mut list = Vec::from(&*self.0.filter_ignore);
        list.push(Cow::Owned(time_format));
        self.0.filter_ignore = Cow::Owned(list);
        self
    }

    /// Clear ignore module filters.
    /// If none are specified, nothing is filtered
    pub fn clear_filter_ignore<'a>(&'a mut self) -> &'a mut ConfigBuilder {
        self.0.filter_ignore = Cow::Borrowed(&[]);
        self
    }

    /// Build new `Config`
    pub fn build(&mut self) -> Config {
        self.0.clone()
    }
}

impl Default for Config {
    fn default() -> Config {
        Config {
            time: LevelFilter::Error,
            level: LevelFilter::Error,
            level_padding: LevelPadding::Off,
            thread: LevelFilter::Debug,
            thread_log_mode: ThreadLogMode::IDs,
            thread_padding: ThreadPadding::Off,
            target: LevelFilter::Debug,
            location: LevelFilter::Trace,
            time_format: Cow::Borrowed("%H:%M:%S"),
            time_offset: FixedOffset::east(0),
            time_local: false,
            filter_allow: Cow::Borrowed(&[]),
            filter_ignore: Cow::Borrowed(&[]),

            #[cfg(feature = "termcolor")]
            level_color: [
                Color::White,  // (dummy)
                Color::Red,    // Error
                Color::Yellow, // Warn
                Color::Blue,   // Info
                Color::Cyan,   // Debug
                Color::White,  // Trace
            ],
        }
    }
}
<|endoftext|>


<commit_before>use mem;
use parser::ast::ctxt::Context;

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

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

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

    stacksize: u32,
    contains_fct_invocation: bool,
}

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

            stacksize: 0,
            contains_fct_invocation: false,
        }
    }

    fn generate(&mut self) {
        self.visit_stmt(&self.fct.block);

        self.ctxt.function(self.fct.id, |fct| {
            fct.stacksize = self.stacksize;
            fct.contains_fct_invocation = self.contains_fct_invocation;
        });
    }
}

impl<'a, 'ast> Visitor<'ast> for CodeGenInfo<'a, 'ast> {
    fn visit_stmt(&mut self, s: &'ast Stmt) {
        if let StmtVar(ref var) = *s {
            let ty = self.ctxt.var(var.id, |v| v.data_type);
            self.stacksize = mem::align(self.stacksize + ty.size(), ty.size());
            self.ctxt.var(var.id, |v| { v.offset = -(self.stacksize as i32); });
        }

        visit::walk_stmt(self, s);
    }

    fn visit_expr(&mut self, e: &'ast Expr) {
        if let ExprCall(_) = *e {
            self.contains_fct_invocation = true;
        }

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

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

    use parser::ast::ctxt::Context;
    use driver::cmd::Args;
    use error::msg::Msg;
    use parser::ast;
    use parser::Parser;
    use semck;

    pub fn parse<F>(code: &'static str, f: F)
            where F: for<'a, 'ast> FnOnce(&'ast Context<'a, 'ast>) -> () {
        let mut parser = Parser::from_str(code);
        let (ast, interner) = parser.parse().unwrap();
        let map = ast::map::build(&ast, &interner);
        let args : Args = Default::default();

        ast::dump::dump(&ast, &interner);

        let ctxt = Context::new(&args, &interner, &map, &ast);

        semck::check(&ctxt);
        assert!(!ctxt.diag.borrow().has_errors());

        f(&ctxt);
    }

    #[test]
    fn test_invocation_flag() {
        parse("fn f() { g(); } fn g() { }", |ctxt| {
            let fct1 = ctxt.ast.elements[0].to_function().unwrap();
            generate(ctxt, fct1);
            assert_eq!(true, ctxt.function(fct1.id, |fct| fct.contains_fct_invocation));

            let fct2 = ctxt.ast.elements[1].to_function().unwrap();
            generate(ctxt, fct2);
            assert_eq!(false, ctxt.function(fct2.id, |fct| fct.contains_fct_invocation));
        });
    }

    #[test]
    fn test_var_offset() {
        parse("fn f() { var a = true; var b = false; var c = 2; var d = \"abc\"; }", |ctxt| {
            let fct = ctxt.ast.elements[0].to_function().unwrap();
            generate(ctxt, fct);
            assert_eq!(16, ctxt.function(fct.id, |fct| fct.stacksize));

            let vars = ctxt.function(fct.id, |fct| fct.vars.clone());

            for (varid, offset) in vars.iter().zip(&[-1, -2, -8, -16]) {
                assert_eq!(*offset, ctxt.var_infos.borrow()[varid.0].offset);
            }
        });
    }
}
<commit_msg>codegen: rewritten tests to avoid clone<commit_after>use mem;
use parser::ast::ctxt::Context;

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

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

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

    stacksize: u32,
    contains_fct_invocation: bool,
}

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

            stacksize: 0,
            contains_fct_invocation: false,
        }
    }

    fn generate(&mut self) {
        self.visit_stmt(&self.fct.block);

        self.ctxt.function(self.fct.id, |fct| {
            fct.stacksize = self.stacksize;
            fct.contains_fct_invocation = self.contains_fct_invocation;
        });
    }
}

impl<'a, 'ast> Visitor<'ast> for CodeGenInfo<'a, 'ast> {
    fn visit_stmt(&mut self, s: &'ast Stmt) {
        if let StmtVar(ref var) = *s {
            let ty = self.ctxt.var(var.id, |v| v.data_type);
            self.stacksize = mem::align(self.stacksize + ty.size(), ty.size());
            self.ctxt.var(var.id, |v| { v.offset = -(self.stacksize as i32); });
        }

        visit::walk_stmt(self, s);
    }

    fn visit_expr(&mut self, e: &'ast Expr) {
        if let ExprCall(_) = *e {
            self.contains_fct_invocation = true;
        }

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

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

    use parser::ast::ctxt::Context;
    use driver::cmd::Args;
    use error::msg::Msg;
    use parser::ast;
    use parser::Parser;
    use semck;

    pub fn parse<F>(code: &'static str, f: F)
            where F: for<'a, 'ast> FnOnce(&'ast Context<'a, 'ast>) -> () {
        let mut parser = Parser::from_str(code);
        let (ast, interner) = parser.parse().unwrap();
        let map = ast::map::build(&ast, &interner);
        let args : Args = Default::default();

        ast::dump::dump(&ast, &interner);

        let ctxt = Context::new(&args, &interner, &map, &ast);

        semck::check(&ctxt);
        assert!(!ctxt.diag.borrow().has_errors());

        f(&ctxt);
    }

    #[test]
    fn test_invocation_flag() {
        parse("fn f() { g(); } fn g() { }", |ctxt| {
            let fct1 = ctxt.ast.elements[0].to_function().unwrap();
            generate(ctxt, fct1);
            assert_eq!(true, ctxt.function(fct1.id, |fct| fct.contains_fct_invocation));

            let fct2 = ctxt.ast.elements[1].to_function().unwrap();
            generate(ctxt, fct2);
            assert_eq!(false, ctxt.function(fct2.id, |fct| fct.contains_fct_invocation));
        });
    }

    #[test]
    fn test_var_offset() {
        parse("fn f() { var a = true; var b = false; var c = 2; var d = \"abc\"; }", |ctxt| {
            let fct = ctxt.ast.elements[0].to_function().unwrap();
            generate(ctxt, fct);
            assert_eq!(16, ctxt.function(fct.id, |fct| fct.stacksize));

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


<commit_before>// Copyright (c) 2016 Nikita Pekin and the smexybot contributors
// See the README.md file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Provides functionality for the `!roll` command.

extern crate regex;

use rand::{self, Rng};
use self::regex::Regex;
use serenity::client::Context;
use serenity::model::Message;

use util::check_msg;

lazy_static! {
    static ref DICE_ROLL_REGEX: Regex = Regex::new(r"^(\d*)d(\d*)").unwrap();
}

pub fn handler(context: &Context, _message: &Message, args: Vec<String>) -> Result<(), String> {
    // TODO: add a notice regarding max X and Y values.
    const ERROR_MESSAGE: &'static str = "Please specify a roll in the form XdY (e.g. 2d6)";

    trace!("Received roll command with args: {:?}", args);
    let arg = match args.iter().next() {
        Some(arg) => arg,
        _ => {
            check_msg(context.say(ERROR_MESSAGE));
            return Ok(());
        },
    };

    let mut captures = DICE_ROLL_REGEX.captures_iter(arg);
    let next_capture = captures.next();
    let (number_of_dice, die_sides) = match next_capture {
        Some(capture) => {
            let number_of_dice = match capture.at(1) {
                Some(number_of_dice) => {
                    match number_of_dice.parse::<u32>() {
                        Ok(number_of_dice) => number_of_dice,
                        _ => {
                            check_msg(context.say(ERROR_MESSAGE));
                            return Ok(());
                        },
                    }
                },
                _ => {
                    check_msg(context.say(ERROR_MESSAGE));
                    return Ok(());
                },
            };

            let die_sides = match capture.at(2) {
                Some(die_sides) => {
                    match die_sides.parse::<u32>() {
                        Ok(die_sides) => die_sides,
                        _ => {
                            check_msg(context.say(ERROR_MESSAGE));
                            return Ok(());
                        },
                    }
                },
                _ => {
                    check_msg(context.say(ERROR_MESSAGE));
                    return Ok(());
                },
            };

            (number_of_dice, die_sides)
        },
        _ => {
            check_msg(context.say(ERROR_MESSAGE));
            return Ok(());
        },
    };

    if number_of_dice == 0 {
        check_msg(context.say("Number of dice cannot be 0"));
        return Ok(());
    }

    let mut rolls = Vec::new();
    let mut rng = rand::thread_rng();
    let mut sum = 0;
    for _ in 1..(number_of_dice + 1) {
        // TODO: verify that die sides is >= 1.
        let roll = rng.gen_range::<u32>(1, die_sides + 1);
        // TODO: add an overflow check here.
        sum += roll;
        rolls.push(roll);
    }
    let roll_string = rolls.iter()
        .map(|v| v.to_string())
        .collect::<Vec<String>>()
        .join(" + ");

    let response = match rolls.len() {
        1 => sum.to_string(),
        _ => format!("{} = {}", roll_string, sum),
    };

    check_msg(context.say(response.as_ref()));

    Ok(())
}
<commit_msg>chore: make match arm explicit<commit_after>// Copyright (c) 2016 Nikita Pekin and the smexybot contributors
// See the README.md file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Provides functionality for the `!roll` command.

extern crate regex;

use rand::{self, Rng};
use self::regex::Regex;
use serenity::client::Context;
use serenity::model::Message;

use util::check_msg;

lazy_static! {
    static ref DICE_ROLL_REGEX: Regex = Regex::new(r"^(\d*)d(\d*)").unwrap();
}

pub fn handler(context: &Context, _message: &Message, args: Vec<String>) -> Result<(), String> {
    // TODO: add a notice regarding max X and Y values.
    const ERROR_MESSAGE: &'static str = "Please specify a roll in the form XdY (e.g. 2d6)";

    trace!("Received roll command with args: {:?}", args);
    let arg = match args.iter().next() {
        Some(arg) => arg,
        None => {
            check_msg(context.say(ERROR_MESSAGE));
            return Ok(());
        },
    };

    let mut captures = DICE_ROLL_REGEX.captures_iter(arg);
    let next_capture = captures.next();
    let (number_of_dice, die_sides) = match next_capture {
        Some(capture) => {
            let number_of_dice = match capture.at(1) {
                Some(number_of_dice) => {
                    match number_of_dice.parse::<u32>() {
                        Ok(number_of_dice) => number_of_dice,
                        _ => {
                            check_msg(context.say(ERROR_MESSAGE));
                            return Ok(());
                        },
                    }
                },
                _ => {
                    check_msg(context.say(ERROR_MESSAGE));
                    return Ok(());
                },
            };

            let die_sides = match capture.at(2) {
                Some(die_sides) => {
                    match die_sides.parse::<u32>() {
                        Ok(die_sides) => die_sides,
                        _ => {
                            check_msg(context.say(ERROR_MESSAGE));
                            return Ok(());
                        },
                    }
                },
                _ => {
                    check_msg(context.say(ERROR_MESSAGE));
                    return Ok(());
                },
            };

            (number_of_dice, die_sides)
        },
        _ => {
            check_msg(context.say(ERROR_MESSAGE));
            return Ok(());
        },
    };

    if number_of_dice == 0 {
        check_msg(context.say("Number of dice cannot be 0"));
        return Ok(());
    }

    let mut rolls = Vec::new();
    let mut rng = rand::thread_rng();
    let mut sum = 0;
    for _ in 1..(number_of_dice + 1) {
        // TODO: verify that die sides is >= 1.
        let roll = rng.gen_range::<u32>(1, die_sides + 1);
        // TODO: add an overflow check here.
        sum += roll;
        rolls.push(roll);
    }
    let roll_string = rolls.iter()
        .map(|v| v.to_string())
        .collect::<Vec<String>>()
        .join(" + ");

    let response = match rolls.len() {
        1 => sum.to_string(),
        _ => format!("{} = {}", roll_string, sum),
    };

    check_msg(context.say(response.as_ref()));

    Ok(())
}
<|endoftext|>


<commit_before>pub mod anoncreds;
pub mod ledger;
pub mod pool;
pub mod signus;
pub mod wallet;

use commands::anoncreds::{AnoncredsCommand, AnoncredsCommandExecutor};
use commands::ledger::{LedgerCommand, LedgerCommandExecutor};
use commands::pool::{PoolCommand, PoolCommandExecutor};
use commands::signus::{SignusCommand, SignusCommandExecutor};
use commands::wallet::{WalletCommand, WalletCommandExecutor};

use services::crypto::CryptoService;
use services::pool::PoolService;
use services::wallet::WalletService;

use std::error;
use std::sync::mpsc::{Sender, channel};
use std::rc::Rc;
use std::thread;
use std::sync::Mutex;

pub enum Command {
    Exit,
    Anoncreds(AnoncredsCommand),
    Ledger(LedgerCommand),
    Pool(PoolCommand),
    Signus(SignusCommand),
    Wallet(WalletCommand)
}

pub struct CommandExecutor {
    worker: Option<thread::JoinHandle<()>>,
    sender: Sender<Command>
}

/// Global (lazy inited) instance of CommandExecutor
///
/// Sample:
///
/// {
///     ...
///     let ref ce: CommandExecutor = *COMMAND_EXECUTOR.lock().unwrap();           <- lock +
///     ce.send(Command::Exit);                                                            |
///     ...                                                                                |
/// }                                                                            <- unlock +
lazy_static! {
    pub static ref COMMAND_EXECUTOR: Mutex<CommandExecutor> = Mutex::new(CommandExecutor::new());
}

impl CommandExecutor {
    fn new() -> CommandExecutor {
        let (sender, receiver) = channel();

        CommandExecutor {
            sender: sender,
            worker: Some(thread::spawn(move || {
                info!(target: "command_executor", "Worker thread started");

                let crypto_service = Rc::new(CryptoService::new());
                let pool_service = Rc::new(PoolService::new());
                let wallet_service = Rc::new(WalletService::new());

                let anoncreds_command_executor = AnoncredsCommandExecutor::new(crypto_service.clone(), pool_service.clone(), wallet_service.clone());
                let ledger_command_executor = LedgerCommandExecutor::new(crypto_service.clone(), pool_service.clone(), wallet_service.clone());
                let pool_command_executor = PoolCommandExecutor::new(pool_service.clone());
                let signus_command_executor = SignusCommandExecutor::new(crypto_service.clone(), pool_service.clone(), wallet_service.clone());
                let wallet_command_executor = WalletCommandExecutor::new(pool_service.clone(), wallet_service.clone());

                loop {
                    match receiver.recv() {
                        Ok(Command::Anoncreds(cmd)) => {
                            info!(target: "command_executor", "AnoncredsCommand command received");
                            anoncreds_command_executor.execute(cmd);
                        },
                        Ok(Command::Ledger(cmd)) => {
                            info!(target: "command_executor", "LedgerCommand command received");
                            ledger_command_executor.execute(cmd);
                        },
                        Ok(Command::Pool(cmd)) => {
                            info!(target: "command_executor", "PoolCommand command received");
                            pool_command_executor.execute(cmd);
                        },
                        Ok(Command::Signus(cmd)) => {
                            info!(target: "command_executor", "SignusCommand command received");
                            signus_command_executor.execute(cmd);
                        },
                        Ok(Command::Wallet(cmd)) => {
                            info!(target: "command_executor", "WalletCommand command received");
                            wallet_command_executor.execute(cmd);
                        },
                        Ok(Command::Exit) => {
                            info!(target: "command_executor", "Exit command received");
                            break
                        },
                        Err(err) => {
                            error!(target: "command_executor", "Failed to get command!");
                            panic!("Failed to get command! {:?}", err)
                        }
                    }
                }
            }))
        }
    }

    pub fn send(&self, cmd: Command) {
        self.sender.send(cmd);
    }
}

impl Drop for CommandExecutor {
    fn drop(&mut self) {
        info!(target: "command_executor", "Drop started");
        self.send(Command::Exit);
        // Option worker type and this kludge is workaround for rust
        self.worker.take().unwrap().join();
        info!(target: "command_executor", "Drop finished");
    }
}

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

    #[test]
    fn command_executor_can_be_created() {
        let command_executor = CommandExecutor::new();
        assert!(true, "No crashes on CommandExecutor::new");
    }

    #[test]
    fn command_executor_can_be_dropped() {
        fn drop_test() {
            let command_executor = CommandExecutor::new();
        }

        drop_test();
        assert!(true, "No crashes on CommandExecutor::drop");
    }
}<commit_msg>Add pub CommandExecutor::instance() and make global variable private.<commit_after>pub mod anoncreds;
pub mod ledger;
pub mod pool;
pub mod signus;
pub mod wallet;

use commands::anoncreds::{AnoncredsCommand, AnoncredsCommandExecutor};
use commands::ledger::{LedgerCommand, LedgerCommandExecutor};
use commands::pool::{PoolCommand, PoolCommandExecutor};
use commands::signus::{SignusCommand, SignusCommandExecutor};
use commands::wallet::{WalletCommand, WalletCommandExecutor};

use services::crypto::CryptoService;
use services::pool::PoolService;
use services::wallet::WalletService;

use std::error;
use std::sync::mpsc::{Sender, channel};
use std::rc::Rc;
use std::thread;
use std::sync::{Mutex, MutexGuard};

pub enum Command {
    Exit,
    Anoncreds(AnoncredsCommand),
    Ledger(LedgerCommand),
    Pool(PoolCommand),
    Signus(SignusCommand),
    Wallet(WalletCommand)
}

pub struct CommandExecutor {
    worker: Option<thread::JoinHandle<()>>,
    sender: Sender<Command>
}

/// Global (lazy inited) instance of CommandExecutor
///
/// Sample:
///
/// {
///     ...
///     let ref ce: CommandExecutor = *CommandExecutor::instance();                <- lock +
///     ce.send(Command::Exit);                                                            |
///     ...                                                                                |
/// }                                                                            <- unlock +
lazy_static! {
    static ref COMMAND_EXECUTOR: Mutex<CommandExecutor> = Mutex::new(CommandExecutor::new());
}

impl CommandExecutor {
    pub fn instance<'mutex>() -> MutexGuard<'mutex, CommandExecutor> {
        COMMAND_EXECUTOR.lock().unwrap()
    }

    fn new() -> CommandExecutor {
        let (sender, receiver) = channel();

        CommandExecutor {
            sender: sender,
            worker: Some(thread::spawn(move || {
                info!(target: "command_executor", "Worker thread started");

                let crypto_service = Rc::new(CryptoService::new());
                let pool_service = Rc::new(PoolService::new());
                let wallet_service = Rc::new(WalletService::new());

                let anoncreds_command_executor = AnoncredsCommandExecutor::new(crypto_service.clone(), pool_service.clone(), wallet_service.clone());
                let ledger_command_executor = LedgerCommandExecutor::new(crypto_service.clone(), pool_service.clone(), wallet_service.clone());
                let pool_command_executor = PoolCommandExecutor::new(pool_service.clone());
                let signus_command_executor = SignusCommandExecutor::new(crypto_service.clone(), pool_service.clone(), wallet_service.clone());
                let wallet_command_executor = WalletCommandExecutor::new(pool_service.clone(), wallet_service.clone());

                loop {
                    match receiver.recv() {
                        Ok(Command::Anoncreds(cmd)) => {
                            info!(target: "command_executor", "AnoncredsCommand command received");
                            anoncreds_command_executor.execute(cmd);
                        },
                        Ok(Command::Ledger(cmd)) => {
                            info!(target: "command_executor", "LedgerCommand command received");
                            ledger_command_executor.execute(cmd);
                        },
                        Ok(Command::Pool(cmd)) => {
                            info!(target: "command_executor", "PoolCommand command received");
                            pool_command_executor.execute(cmd);
                        },
                        Ok(Command::Signus(cmd)) => {
                            info!(target: "command_executor", "SignusCommand command received");
                            signus_command_executor.execute(cmd);
                        },
                        Ok(Command::Wallet(cmd)) => {
                            info!(target: "command_executor", "WalletCommand command received");
                            wallet_command_executor.execute(cmd);
                        },
                        Ok(Command::Exit) => {
                            info!(target: "command_executor", "Exit command received");
                            break
                        },
                        Err(err) => {
                            error!(target: "command_executor", "Failed to get command!");
                            panic!("Failed to get command! {:?}", err)
                        }
                    }
                }
            }))
        }
    }

    pub fn send(&self, cmd: Command) {
        self.sender.send(cmd);
    }
}

impl Drop for CommandExecutor {
    fn drop(&mut self) {
        info!(target: "command_executor", "Drop started");
        self.send(Command::Exit);
        // Option worker type and this kludge is workaround for rust
        self.worker.take().unwrap().join();
        info!(target: "command_executor", "Drop finished");
    }
}

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

    #[test]
    fn command_executor_can_be_created() {
        let command_executor = CommandExecutor::new();
        assert!(true, "No crashes on CommandExecutor::new");
    }

    #[test]
    fn command_executor_can_be_dropped() {
        fn drop_test() {
            let command_executor = CommandExecutor::new();
        }

        drop_test();
        assert!(true, "No crashes on CommandExecutor::drop");
    }

    #[test]
    fn command_executor_can_get_instance() {
        let ref command_executor: CommandExecutor = *CommandExecutor::instance();
        // Deadlock if another one instance will be requested (try to uncomment the next line)
        // let ref other_ce: CommandExecutor = *CommandExecutor::instance();
    }
}<|endoftext|>


<commit_before>
use byteorder::{ByteOrder, LittleEndian};

use std;

use gameboy;
use gameboy::{Interconnect, Interrupt, Irq, Memory};
use gameboy::gfx::{Color, Frame, GpuMode, GpuStat, SpriteShape, TileRenderOptions, TileRenderType};

pub struct Gpu {
    pub enabled: bool,
    pub ram: Memory,
    pub sprite_data: Memory,

    pub ly: u8,
    pub lyc: u8,

    pub frame: Frame,
    pub backbuffer: Frame,
    pub mode: GpuMode,

    control_register: u8,
    stat: GpuStat,
    scroll_y: u8,
    scroll_x: u8,
    window_y: u8,
    window_x: u8,
    bg_palette: u8,
    palette0: u8,
    palette1: u8,

    cycles: isize,

    counter: u8,
    tile_data_addr: usize,
    background_tilemap_addr: usize,
    window_tilemap_addr: usize,
    sprite_shape: SpriteShape,
    sprites_enabled: bool,
    background_enabled: bool,
    window_enabled: bool,
}

impl Gpu {
    pub fn new() -> Gpu {
        Gpu {
            enabled: true,
            ram: Memory::new(gameboy::gfx::VRAM_SIZE),
            sprite_data: Memory::new(gameboy::gfx::SPRITE_DATA_SIZE),
            control_register: 0x00,
            stat: GpuStat::new(),
            scroll_y: 0x00,
            scroll_x: 0x00,
            window_y: 0x00,
            window_x: 0x00,
            ly: 0x00,
            lyc: 0x00,
            bg_palette: 0x00,
            palette0: 0x00,
            palette1: 0x00,
            cycles: 0x00,
            frame: Frame::new(),
            backbuffer: Frame::new(),
            mode: GpuMode::HBlank,
            counter: 0,
            tile_data_addr: 0x00,
            background_tilemap_addr: 0xC00,
            window_tilemap_addr: 0x00,
            sprite_shape: SpriteShape::Square,
            sprites_enabled: true,
            background_enabled: true,
            window_enabled: true,
        }
    }

    pub fn reset(&mut self) {
        *self = Gpu::new();
    }

    fn set_status(&mut self, irq: &mut Irq) {
        if !self.enabled {
            self.cycles = 0x1C8;
            self.ly = 0x00;
            self.stat = GpuStat::from(0xFD);
        }

        let current_mode = self.mode.clone();

        if self.ly >= 0x90 {
            self.mode = GpuMode::VBlank;
        } else {
            if self.cycles >= 0x178 {
                self.mode = GpuMode::SearchingRam;
            } else if self.cycles >= 0xCC {
                self.mode = GpuMode::TransferringData;
            } else {
                self.mode = GpuMode::HBlank;
            }
        }

        if self.mode != current_mode {
            irq.request(Interrupt::Lcd);
        }

        self.check_coincidence(irq);
    }

    pub fn step(&mut self, irq: &mut Irq, cycles: usize) -> Result<(), String> {
        self.set_status(irq);

        let cycles = cycles as isize;
        if self.enabled {
            self.cycles -= cycles;
        } else {
            return Ok(());
        }

        if self.cycles <= 0 {
            self.ly += 0x01;

            self.cycles = 0x1C8;

            if self.ly == 0x90 {
                irq.request(Interrupt::Vblank);
            } else if self.ly > 0x99 {
                self.ly = 0x00;
            } else if self.ly < 0x90 {
                self.render_scanline();
            }
        }

        Ok(())
    }

    fn render_scanline(&mut self) {
        self.render_background();
        self.render_window();
        self.render_sprites();
        self.frame = self.backbuffer.clone();
    }

    fn render_background(&mut self) {
        requires!(self.background_enabled);

        let options = TileRenderOptions::new(TileRenderType::Background,
                                             self.ly,
                                             self.background_tilemap_addr,
                                             self.tile_data_addr);
        self.render_tile(&options);
    }

    fn render_window(&mut self) {
        requires!(self.window_enabled);
        requires!(self.ly as usize >= self.window_y as usize);

        let options = TileRenderOptions::new(TileRenderType::Window,
                                             self.ly,
                                             self.window_tilemap_addr,
                                             self.tile_data_addr);
        self.render_tile(&options);
    }

    fn render_tile(&mut self, options: &TileRenderOptions) {
        let window = variant_equals!(TileRenderType::Window, options.render_type);
        let y = if window {
            options.line as usize
        } else {
            options.line.wrapping_add(self.scroll_y) as usize
        };

        // If rendering a window tile, we need to make sure we offset tile line properly
        let window_offset = if window { self.window_y as usize } else { 0x00 };
        let map_row = ((y - window_offset) / 0x08) as usize;

        for i in 0..gameboy::SCREEN_WIDTH {
            // if nowhere near the window, skip
            if window && i < self.window_x as usize {
                continue;
            }

            // If we're at the window, lets negate the window X position from where we
            // need to be in the tile map
            let x = if window {
                i as u8 - self.window_x
            } else {
                // Otherwise, scroll the background
                (i as u8).wrapping_add(self.scroll_x)
            };
            let map_col = (x / 0x08) as usize;
            let raw_tile_number = self.ram[options.map_addr + (map_row * 0x20 + map_col)] as usize;

            let line_offset = (y % 0x08) << 0x01;

            let tile_data_start = options.tile_base_addr +
                                  (if options.tile_base_addr == 0x00 {
                raw_tile_number
            } else {
                (raw_tile_number as i8 as i16 + 0x80) as usize
            }) * 0x10 + line_offset;

            let x_shift = (x % 8).wrapping_sub(0x07).wrapping_mul(0xFF);
            let color_value = Self::build_palette_index(&self.ram[tile_data_start..], x_shift);
            let c = self.get_background_color_for_byte(color_value as u8);
            self.backbuffer.pixels[self.ly as usize * gameboy::SCREEN_WIDTH + i as usize] = c;
        }
    }

    fn render_sprites(&mut self) {
        requires!(self.sprites_enabled);

        for i in 0..40 {
            let sprite_table_entry_base = i * 0x04;
            let s_y = self.sprite_data[sprite_table_entry_base] as i16 - 0x10;
            let s_x = self.sprite_data[sprite_table_entry_base + 0x01] as i16 - 0x08;
            let sprite_height = if let SpriteShape::Rectangle = self.sprite_shape {
                0x10
            } else {
                0x08
            };

            if s_y <= self.ly as i16 && (s_y + sprite_height as i16) > self.ly as i16 {
                let tile_number = self.sprite_data[sprite_table_entry_base + 0x02] as i16;
                let attributes = self.sprite_data[sprite_table_entry_base + 0x03];
                let above_background = attributes & 0x80 == 0x00;
                let flip_y = attributes & 0x40 == 0x40;
                let flip_x = attributes & 0x20 == 0x20;

                let sprite_line = (self.ly as i16 - s_y) << 0x01;
                let tile_data_start = (tile_number * 0x10) + sprite_line;
                for x in 0..8 {
                    if s_x + x < 0 || s_x + x >= gameboy::SCREEN_WIDTH as i16 {
                        continue;
                    }
                    let shift = if flip_x { x } else { 0x07 - x };
                    let color_value =
                        Self::build_palette_index(&self.ram[tile_data_start as usize..],
                                                  shift as u8);
                    if color_value == 0x00 {
                        continue;
                    }
                    let c = self.get_sprite_color_for_byte(color_value as u8,
                                                           ((attributes & 0x10) >> 0x04) as u8);
                    let idx = self.ly as usize * gameboy::SCREEN_WIDTH +
                              (s_x as usize + x as usize);
                    if idx > self.backbuffer.pixels.len() - 1 {
                        panic!(format!("Just tried to place a pixel outside framebuffer size. \
                                        Index was {} and backbuffer length is {}. line: {:b}, \
                                        gameboy::SCREEN_WIDTH: {:b}, s_x: {:b}, x: {:b}",
                                       idx,
                                       self.backbuffer.pixels.len(),
                                       self.ly,
                                       gameboy::SCREEN_WIDTH,
                                       s_x,
                                       x));
                    } else {
                        self.backbuffer.pixels[idx] = c;
                    }
                }
            }
        }
    }

    fn get_background_color_for_byte(&self, b: u8) -> Color {
        let palette_index = match b {
            0x00 => self.bg_palette & 0x0003,
            0x01 => (self.bg_palette & 0x000C) >> 0x02,
            0x02 => (self.bg_palette & 0x0030) >> 0x04,
            0x03 => (self.bg_palette & 0x00C0) >> 0x06,
            _ => panic!("err: invalid pixel color value found"),
        };

        Color::from(palette_index as u8)
    }

    fn get_sprite_color_for_byte(&self, b: u8, palette_entry: u8) -> Color {
        let palette = if palette_entry == 0x00 {
            self.palette0
        } else {
            self.palette1
        };

        let palette_index = match b {
            0x00 => palette & 0x0003,
            0x01 => (palette & 0x000C) >> 0x02,
            0x02 => (palette & 0x0030) >> 0x04,
            0x03 => (palette & 0x00C0) >> 0x06,
            _ => panic!("err: invalid pixel color value found"),
        };

        Color::from(palette_index as u8)
    }

    pub fn read_u8(&self, addr: u16) -> u8 {
        match addr {
            0x40 => self.control_register,
            0x41 => self.stat.to_u8(self),
            0x42 => self.scroll_y,
            0x43 => self.scroll_x,
            0x4A => self.window_y,
            0x4B => self.window_x,
            0x44 => self.ly,
            0x45 => self.lyc,
            0x47 => self.bg_palette,
            0x48 => self.palette0,
            0x49 => self.palette1,
            _ => panic!("tried to read GPU memory that is not mapped"),
        }
    }

    pub fn write_u8(&mut self, addr: u16, val: u8) {
        match addr {
            0x40 => {
                self.control_register = val;
                self.enabled = self.control_register & 0x80 == 0x80;
                self.tile_data_addr = if self.control_register & 0x10 == 0x10 {
                    0x00
                } else {
                    0x800
                };
                self.window_tilemap_addr = if self.control_register & 0x40 == 0x40 {
                    0x1C00
                } else {
                    0x1800
                };
                self.background_tilemap_addr = if self.control_register & 0x08 == 0x08 {
                    0x1C00
                } else {
                    0x1800
                };
                self.sprite_shape = if self.control_register & 0x04 == 0x04 {
                    SpriteShape::Rectangle
                } else {
                    SpriteShape::Square
                };
                self.window_enabled = self.control_register & 0x20 == 0x20;
                self.sprites_enabled = self.control_register & 0x02 == 0x02;
                self.background_enabled = self.control_register & 0x01 == 0x01;
            }
            0x41 => self.stat = GpuStat::from(val),
            0x42 => self.scroll_y = val,
            0x43 => self.scroll_x = val,
            0x44 => (),
            0x45 => {
                self.lyc = val;
            }
            0x47 => self.bg_palette = val,
            0x48 => self.palette0 = val,
            0x49 => self.palette1 = val,
            0x4A => self.window_y = val,
            0x4B => self.window_x = val,
            _ => panic!("tried to write GPU memory that is not mapped: {:04}", addr),
        }
    }

    pub fn write_u16(&mut self, addr: u16, val: u16) {
        let addr = addr as usize;
        LittleEndian::write_u16(&mut self.ram[addr..], val)
    }

    fn check_coincidence(&mut self, irq: &mut Irq) {
        // If LY == LYC then set the coincidence flag
        if self.lyc == self.ly && self.stat.coincidence_interrupt_enabled {
            irq.request(Interrupt::Lcd);
        }
    }

    fn build_palette_index(data: &[u8], x_shift: u8) -> u8 {
        let tile_data1 = (data[0x00] >> x_shift) & 0x01;
        let tile_data2 = (data[0x01] >> x_shift) & 0x01;
        let pixel_palette_entry = (tile_data2 << 1) | tile_data1;
        pixel_palette_entry
    }
}
<commit_msg>Fix window X position bug<commit_after>
use byteorder::{ByteOrder, LittleEndian};

use std;

use gameboy;
use gameboy::{Interconnect, Interrupt, Irq, Memory};
use gameboy::gfx::{Color, Frame, GpuMode, GpuStat, SpriteShape, TileRenderOptions, TileRenderType};

pub struct Gpu {
    pub enabled: bool,
    pub ram: Memory,
    pub sprite_data: Memory,

    pub ly: u8,
    pub lyc: u8,

    pub frame: Frame,
    pub backbuffer: Frame,
    pub mode: GpuMode,

    control_register: u8,
    stat: GpuStat,
    scroll_y: u8,
    scroll_x: u8,
    window_y: u8,
    window_x: u8,
    bg_palette: u8,
    palette0: u8,
    palette1: u8,

    cycles: isize,

    counter: u8,
    tile_data_addr: usize,
    background_tilemap_addr: usize,
    window_tilemap_addr: usize,
    sprite_shape: SpriteShape,
    sprites_enabled: bool,
    background_enabled: bool,
    window_enabled: bool,
}

impl Gpu {
    pub fn new() -> Gpu {
        Gpu {
            enabled: true,
            ram: Memory::new(gameboy::gfx::VRAM_SIZE),
            sprite_data: Memory::new(gameboy::gfx::SPRITE_DATA_SIZE),
            control_register: 0x00,
            stat: GpuStat::new(),
            scroll_y: 0x00,
            scroll_x: 0x00,
            window_y: 0x00,
            window_x: 0x00,
            ly: 0x00,
            lyc: 0x00,
            bg_palette: 0x00,
            palette0: 0x00,
            palette1: 0x00,
            cycles: 0x00,
            frame: Frame::new(),
            backbuffer: Frame::new(),
            mode: GpuMode::HBlank,
            counter: 0,
            tile_data_addr: 0x00,
            background_tilemap_addr: 0xC00,
            window_tilemap_addr: 0x00,
            sprite_shape: SpriteShape::Square,
            sprites_enabled: true,
            background_enabled: true,
            window_enabled: true,
        }
    }

    pub fn reset(&mut self) {
        *self = Gpu::new();
    }

    fn set_status(&mut self, irq: &mut Irq) {
        if !self.enabled {
            self.cycles = 0x1C8;
            self.ly = 0x00;
            self.stat = GpuStat::from(0xFD);
        }

        let current_mode = self.mode.clone();

        if self.ly >= 0x90 {
            self.mode = GpuMode::VBlank;
        } else {
            if self.cycles >= 0x178 {
                self.mode = GpuMode::SearchingRam;
            } else if self.cycles >= 0xCC {
                self.mode = GpuMode::TransferringData;
            } else {
                self.mode = GpuMode::HBlank;
            }
        }

        if self.mode != current_mode {
            irq.request(Interrupt::Lcd);
        }

        self.check_coincidence(irq);
    }

    pub fn step(&mut self, irq: &mut Irq, cycles: usize) -> Result<(), String> {
        self.set_status(irq);

        let cycles = cycles as isize;
        if self.enabled {
            self.cycles -= cycles;
        } else {
            return Ok(());
        }

        if self.cycles <= 0 {
            self.ly += 0x01;

            self.cycles = 0x1C8;

            if self.ly == 0x90 {
                irq.request(Interrupt::Vblank);
            } else if self.ly > 0x99 {
                self.ly = 0x00;
            } else if self.ly < 0x90 {
                self.render_scanline();
            }
        }

        Ok(())
    }

    fn render_scanline(&mut self) {
        self.render_background();
        self.render_window();
        self.render_sprites();
        self.frame = self.backbuffer.clone();
    }

    fn render_background(&mut self) {
        requires!(self.background_enabled);

        let options = TileRenderOptions::new(TileRenderType::Background,
                                             self.ly,
                                             self.background_tilemap_addr,
                                             self.tile_data_addr);
        self.render_tile(&options);
    }

    fn render_window(&mut self) {
        requires!(self.window_enabled);
        requires!(self.ly as usize >= self.window_y as usize);

        let options = TileRenderOptions::new(TileRenderType::Window,
                                             self.ly,
                                             self.window_tilemap_addr,
                                             self.tile_data_addr);
        self.render_tile(&options);
    }

    fn render_tile(&mut self, options: &TileRenderOptions) {
        let window = variant_equals!(TileRenderType::Window, options.render_type);
        let y = if window {
            options.line as usize
        } else {
            options.line.wrapping_add(self.scroll_y) as usize
        };

        // If rendering a window tile, we need to make sure we offset tile line properly
        let window_offset = if window { self.window_y as usize } else { 0x00 };
        let map_row = ((y - window_offset) / 0x08) as usize;
        let start = if window {
            // Gameboy manual: "With WX = 7, the window is displayed from the left edge of the LCD screen."
            // If we don't do this, the window begins rendering slightly to the right
            self.window_x.wrapping_sub(0x07) as usize
        } else {
            0
        };

        for i in start..gameboy::SCREEN_WIDTH {

            // If we're at the window, lets negate the window X position from where we
            // need to be in the tile map
            let x = if window {
                i as u8 - self.window_x
            } else {
                // Otherwise, scroll the background
                (i as u8).wrapping_add(self.scroll_x)
            };
            let map_col = (x / 0x08) as usize;
            let raw_tile_number = self.ram[options.map_addr + (map_row * 0x20 + map_col)] as usize;

            let line_offset = (y % 0x08) << 0x01;

            let tile_data_start = options.tile_base_addr +
                                  (if options.tile_base_addr == 0x00 {
                raw_tile_number
            } else {
                (raw_tile_number as i8 as i16 + 0x80) as usize
            }) * 0x10 + line_offset;

            let x_shift = (x % 8).wrapping_sub(0x07).wrapping_mul(0xFF);
            let color_value = Self::build_palette_index(&self.ram[tile_data_start..], x_shift);
            let c = self.get_background_color_for_byte(color_value as u8);
            self.backbuffer.pixels[self.ly as usize * gameboy::SCREEN_WIDTH + i as usize] = c;
        }
    }

    fn render_sprites(&mut self) {
        requires!(self.sprites_enabled);

        for i in 0..40 {
            let sprite_table_entry_base = i * 0x04;
            let s_y = self.sprite_data[sprite_table_entry_base] as i16 - 0x10;
            let s_x = self.sprite_data[sprite_table_entry_base + 0x01] as i16 - 0x08;
            let sprite_height = if let SpriteShape::Rectangle = self.sprite_shape {
                0x10
            } else {
                0x08
            };

            if s_y <= self.ly as i16 && (s_y + sprite_height as i16) > self.ly as i16 {
                let tile_number = self.sprite_data[sprite_table_entry_base + 0x02] as i16;
                let attributes = self.sprite_data[sprite_table_entry_base + 0x03];
                let above_background = attributes & 0x80 == 0x00;
                let flip_y = attributes & 0x40 == 0x40;
                let flip_x = attributes & 0x20 == 0x20;

                let sprite_line = (self.ly as i16 - s_y) << 0x01;
                let tile_data_start = (tile_number * 0x10) + sprite_line;
                for x in 0..8 {
                    if s_x + x < 0 || s_x + x >= gameboy::SCREEN_WIDTH as i16 {
                        continue;
                    }
                    let shift = if flip_x { x } else { 0x07 - x };
                    let color_value =
                        Self::build_palette_index(&self.ram[tile_data_start as usize..],
                                                  shift as u8);
                    if color_value == 0x00 {
                        continue;
                    }
                    let c = self.get_sprite_color_for_byte(color_value as u8,
                                                           ((attributes & 0x10) >> 0x04) as u8);
                    let idx = self.ly as usize * gameboy::SCREEN_WIDTH +
                              (s_x as usize + x as usize);
                    if idx > self.backbuffer.pixels.len() - 1 {
                        panic!(format!("Just tried to place a pixel outside framebuffer size. \
                                        Index was {} and backbuffer length is {}. line: {:b}, \
                                        gameboy::SCREEN_WIDTH: {:b}, s_x: {:b}, x: {:b}",
                                       idx,
                                       self.backbuffer.pixels.len(),
                                       self.ly,
                                       gameboy::SCREEN_WIDTH,
                                       s_x,
                                       x));
                    } else {
                        self.backbuffer.pixels[idx] = c;
                    }
                }
            }
        }
    }

    fn get_background_color_for_byte(&self, b: u8) -> Color {
        let palette_index = match b {
            0x00 => self.bg_palette & 0x0003,
            0x01 => (self.bg_palette & 0x000C) >> 0x02,
            0x02 => (self.bg_palette & 0x0030) >> 0x04,
            0x03 => (self.bg_palette & 0x00C0) >> 0x06,
            _ => panic!("err: invalid pixel color value found"),
        };

        Color::from(palette_index as u8)
    }

    fn get_sprite_color_for_byte(&self, b: u8, palette_entry: u8) -> Color {
        let palette = if palette_entry == 0x00 {
            self.palette0
        } else {
            self.palette1
        };

        let palette_index = match b {
            0x00 => palette & 0x0003,
            0x01 => (palette & 0x000C) >> 0x02,
            0x02 => (palette & 0x0030) >> 0x04,
            0x03 => (palette & 0x00C0) >> 0x06,
            _ => panic!("err: invalid pixel color value found"),
        };

        Color::from(palette_index as u8)
    }

    pub fn read_u8(&self, addr: u16) -> u8 {
        match addr {
            0x40 => self.control_register,
            0x41 => self.stat.to_u8(self),
            0x42 => self.scroll_y,
            0x43 => self.scroll_x,
            0x4A => self.window_y,
            0x4B => self.window_x,
            0x44 => self.ly,
            0x45 => self.lyc,
            0x47 => self.bg_palette,
            0x48 => self.palette0,
            0x49 => self.palette1,
            _ => panic!("tried to read GPU memory that is not mapped"),
        }
    }

    pub fn write_u8(&mut self, addr: u16, val: u8) {
        match addr {
            0x40 => {
                self.control_register = val;
                self.enabled = self.control_register & 0x80 == 0x80;
                self.tile_data_addr = if self.control_register & 0x10 == 0x10 {
                    0x00
                } else {
                    0x800
                };
                self.window_tilemap_addr = if self.control_register & 0x40 == 0x40 {
                    0x1C00
                } else {
                    0x1800
                };
                self.background_tilemap_addr = if self.control_register & 0x08 == 0x08 {
                    0x1C00
                } else {
                    0x1800
                };
                self.sprite_shape = if self.control_register & 0x04 == 0x04 {
                    SpriteShape::Rectangle
                } else {
                    SpriteShape::Square
                };
                self.window_enabled = self.control_register & 0x20 == 0x20;
                self.sprites_enabled = self.control_register & 0x02 == 0x02;
                self.background_enabled = self.control_register & 0x01 == 0x01;
            }
            0x41 => self.stat = GpuStat::from(val),
            0x42 => self.scroll_y = val,
            0x43 => self.scroll_x = val,
            0x44 => (),
            0x45 => {
                self.lyc = val;
            }
            0x47 => self.bg_palette = val,
            0x48 => self.palette0 = val,
            0x49 => self.palette1 = val,
            0x4A => self.window_y = val,
            0x4B => self.window_x = val,
            _ => panic!("tried to write GPU memory that is not mapped: {:04}", addr),
        }
    }

    pub fn write_u16(&mut self, addr: u16, val: u16) {
        let addr = addr as usize;
        LittleEndian::write_u16(&mut self.ram[addr..], val)
    }

    fn check_coincidence(&mut self, irq: &mut Irq) {
        // If LY == LYC then set the coincidence flag
        if self.lyc == self.ly && self.stat.coincidence_interrupt_enabled {
            irq.request(Interrupt::Lcd);
        }
    }

    fn build_palette_index(data: &[u8], x_shift: u8) -> u8 {
        let tile_data1 = (data[0x00] >> x_shift) & 0x01;
        let tile_data2 = (data[0x01] >> x_shift) & 0x01;
        let pixel_palette_entry = (tile_data2 << 1) | tile_data1;
        pixel_palette_entry
    }
}
<|endoftext|>


<commit_before>use std::sync::atomic::{AtomicUsize, Ordering};

use gc::bump::BumpAllocator;
use gc::{align_space, arena};
use gc::{Address, Region};
use os::{self, ProtType};

const SEMI_RATIO: usize = 3;

pub struct YoungGen {
    // bounds of eden & semi-spaces
    total: Region,

    // eden space
    eden: Eden,

    // from/to-space
    semi: SemiSpace,
}

impl YoungGen {
    pub fn new(total: Region, committed_size: usize, protect: bool) -> YoungGen {
        let half_size = total.size() / 2;

        let eden_total = total.start.region_start(half_size);
        let semi_total = Region::new(eden_total.end, total.end);

        let semi_committed = align_space(committed_size / SEMI_RATIO);
        let eden_committed = committed_size - semi_committed;

        let young = YoungGen {
            total: total,
            eden: Eden::new(eden_total, eden_committed),
            semi: SemiSpace::new(semi_total, semi_committed, protect),
        };

        young.commit();

        young
    }

    fn commit(&self) {
        self.eden.commit();
        self.semi.commit();
    }

    pub fn eden_active(&self) -> Region {
        self.eden.active()
    }

    pub fn eden_committed(&self) -> Region {
        self.eden.committed()
    }

    pub fn from_active(&self) -> Region {
        self.semi.from_active()
    }

    pub fn from_committed(&self) -> Region {
        self.semi.from_committed()
    }

    pub fn from_total(&self) -> Region {
        self.semi.from_total()
    }

    pub fn to_committed(&self) -> Region {
        self.semi.to_committed()
    }

    pub fn total(&self) -> Region {
        self.total.clone()
    }

    pub fn contains(&self, addr: Address) -> bool {
        self.total.contains(addr)
    }

    pub fn valid_top(&self, addr: Address) -> bool {
        self.total.valid_top(addr)
    }

    pub fn clear(&self) {
        self.eden.reset_top();
        self.semi.clear_from();
    }

    pub fn active_size(&self) -> usize {
        self.eden.active().size() + self.semi.from_block().active().size()
    }

    pub fn unprotect_to(&self) {
        self.semi.unprotect_to();
    }

    pub fn protect_to(&self) {
        self.semi.protect_to();
    }

    pub fn clear_eden(&self) {
        self.eden.reset_top();
    }

    pub fn swap_semi(&self, top: Address) {
        self.semi.swap(top);
    }

    pub fn should_be_promoted(&self, addr: Address) -> bool {
        debug_assert!(self.total.contains(addr));

        if addr < self.semi.total.start {
            return false;
        }

        self.semi.should_be_promoted(addr)
    }

    pub fn bump_alloc(&self, size: usize) -> Address {
        let eden_ptr = self.eden.bump_alloc(size);

        if eden_ptr.is_non_null() {
            return eden_ptr;
        }

        return self.semi.bump_alloc(size);
    }
}

struct Eden {
    total: Region,
    block: Block,
    alloc: BumpAllocator,
}

impl Eden {
    fn new(total: Region, committed_size: usize) -> Eden {
        let limit = total.start.offset(committed_size);

        Eden {
            total: total.clone(),
            block: Block::new(total.clone(), committed_size),
            alloc: BumpAllocator::new(total.start, limit),
        }
    }

    fn commit(&self) {
        self.block.commit();
    }

    fn total(&self) -> Region {
        self.block.total()
    }

    fn committed(&self) -> Region {
        self.block.committed()
    }

    fn active(&self) -> Region {
        self.block.active()
    }

    fn reset_top(&self) {
        self.block.reset_top();
    }

    fn bump_alloc(&self, size: usize) -> Address {
        self.block.bump_alloc(size)
    }
}

struct SemiSpace {
    total: Region,

    first: Block,
    second: Block,

    // decides whether first or second is the from-space (value 1=first or 2=second)
    from_index: AtomicUsize,

    protect: bool,
    alloc: BumpAllocator,
    age_marker: AtomicUsize,
}

impl SemiSpace {
    fn new(total: Region, committed_size: usize, protect: bool) -> SemiSpace {
        let committed_semi_size = committed_size / 2;

        let semi_size = total.size() / 2;
        assert!(committed_semi_size <= semi_size);

        let first = total.start.region_start(semi_size);
        let second = Region::new(first.end, total.end);
        assert!(first.size() == second.size());

        let limit = total.start.offset(committed_semi_size);

        SemiSpace {
            total: total.clone(),

            first: Block::new(first.clone(), committed_semi_size),
            second: Block::new(second, committed_semi_size),

            from_index: AtomicUsize::new(1),
            protect: protect,
            alloc: BumpAllocator::new(total.start, limit),
            age_marker: AtomicUsize::new(first.start.to_usize()),
        }
    }

    fn commit(&self) {
        self.from_block().commit();
    }

    fn from_block(&self) -> &Block {
        let from_index = self.from_index.load(Ordering::Relaxed);
        if from_index == 1 {
            &self.first
        } else {
            assert!(from_index == 2);
            &self.second
        }
    }

    fn to_block(&self) -> &Block {
        let from_index = self.from_index.load(Ordering::Relaxed);
        if from_index == 1 {
            &self.second
        } else {
            assert!(from_index == 2);
            &self.first
        }
    }

    fn from_total(&self) -> Region {
        self.from_block().total()
    }

    fn from_committed(&self) -> Region {
        self.from_block().committed()
    }

    fn from_active(&self) -> Region {
        self.from_block().active()
    }

    fn to_total(&self) -> Region {
        self.to_block().total()
    }

    fn to_committed(&self) -> Region {
        self.to_block().committed()
    }

    fn to_active(&self) -> Region {
        self.to_block().active()
    }

    // Make to-space writable.
    fn unprotect_to(&self) {
        // make memory writable again, so that we
        // can copy objects to the to-space.
        // Since this has some overhead, do it only in debug builds.

        if cfg!(debug_assertions) || self.protect {
            let to_space = self.to_committed();

            os::mprotect(
                to_space.start.to_ptr::<u8>(),
                to_space.size(),
                ProtType::Writable,
            );
        }
    }

    // Make to-space inaccessible.
    fn protect_to(&self) {
        // Make from-space unaccessible both from read/write.
        // Since this has some overhead, do it only in debug builds.
        if cfg!(debug_assertions) || self.protect {
            let to_space = self.to_committed();
            os::mprotect(
                to_space.start.to_ptr::<u8>(),
                to_space.size(),
                ProtType::None,
            );
        }
    }

    // Free all objects in from-semi-space.
    fn clear_from(&self) {
        let from_space = self.from_block();

        self.age_marker
            .store(from_space.start.to_usize(), Ordering::Relaxed);
        self.from_block().reset_top();
    }

    // Switch from- & to-semi-space.
    fn swap(&self, top: Address) {
        // current from-space is now empty
        self.from_block().reset_top();

        // swap from_index
        self.swap_from_index();

        // new from-space is already used up to `top`.
        self.from_block().set_top(top);
        self.age_marker.store(top.to_usize(), Ordering::Relaxed);
    }

    fn swap_from_index(&self) {
        let from_index = self.from_index.load(Ordering::Relaxed);

        let updated_from_index = if from_index == 1 {
            2
        } else {
            assert!(from_index == 2);
            1
        };

        self.from_index.store(updated_from_index, Ordering::Relaxed);
    }

    fn should_be_promoted(&self, addr: Address) -> bool {
        debug_assert!(self.from_active().contains(addr));
        return addr.to_usize() < self.age_marker.load(Ordering::Relaxed);
    }

    fn bump_alloc(&self, size: usize) -> Address {
        self.from_block().bump_alloc(size)
    }
}

struct Block {
    start: Address,
    end: Address,
    committed: AtomicUsize,
    alloc: BumpAllocator,
}

impl Block {
    fn new(region: Region, committed_size: usize) -> Block {
        assert!(committed_size <= region.size());
        let committed = region.start.offset(committed_size);

        Block {
            start: region.start,
            end: region.end,
            committed: AtomicUsize::new(committed.to_usize()),
            alloc: BumpAllocator::new(region.start, committed),
        }
    }

    fn commit(&self) {
        let committed = self.committed.load(Ordering::Relaxed);
        let size = committed - self.start.to_usize();

        if size > 0 {
            arena::commit(self.start, size, false);
        }
    }

    fn total(&self) -> Region {
        Region::new(self.start, self.end)
    }

    fn committed(&self) -> Region {
        let committed = self.committed.load(Ordering::Relaxed);
        Region::new(self.start, committed.into())
    }

    fn active(&self) -> Region {
        Region::new(self.start, self.alloc.top())
    }

    fn reset_top(&self) {
        let committed = self.committed.load(Ordering::Relaxed);
        self.alloc.reset(self.start, committed.into());
    }

    fn set_top(&self, addr: Address) {
        let committed = self.committed.load(Ordering::Relaxed);
        assert!(self.start <= addr && addr <= committed.into());
        self.alloc.reset(addr, committed.into())
    }

    fn bump_alloc(&self, size: usize) -> Address {
        self.alloc.bump_alloc(size)
    }
}
<commit_msg>swiper: commit to-space<commit_after>use std::sync::atomic::{AtomicUsize, Ordering};

use gc::bump::BumpAllocator;
use gc::{align_space, arena};
use gc::{Address, Region};
use os::{self, ProtType};

const SEMI_RATIO: usize = 3;

pub struct YoungGen {
    // bounds of eden & semi-spaces
    total: Region,

    // eden space
    eden: Eden,

    // from/to-space
    semi: SemiSpace,
}

impl YoungGen {
    pub fn new(total: Region, committed_size: usize, protect: bool) -> YoungGen {
        let half_size = total.size() / 2;

        let eden_total = total.start.region_start(half_size);
        let semi_total = Region::new(eden_total.end, total.end);

        let semi_committed = align_space(committed_size / SEMI_RATIO);
        let eden_committed = committed_size - semi_committed;

        let young = YoungGen {
            total: total,
            eden: Eden::new(eden_total, eden_committed),
            semi: SemiSpace::new(semi_total, semi_committed, protect),
        };

        young.commit();
        young.protect_to();

        young
    }

    fn commit(&self) {
        self.eden.commit();
        self.semi.commit();
    }

    pub fn eden_active(&self) -> Region {
        self.eden.active()
    }

    pub fn eden_committed(&self) -> Region {
        self.eden.committed()
    }

    pub fn from_active(&self) -> Region {
        self.semi.from_active()
    }

    pub fn from_committed(&self) -> Region {
        self.semi.from_committed()
    }

    pub fn from_total(&self) -> Region {
        self.semi.from_total()
    }

    pub fn to_committed(&self) -> Region {
        self.semi.to_committed()
    }

    pub fn total(&self) -> Region {
        self.total.clone()
    }

    pub fn contains(&self, addr: Address) -> bool {
        self.total.contains(addr)
    }

    pub fn valid_top(&self, addr: Address) -> bool {
        self.total.valid_top(addr)
    }

    pub fn clear(&self) {
        self.eden.reset_top();
        self.semi.clear_from();
    }

    pub fn active_size(&self) -> usize {
        self.eden.active().size() + self.semi.from_block().active().size()
    }

    pub fn unprotect_to(&self) {
        self.semi.unprotect_to();
    }

    pub fn protect_to(&self) {
        self.semi.protect_to();
    }

    pub fn clear_eden(&self) {
        self.eden.reset_top();
    }

    pub fn swap_semi(&self, top: Address) {
        self.semi.swap(top);
    }

    pub fn should_be_promoted(&self, addr: Address) -> bool {
        debug_assert!(self.total.contains(addr));

        if addr < self.semi.total.start {
            return false;
        }

        self.semi.should_be_promoted(addr)
    }

    pub fn bump_alloc(&self, size: usize) -> Address {
        let eden_ptr = self.eden.bump_alloc(size);

        if eden_ptr.is_non_null() {
            return eden_ptr;
        }

        return self.semi.bump_alloc(size);
    }
}

struct Eden {
    total: Region,
    block: Block,
    alloc: BumpAllocator,
}

impl Eden {
    fn new(total: Region, committed_size: usize) -> Eden {
        let limit = total.start.offset(committed_size);

        Eden {
            total: total.clone(),
            block: Block::new(total.clone(), committed_size),
            alloc: BumpAllocator::new(total.start, limit),
        }
    }

    fn commit(&self) {
        self.block.commit();
    }

    fn total(&self) -> Region {
        self.block.total()
    }

    fn committed(&self) -> Region {
        self.block.committed()
    }

    fn active(&self) -> Region {
        self.block.active()
    }

    fn reset_top(&self) {
        self.block.reset_top();
    }

    fn bump_alloc(&self, size: usize) -> Address {
        self.block.bump_alloc(size)
    }
}

struct SemiSpace {
    total: Region,

    first: Block,
    second: Block,

    // decides whether first or second is the from-space (value 1=first or 2=second)
    from_index: AtomicUsize,

    protect: bool,
    alloc: BumpAllocator,
    age_marker: AtomicUsize,
}

impl SemiSpace {
    fn new(total: Region, committed_size: usize, protect: bool) -> SemiSpace {
        let committed_semi_size = committed_size / 2;

        let semi_size = total.size() / 2;
        assert!(committed_semi_size <= semi_size);

        let first = total.start.region_start(semi_size);
        let second = Region::new(first.end, total.end);
        assert!(first.size() == second.size());

        let limit = total.start.offset(committed_semi_size);

        SemiSpace {
            total: total.clone(),

            first: Block::new(first.clone(), committed_semi_size),
            second: Block::new(second, committed_semi_size),

            from_index: AtomicUsize::new(1),
            protect: protect,
            alloc: BumpAllocator::new(total.start, limit),
            age_marker: AtomicUsize::new(first.start.to_usize()),
        }
    }

    fn commit(&self) {
        self.from_block().commit();
        self.to_block().commit();
    }

    fn from_block(&self) -> &Block {
        let from_index = self.from_index.load(Ordering::Relaxed);
        if from_index == 1 {
            &self.first
        } else {
            assert!(from_index == 2);
            &self.second
        }
    }

    fn to_block(&self) -> &Block {
        let from_index = self.from_index.load(Ordering::Relaxed);
        if from_index == 1 {
            &self.second
        } else {
            assert!(from_index == 2);
            &self.first
        }
    }

    fn from_total(&self) -> Region {
        self.from_block().total()
    }

    fn from_committed(&self) -> Region {
        self.from_block().committed()
    }

    fn from_active(&self) -> Region {
        self.from_block().active()
    }

    fn to_total(&self) -> Region {
        self.to_block().total()
    }

    fn to_committed(&self) -> Region {
        self.to_block().committed()
    }

    fn to_active(&self) -> Region {
        self.to_block().active()
    }

    // Make to-space writable.
    fn unprotect_to(&self) {
        // make memory writable again, so that we
        // can copy objects to the to-space.
        // Since this has some overhead, do it only in debug builds.

        if cfg!(debug_assertions) || self.protect {
            let to_space = self.to_committed();

            os::mprotect(
                to_space.start.to_ptr::<u8>(),
                to_space.size(),
                ProtType::Writable,
            );
        }
    }

    // Make to-space inaccessible.
    fn protect_to(&self) {
        // Make from-space unaccessible both from read/write.
        // Since this has some overhead, do it only in debug builds.
        if cfg!(debug_assertions) || self.protect {
            let to_space = self.to_committed();
            os::mprotect(
                to_space.start.to_ptr::<u8>(),
                to_space.size(),
                ProtType::None,
            );
        }
    }

    // Free all objects in from-semi-space.
    fn clear_from(&self) {
        let from_space = self.from_block();

        self.age_marker
            .store(from_space.start.to_usize(), Ordering::Relaxed);
        self.from_block().reset_top();
    }

    // Switch from- & to-semi-space.
    fn swap(&self, top: Address) {
        // current from-space is now empty
        self.from_block().reset_top();

        // swap from_index
        self.swap_from_index();

        // new from-space is already used up to `top`.
        self.from_block().set_top(top);
        self.age_marker.store(top.to_usize(), Ordering::Relaxed);
    }

    fn swap_from_index(&self) {
        let from_index = self.from_index.load(Ordering::Relaxed);

        let updated_from_index = if from_index == 1 {
            2
        } else {
            assert!(from_index == 2);
            1
        };

        self.from_index.store(updated_from_index, Ordering::Relaxed);
    }

    fn should_be_promoted(&self, addr: Address) -> bool {
        debug_assert!(self.from_active().contains(addr));
        return addr.to_usize() < self.age_marker.load(Ordering::Relaxed);
    }

    fn bump_alloc(&self, size: usize) -> Address {
        self.from_block().bump_alloc(size)
    }
}

struct Block {
    start: Address,
    end: Address,
    committed: AtomicUsize,
    alloc: BumpAllocator,
}

impl Block {
    fn new(region: Region, committed_size: usize) -> Block {
        assert!(committed_size <= region.size());
        let committed = region.start.offset(committed_size);

        Block {
            start: region.start,
            end: region.end,
            committed: AtomicUsize::new(committed.to_usize()),
            alloc: BumpAllocator::new(region.start, committed),
        }
    }

    fn commit(&self) {
        let committed = self.committed.load(Ordering::Relaxed);
        let size = committed - self.start.to_usize();

        if size > 0 {
            arena::commit(self.start, size, false);
        }
    }

    fn total(&self) -> Region {
        Region::new(self.start, self.end)
    }

    fn committed(&self) -> Region {
        let committed = self.committed.load(Ordering::Relaxed);
        Region::new(self.start, committed.into())
    }

    fn active(&self) -> Region {
        Region::new(self.start, self.alloc.top())
    }

    fn reset_top(&self) {
        let committed = self.committed.load(Ordering::Relaxed);
        self.alloc.reset(self.start, committed.into());
    }

    fn set_top(&self, addr: Address) {
        let committed = self.committed.load(Ordering::Relaxed);
        assert!(self.start <= addr && addr <= committed.into());
        self.alloc.reset(addr, committed.into())
    }

    fn bump_alloc(&self, size: usize) -> Address {
        self.alloc.bump_alloc(size)
    }
}
<|endoftext|>


<commit_before>macro_rules! tx_deref {
    ($ty:ty) => {
        impl ::std::ops::Deref for $ty {
            type Target = Tx;

            fn deref(&self) -> &Tx {
                &self.base
            }
        }
    };
}

macro_rules! tx_sign {
    ($ty:ty) => {
        impl SignTx for $ty {
            fn sign(&self, key_pair: &KeyPair) -> SigPair {
                let mut buf = Vec::new();
                self.encode(&mut buf);
                key_pair.sign(&buf)
            }

            fn append_sign(&mut self, key_pair: &KeyPair) {
                let pair = self.sign(key_pair);
                self.base.signature_pairs.push(pair);
            }
        }
    }
}
<commit_msg>Inline tx util functions<commit_after>macro_rules! tx_deref {
    ($ty:ty) => {
        impl ::std::ops::Deref for $ty {
            type Target = Tx;

            #[inline(always)]
            fn deref(&self) -> &Tx {
                &self.base
            }
        }
    };
}

macro_rules! tx_sign {
    ($ty:ty) => {
        impl SignTx for $ty {
            #[inline]
            fn sign(&self, key_pair: &KeyPair) -> SigPair {
                let mut buf = Vec::new();
                self.encode(&mut buf);
                key_pair.sign(&buf)
            }

            #[inline]
            fn append_sign(&mut self, key_pair: &KeyPair) {
                let pair = self.sign(key_pair);
                self.base.signature_pairs.push(pair);
            }
        }
    }
}
<|endoftext|>