<commit_before>
use super::{ffi, DataSource, Return, Result, Raii, Handle, Connected};
use super::ffi::SQLRETURN::*;
use std::marker::PhantomData;
use std::ptr::null_mut;
use std;

/// `Statement` state used to represent a freshly allocated connection
pub enum Allocated{}
/// `Statement` state used to represet an executed statement
pub enum Executed{}

/// RAII wrapper around ODBC statement
pub struct Statement<'a, S> {
    raii: Raii<ffi::Stmt>,
    // we use phantom data to tell the borrow checker that we need to keep the data source alive
    // for the lifetime of the statement
    parent: PhantomData<&'a DataSource<'a, Connected>>,
    state : PhantomData<S>,
}

impl<'a, S> Handle for Statement<'a, S> {
    type To = ffi::Stmt;
    unsafe fn handle(&self) -> ffi::SQLHSTMT {
        self.raii.handle()
    }
}

impl<'a> Statement<'a, Allocated> {
    pub fn with_parent(ds: &'a mut DataSource<Connected>) -> Result<Statement<'a, Allocated>> {
        let raii = Raii::with_parent(ds).into_result(ds)?;
        let stmt = Statement {
            raii: raii,
            parent: PhantomData,
            state: PhantomData,
        };
        Ok(stmt)
    }

    pub fn tables<'b>(mut self) -> Result<Statement<'a, Executed>> {
        self.raii.tables().into_result(&self)?;
        Ok( Statement{ raii: self.raii, parent: PhantomData, state: PhantomData})
    }

    /// Executes a preparable statement, using the current values of the parameter marker variables
    /// if any parameters exist in the statement.
    ///
    /// `SQLExecDirect` is the fastest way to submit an SQL statement for one-time execution.
    pub fn exec_direct(mut self, statement_text: &str) -> Result<Statement<'a, Executed>> {
        assert!(self.raii.exec_direct(statement_text).into_result(&self)?);
        Ok( Statement{ raii: self.raii, parent: PhantomData, state: PhantomData})
    }
}

impl<'a> Statement<'a, Executed> {

    /// The number of columns in a result set
    ///
    /// Can be called successfully only when the statement is in the prepared, executed, or
    /// positioned state. If the statement does not return columns the result will be 0.
    pub fn num_result_cols(&self) -> Result<i16> {
        self.raii.num_result_cols().into_result(self)
    }

    /// Fetches the next rowset of data from the result set and returns data for all bound columns.
    ///
    /// # Return
    /// Returns false on the last row
    pub fn fetch(&mut self) -> Result<bool> {
        self.raii.fetch().into_result(self)
    }

    /// Retrieves data for a single column in the result set
    pub fn get_data(&mut self, col_or_param_num: u16) -> Result<Option<String>>{
        self.raii.get_data(col_or_param_num).into_result(self)
    }
}

impl Raii<ffi::Stmt> {
    fn num_result_cols(&self) -> Return<i16> {
        let mut num_cols: ffi::SQLSMALLINT = 0;
        unsafe {
            match ffi::SQLNumResultCols(self.handle(), &mut num_cols as *mut ffi::SQLSMALLINT) {
                SQL_SUCCESS => Return::Success(num_cols),
                SQL_SUCCESS_WITH_INFO => Return::SuccessWithInfo(num_cols),
                SQL_ERROR => Return::Error,
                r => panic!("SQLNumResultCols returned unexpected result: {:?}", r),
            }
        }
    }

    fn exec_direct(&mut self, statement_text: &str) -> Return<bool> {
        let length = statement_text.len();
        if length > ffi::SQLINTEGER::max_value() as usize {
            panic!("Statement text too long");
        }
        match unsafe {
                  ffi::SQLExecDirect(self.handle(),
                                     statement_text.as_ptr(),
                                     length as ffi::SQLINTEGER)
              } {
            ffi::SQL_SUCCESS => Return::Success(true),
            ffi::SQL_SUCCESS_WITH_INFO => Return::SuccessWithInfo(true),
            ffi::SQL_ERROR => Return::Error,
            ffi::SQL_NEED_DATA => panic!("SQLExecDirec returned SQL_NEED_DATA"),
            ffi::SQL_NO_DATA => Return::Success(false),
            r => panic!("SQLExecDirect returned unexpected result: {:?}", r),
        }
    }

    /// Fetches the next rowset of data from the result set and returns data for all bound columns.
    fn fetch(&mut self) -> Return<bool> {
        match unsafe { ffi::SQLFetch(self.handle()) } {
            ffi::SQL_SUCCESS => Return::Success(true),
            ffi::SQL_SUCCESS_WITH_INFO => Return::SuccessWithInfo(true),
            ffi::SQL_ERROR => Return::Error,
            ffi::SQL_NO_DATA => Return::Success(false),
            r => panic!("SQLFetch returned unexpected result: {:?}", r),
        }
    }

    fn tables(&mut self) -> Return<()> {
        let catalog_name = "";
        let schema_name = "";
        let table_name = "";
        let table_type = "TABLE";
        unsafe {
            match ffi::SQLTables(self.handle(),
                                 catalog_name.as_ptr(),
                                 catalog_name.as_bytes().len() as ffi::SQLSMALLINT,
                                 schema_name.as_ptr(),
                                 schema_name.as_bytes().len() as ffi::SQLSMALLINT,
                                 table_name.as_ptr(),
                                 table_name.as_bytes().len() as ffi::SQLSMALLINT,
                                 table_type.as_ptr(),
                                 table_type.as_bytes().len() as ffi::SQLSMALLINT) {
                SQL_SUCCESS => Return::Success(()),
                SQL_SUCCESS_WITH_INFO => Return::SuccessWithInfo(()),
                SQL_ERROR => Return::Error,
                r => panic!("SQLTables returned: {:?}", r),
            }
        }
    }

    fn get_data(&mut self, col_or_param_num: u16) -> Return<Option<String>> {
        let mut indicator: ffi::SQLLEN = 0;
        const BUF_LENGTH: ffi::SQLLEN = 512;
        let mut buf = [0u8; BUF_LENGTH as usize];
        unsafe {
            // Get buffer length...
            let result = ffi::SQLGetData(self.handle(),
                                         col_or_param_num,
                                         ffi::SQL_C_CHAR,
                                         buf.as_mut_ptr() as ffi::SQLPOINTER,
                                         BUF_LENGTH,
                                         &mut indicator as *mut ffi::SQLLEN);
            match result {
                ffi::SQL_SUCCESS => {
                    if indicator == ffi::SQL_NULL_DATA {
                        Return::Success(None)
                    } else {
                        Return::Success(Some(std::str::from_utf8(&buf[..(indicator as usize)])
                                                 .unwrap()
                                                 .to_owned()))
                    }
                }
                ffi::SQL_SUCCESS_WITH_INFO => {
                    if indicator == ffi::SQL_NO_TOTAL {
                        Return::SuccessWithInfo(None)
                    } else {
                        // Check if string has been truncated. String is also truncated if indicator is
                        // equal to BUF_LENGTH because of terminating nul
                        if indicator >= BUF_LENGTH {
                            let extra_space = (indicator as usize + 1) - (BUF_LENGTH as usize - 1);
                            let mut heap_buf = Vec::with_capacity((indicator as usize) + 1);
                            // Copy everything but the terminating zero into the new buffer
                            heap_buf.extend_from_slice(&buf[..(BUF_LENGTH as usize) - 1]);
                            // increase length
                            heap_buf.extend(std::iter::repeat(0).take(extra_space));
                            // Get remainder of string
                            let ret = ffi::SQLGetData(self.handle(),
                                                        col_or_param_num,
                                                        ffi::SQL_C_CHAR,
                                                        heap_buf.as_mut_slice()[(BUF_LENGTH as usize) -
                                                        1..]
                                                                .as_mut_ptr() as
                                                        ffi::SQLPOINTER,
                                                        extra_space as ffi::SQLLEN,
                                                        null_mut());
                            heap_buf.pop();
                            let value = String::from_utf8(heap_buf).unwrap();
                            match ret {
                                ffi::SQL_SUCCESS => Return::Success(Some(value)),
                                ffi::SQL_SUCCESS_WITH_INFO => Return::SuccessWithInfo(Some(value)),
                                ffi::SQL_ERROR => Return::Error,
                                r => panic!("SQLGetData returned {:?}", r)
                            }
                        } else {
                            // No truncation. Warning may be due to some other issue.
                            Return::SuccessWithInfo(
                            Some(std::str::from_utf8(&buf[..(indicator as usize)])
                                .unwrap()
                                .to_owned()))
                        }
                    }
                }
                ffi::SQL_ERROR => Return::Error,
                ffi::SQL_NO_DATA => panic!("SQLGetData has already returned the colmun data"),
                _ => panic!("unexpected return value from SQLGetData"),
            }
        }
    }
}

<commit_msg>statement can be constructed from immutable DS<commit_after>
use super::{ffi, DataSource, Return, Result, Raii, Handle, Connected};
use super::ffi::SQLRETURN::*;
use std::marker::PhantomData;
use std::ptr::null_mut;
use std;

/// `Statement` state used to represent a freshly allocated connection
pub enum Allocated{}
/// `Statement` state used to represet an executed statement
pub enum Executed{}

/// RAII wrapper around ODBC statement
pub struct Statement<'a, S> {
    raii: Raii<ffi::Stmt>,
    // we use phantom data to tell the borrow checker that we need to keep the data source alive
    // for the lifetime of the statement
    parent: PhantomData<&'a DataSource<'a, Connected>>,
    state : PhantomData<S>,
}

impl<'a, S> Handle for Statement<'a, S> {
    type To = ffi::Stmt;
    unsafe fn handle(&self) -> ffi::SQLHSTMT {
        self.raii.handle()
    }
}

impl<'a> Statement<'a, Allocated> {
    pub fn with_parent(ds: &'a DataSource<Connected>) -> Result<Statement<'a, Allocated>> {
        let raii = Raii::with_parent(ds).into_result(ds)?;
        let stmt = Statement {
            raii: raii,
            parent: PhantomData,
            state: PhantomData,
        };
        Ok(stmt)
    }

    pub fn tables<'b>(mut self) -> Result<Statement<'a, Executed>> {
        self.raii.tables().into_result(&self)?;
        Ok( Statement{ raii: self.raii, parent: PhantomData, state: PhantomData})
    }

    /// Executes a preparable statement, using the current values of the parameter marker variables
    /// if any parameters exist in the statement.
    ///
    /// `SQLExecDirect` is the fastest way to submit an SQL statement for one-time execution.
    pub fn exec_direct(mut self, statement_text: &str) -> Result<Statement<'a, Executed>> {
        assert!(self.raii.exec_direct(statement_text).into_result(&self)?);
        Ok( Statement{ raii: self.raii, parent: PhantomData, state: PhantomData})
    }
}

impl<'a> Statement<'a, Executed> {

    /// The number of columns in a result set
    ///
    /// Can be called successfully only when the statement is in the prepared, executed, or
    /// positioned state. If the statement does not return columns the result will be 0.
    pub fn num_result_cols(&self) -> Result<i16> {
        self.raii.num_result_cols().into_result(self)
    }

    /// Fetches the next rowset of data from the result set and returns data for all bound columns.
    ///
    /// # Return
    /// Returns false on the last row
    pub fn fetch(&mut self) -> Result<bool> {
        self.raii.fetch().into_result(self)
    }

    /// Retrieves data for a single column in the result set
    pub fn get_data(&mut self, col_or_param_num: u16) -> Result<Option<String>>{
        self.raii.get_data(col_or_param_num).into_result(self)
    }
}

impl Raii<ffi::Stmt> {
    fn num_result_cols(&self) -> Return<i16> {
        let mut num_cols: ffi::SQLSMALLINT = 0;
        unsafe {
            match ffi::SQLNumResultCols(self.handle(), &mut num_cols as *mut ffi::SQLSMALLINT) {
                SQL_SUCCESS => Return::Success(num_cols),
                SQL_SUCCESS_WITH_INFO => Return::SuccessWithInfo(num_cols),
                SQL_ERROR => Return::Error,
                r => panic!("SQLNumResultCols returned unexpected result: {:?}", r),
            }
        }
    }

    fn exec_direct(&mut self, statement_text: &str) -> Return<bool> {
        let length = statement_text.len();
        if length > ffi::SQLINTEGER::max_value() as usize {
            panic!("Statement text too long");
        }
        match unsafe {
                  ffi::SQLExecDirect(self.handle(),
                                     statement_text.as_ptr(),
                                     length as ffi::SQLINTEGER)
              } {
            ffi::SQL_SUCCESS => Return::Success(true),
            ffi::SQL_SUCCESS_WITH_INFO => Return::SuccessWithInfo(true),
            ffi::SQL_ERROR => Return::Error,
            ffi::SQL_NEED_DATA => panic!("SQLExecDirec returned SQL_NEED_DATA"),
            ffi::SQL_NO_DATA => Return::Success(false),
            r => panic!("SQLExecDirect returned unexpected result: {:?}", r),
        }
    }

    /// Fetches the next rowset of data from the result set and returns data for all bound columns.
    fn fetch(&mut self) -> Return<bool> {
        match unsafe { ffi::SQLFetch(self.handle()) } {
            ffi::SQL_SUCCESS => Return::Success(true),
            ffi::SQL_SUCCESS_WITH_INFO => Return::SuccessWithInfo(true),
            ffi::SQL_ERROR => Return::Error,
            ffi::SQL_NO_DATA => Return::Success(false),
            r => panic!("SQLFetch returned unexpected result: {:?}", r),
        }
    }

    fn tables(&mut self) -> Return<()> {
        let catalog_name = "";
        let schema_name = "";
        let table_name = "";
        let table_type = "TABLE";
        unsafe {
            match ffi::SQLTables(self.handle(),
                                 catalog_name.as_ptr(),
                                 catalog_name.as_bytes().len() as ffi::SQLSMALLINT,
                                 schema_name.as_ptr(),
                                 schema_name.as_bytes().len() as ffi::SQLSMALLINT,
                                 table_name.as_ptr(),
                                 table_name.as_bytes().len() as ffi::SQLSMALLINT,
                                 table_type.as_ptr(),
                                 table_type.as_bytes().len() as ffi::SQLSMALLINT) {
                SQL_SUCCESS => Return::Success(()),
                SQL_SUCCESS_WITH_INFO => Return::SuccessWithInfo(()),
                SQL_ERROR => Return::Error,
                r => panic!("SQLTables returned: {:?}", r),
            }
        }
    }

    fn get_data(&mut self, col_or_param_num: u16) -> Return<Option<String>> {
        let mut indicator: ffi::SQLLEN = 0;
        const BUF_LENGTH: ffi::SQLLEN = 512;
        let mut buf = [0u8; BUF_LENGTH as usize];
        unsafe {
            // Get buffer length...
            let result = ffi::SQLGetData(self.handle(),
                                         col_or_param_num,
                                         ffi::SQL_C_CHAR,
                                         buf.as_mut_ptr() as ffi::SQLPOINTER,
                                         BUF_LENGTH,
                                         &mut indicator as *mut ffi::SQLLEN);
            match result {
                ffi::SQL_SUCCESS => {
                    if indicator == ffi::SQL_NULL_DATA {
                        Return::Success(None)
                    } else {
                        Return::Success(Some(std::str::from_utf8(&buf[..(indicator as usize)])
                                                 .unwrap()
                                                 .to_owned()))
                    }
                }
                ffi::SQL_SUCCESS_WITH_INFO => {
                    if indicator == ffi::SQL_NO_TOTAL {
                        Return::SuccessWithInfo(None)
                    } else {
                        // Check if string has been truncated. String is also truncated if indicator is
                        // equal to BUF_LENGTH because of terminating nul
                        if indicator >= BUF_LENGTH {
                            let extra_space = (indicator as usize + 1) - (BUF_LENGTH as usize - 1);
                            let mut heap_buf = Vec::with_capacity((indicator as usize) + 1);
                            // Copy everything but the terminating zero into the new buffer
                            heap_buf.extend_from_slice(&buf[..(BUF_LENGTH as usize) - 1]);
                            // increase length
                            heap_buf.extend(std::iter::repeat(0).take(extra_space));
                            // Get remainder of string
                            let ret = ffi::SQLGetData(self.handle(),
                                                        col_or_param_num,
                                                        ffi::SQL_C_CHAR,
                                                        heap_buf.as_mut_slice()[(BUF_LENGTH as usize) -
                                                        1..]
                                                                .as_mut_ptr() as
                                                        ffi::SQLPOINTER,
                                                        extra_space as ffi::SQLLEN,
                                                        null_mut());
                            heap_buf.pop();
                            let value = String::from_utf8(heap_buf).unwrap();
                            match ret {
                                ffi::SQL_SUCCESS => Return::Success(Some(value)),
                                ffi::SQL_SUCCESS_WITH_INFO => Return::SuccessWithInfo(Some(value)),
                                ffi::SQL_ERROR => Return::Error,
                                r => panic!("SQLGetData returned {:?}", r)
                            }
                        } else {
                            // No truncation. Warning may be due to some other issue.
                            Return::SuccessWithInfo(
                            Some(std::str::from_utf8(&buf[..(indicator as usize)])
                                .unwrap()
                                .to_owned()))
                        }
                    }
                }
                ffi::SQL_ERROR => Return::Error,
                ffi::SQL_NO_DATA => panic!("SQLGetData has already returned the colmun data"),
                _ => panic!("unexpected return value from SQLGetData"),
            }
        }
    }
}

<|endoftext|>


<commit_before>// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// 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.

// error-pattern:binary operation + cannot be applied to type `*int`

fn die() -> *int { (0 as *int) + (0 as *int) }
fn main() { }
<commit_msg>rm silly compile-fail test<commit_after><|endoftext|>


<commit_before>/*
Copyright ⓒ 2017 cargo-script contributors.

Licensed under the MIT license (see LICENSE or <http://opensource.org
/licenses/MIT>) or the Apache License, Version 2.0 (see LICENSE of
<http://www.apache.org/licenses/LICENSE-2.0>), at your option. All
files in the project carrying such notice may not be copied, modified,
or distributed except according to those terms.
*/
/*!
This module contains code related to template support.
*/
use std::borrow::Cow;
use std::collections::HashMap;
use std::fs;
use std::path::PathBuf;
use clap;
use open;
use regex::Regex;
use consts;
use error::{Blame, MainError, Result, ResultExt};
use platform;

lazy_static! {
    static ref RE_SUB: Regex = Regex::new(r#"#\{([A-Za-z_][A-Za-z0-9_]*)}"#).unwrap();
}

#[derive(Debug)]
pub enum Args {
    List,
    Show { path: bool },
}

impl Args {
    pub fn subcommand() -> clap::App<'static, 'static> {
        use clap::{AppSettings, Arg, SubCommand};

        SubCommand::with_name("templates")
            .about("Manage Cargo Script expression templates.")
            .setting(AppSettings::SubcommandRequiredElseHelp)

            .subcommand(SubCommand::with_name("list")
                .about("List the available templates.")
            )

            .subcommand(SubCommand::with_name("show")
                .about("Open the template folder in a file browser.")

                .arg(Arg::with_name("show_path")
                    .help("Output the path to the template folder to standard output instead.")
                    .long("path")
                )
            )
    }

    pub fn parse(m: &clap::ArgMatches) -> Self {
        match m.subcommand() {
            ("list", _) => Args::List,
            ("show", Some(m)) => {
                Args::Show {
                    path: m.is_present("show_path"),
                }
            },
            (name, _) => panic!("bad subcommand: {:?}", name)
        }
    }
}

pub fn try_main(args: Args) -> Result<i32> {
    match args {
        Args::List => try!(list()),
        Args::Show { path } => try!(show(path)),
    }

    Ok(0)
}

pub fn expand(src: &str, subs: &HashMap<&str, &str>) -> Result<String> {
    // The estimate of final size is the sum of the size of all the input.
    let sub_size = subs
        .iter()
        .map(|(_, v)| v.len())
        .sum::<usize>();
    let est_size = src.len() + sub_size;

    let mut anchor = 0;
    let mut result = String::with_capacity(est_size);

    for m in RE_SUB.captures_iter(src) {
        // Concatenate the static bit just before the match.
        let (m_start, m_end) = {
            let m_0 = m.get(0).unwrap();
            (m_0.start(), m_0.end())
        };
        let prior_slice = anchor..m_start;
        anchor = m_end;
        result.push_str(&src[prior_slice]);

        // Concat the substitution.
        let sub_name = m.get(1).unwrap().as_str();
        match subs.get(sub_name) {
            Some(s) => result.push_str(s),
            None => return Err(MainError::OtherOwned(Blame::Human, format!("substitution `{}` in template is unknown", sub_name))),
        }
    }
    result.push_str(&src[anchor..]);
    Ok(result)
}

/**
Returns the path to the template directory.
*/
pub fn get_template_path() -> Result<PathBuf> {
    if cfg!(debug_assertions) {
        use std::env;
        if let Ok(path) = env::var("CARGO_SCRIPT_DEBUG_TEMPLATE_PATH") {
            return Ok(path.into());
        }
    }

    let cache_path = try!(platform::get_config_dir());
    Ok(cache_path.join("script-templates"))
}

/**
Attempts to locate and load the contents of the specified template.
*/
pub fn get_template(name: &str) -> Result<Cow<'static, str>> {
    use std::io::Read;

    let base = try!(get_template_path());

    let file = fs::File::open(base.join(format!("{}.rs", name)))
        .map_err(MainError::from)
        .err_tag(format!("template file `{}.rs` does not exist in {}",
            name,
            base.display()))
        .shift_blame(Blame::Human);

    // If the template is one of the built-in ones, do fallback if it wasn't found on disk.
    if file.is_err() {
        if let Some(text) = builtin_template(name) {
            return Ok(text.into())
        }
    }

    let mut file = try!(file);

    let mut text = String::new();
    try!(file.read_to_string(&mut text));
    Ok(text.into())
}

fn builtin_template(name: &str) -> Option<&'static str> {
    Some(match name {
        "expr" => consts::EXPR_TEMPLATE,
        "file" => consts::FILE_TEMPLATE,
        "loop" => consts::LOOP_TEMPLATE,
        "loop-count" => consts::LOOP_COUNT_TEMPLATE,
        _ => return None,
    })
}

fn list() -> Result<()> {
    use std::ffi::OsStr;

    let t_path = try!(get_template_path());

    if !t_path.exists() {
        return Err(format!("cannot list template directory `{}`: it does not exist", t_path.display()).into());
    }

    if !t_path.is_dir() {
        return Err(format!("cannot list template directory `{}`: it is not a directory", t_path.display()).into());
    }

    for entry in try!(fs::read_dir(&t_path)) {
        let entry = try!(entry);
        if !try!(entry.file_type()).is_file() {
            continue;
        }
        let f_path = entry.path();
        if f_path.extension() != Some(OsStr::new("rs")) {
            continue;
        }
        if let Some(stem) = f_path.file_stem() {
            println!("{}", stem.to_string_lossy());
        }
    }
    Ok(())
}

fn show(path: bool) -> Result<()> {
    let t_path = try!(get_template_path());

    if path {
        println!("{}", t_path.display());
        Ok(())
    } else {
        if !t_path.exists() {
            try!(fs::create_dir_all(&t_path));
        }
        if t_path.is_dir() {
            try!(open::that(&t_path));
        } else {
            return Err(format!("cannot open directory `{}`; it isn't a directory", t_path.display()).into());
        }
        Ok(())
    }
}
<commit_msg>Added command to dump templates.<commit_after>/*
Copyright ⓒ 2017 cargo-script contributors.

Licensed under the MIT license (see LICENSE or <http://opensource.org
/licenses/MIT>) or the Apache License, Version 2.0 (see LICENSE of
<http://www.apache.org/licenses/LICENSE-2.0>), at your option. All
files in the project carrying such notice may not be copied, modified,
or distributed except according to those terms.
*/
/*!
This module contains code related to template support.
*/
use std::borrow::Cow;
use std::collections::HashMap;
use std::fs;
use std::path::PathBuf;
use clap;
use open;
use regex::Regex;
use consts;
use error::{Blame, MainError, Result, ResultExt};
use platform;

lazy_static! {
    static ref RE_SUB: Regex = Regex::new(r#"#\{([A-Za-z_][A-Za-z0-9_]*)}"#).unwrap();
}

#[derive(Debug)]
pub enum Args {
    Dump { name: String },
    List,
    Show { path: bool },
}

impl Args {
    pub fn subcommand() -> clap::App<'static, 'static> {
        use clap::{AppSettings, Arg, SubCommand};

        SubCommand::with_name("templates")
            .about("Manage Cargo Script expression templates.")
            .setting(AppSettings::SubcommandRequiredElseHelp)

            .subcommand(SubCommand::with_name("dump")
                .about("Outputs the contents of a template to standard output.")

                .arg(Arg::with_name("template")
                    .help("Name of template to dump.")
                    .index(1)
                    .required(true)
                )
            )

            .subcommand(SubCommand::with_name("list")
                .about("List the available templates.")
            )

            .subcommand(SubCommand::with_name("show")
                .about("Open the template folder in a file browser.")

                .arg(Arg::with_name("show_path")
                    .help("Output the path to the template folder to standard output instead.")
                    .long("path")
                )
            )
    }

    pub fn parse(m: &clap::ArgMatches) -> Self {
        match m.subcommand() {
            ("dump", Some(m)) => {
                Args::Dump {
                    name: m.value_of("template").unwrap().into(),
                }
            },
            ("list", _) => Args::List,
            ("show", Some(m)) => {
                Args::Show {
                    path: m.is_present("show_path"),
                }
            },
            (name, _) => panic!("bad subcommand: {:?}", name)
        }
    }
}

pub fn try_main(args: Args) -> Result<i32> {
    match args {
        Args::Dump { name } => try!(dump(&name)),
        Args::List => try!(list()),
        Args::Show { path } => try!(show(path)),
    }

    Ok(0)
}

pub fn expand(src: &str, subs: &HashMap<&str, &str>) -> Result<String> {
    // The estimate of final size is the sum of the size of all the input.
    let sub_size = subs
        .iter()
        .map(|(_, v)| v.len())
        .sum::<usize>();
    let est_size = src.len() + sub_size;

    let mut anchor = 0;
    let mut result = String::with_capacity(est_size);

    for m in RE_SUB.captures_iter(src) {
        // Concatenate the static bit just before the match.
        let (m_start, m_end) = {
            let m_0 = m.get(0).unwrap();
            (m_0.start(), m_0.end())
        };
        let prior_slice = anchor..m_start;
        anchor = m_end;
        result.push_str(&src[prior_slice]);

        // Concat the substitution.
        let sub_name = m.get(1).unwrap().as_str();
        match subs.get(sub_name) {
            Some(s) => result.push_str(s),
            None => return Err(MainError::OtherOwned(Blame::Human, format!("substitution `{}` in template is unknown", sub_name))),
        }
    }
    result.push_str(&src[anchor..]);
    Ok(result)
}

/**
Returns the path to the template directory.
*/
pub fn get_template_path() -> Result<PathBuf> {
    if cfg!(debug_assertions) {
        use std::env;
        if let Ok(path) = env::var("CARGO_SCRIPT_DEBUG_TEMPLATE_PATH") {
            return Ok(path.into());
        }
    }

    let cache_path = try!(platform::get_config_dir());
    Ok(cache_path.join("script-templates"))
}

/**
Attempts to locate and load the contents of the specified template.
*/
pub fn get_template(name: &str) -> Result<Cow<'static, str>> {
    use std::io::Read;

    let base = try!(get_template_path());

    let file = fs::File::open(base.join(format!("{}.rs", name)))
        .map_err(MainError::from)
        .err_tag(format!("template file `{}.rs` does not exist in {}",
            name,
            base.display()))
        .shift_blame(Blame::Human);

    // If the template is one of the built-in ones, do fallback if it wasn't found on disk.
    if file.is_err() {
        if let Some(text) = builtin_template(name) {
            return Ok(text.into())
        }
    }

    let mut file = try!(file);

    let mut text = String::new();
    try!(file.read_to_string(&mut text));
    Ok(text.into())
}

fn builtin_template(name: &str) -> Option<&'static str> {
    Some(match name {
        "expr" => consts::EXPR_TEMPLATE,
        "file" => consts::FILE_TEMPLATE,
        "loop" => consts::LOOP_TEMPLATE,
        "loop-count" => consts::LOOP_COUNT_TEMPLATE,
        _ => return None,
    })
}

fn dump(name: &str) -> Result<()> {
    let text = try!(get_template(name));
    print!("{}", text);
    Ok(())
}

fn list() -> Result<()> {
    use std::ffi::OsStr;

    let t_path = try!(get_template_path());

    if !t_path.exists() {
        return Err(format!("cannot list template directory `{}`: it does not exist", t_path.display()).into());
    }

    if !t_path.is_dir() {
        return Err(format!("cannot list template directory `{}`: it is not a directory", t_path.display()).into());
    }

    for entry in try!(fs::read_dir(&t_path)) {
        let entry = try!(entry);
        if !try!(entry.file_type()).is_file() {
            continue;
        }
        let f_path = entry.path();
        if f_path.extension() != Some(OsStr::new("rs")) {
            continue;
        }
        if let Some(stem) = f_path.file_stem() {
            println!("{}", stem.to_string_lossy());
        }
    }
    Ok(())
}

fn show(path: bool) -> Result<()> {
    let t_path = try!(get_template_path());

    if path {
        println!("{}", t_path.display());
        Ok(())
    } else {
        if !t_path.exists() {
            try!(fs::create_dir_all(&t_path));
        }
        if t_path.is_dir() {
            try!(open::that(&t_path));
        } else {
            return Err(format!("cannot open directory `{}`; it isn't a directory", t_path.display()).into());
        }
        Ok(())
    }
}
<|endoftext|>


<commit_before>use event::{Event, Rule, State};
use std::borrow::Cow::Borrowed;

fn is_ascii_whitespace(c: u8) -> bool {
   is_newline(c) || is_ascii_whitespace_no_nl(c)
}

fn is_ascii_whitespace_no_nl(c: u8) -> bool {
    c == b'\t' || c == 0x0b || c == 0x0c || c == b' '
}

fn is_newline(c: u8) -> bool {
    c == b'\n' || c == b'\r'
}

fn isnt_newline(c: u8) -> bool {
    !is_newline(c)
}

// unusual among "scan" functions in that it scans from the _back_ of the string
// TODO: should also scan unicode whitespace?
fn scan_trailing_whitespace(data: &str) -> usize {
    match data.as_bytes().iter().rev().position(|&c| !is_ascii_whitespace_no_nl(c)) {
        Some(i) => i,
        None => data.len()
    }
}

#[derive(Debug)]
pub struct Tokenizer<'a> {
    sass: &'a str,
    bytes: &'a [u8],
    offset: usize,
    stack: Vec<Rule>,
    state: State,
}

impl<'a> Tokenizer<'a> {
    pub fn new(sass: &'a str) -> Tokenizer<'a> {
        Tokenizer {
            sass: &sass,
            bytes: &sass.as_bytes(),
            offset: 0,
            stack: Vec::new(),
            state: State::StartRule,
        }
    }

    fn start_rule(&mut self) -> Option<Event<'a>> {
        self.skip_leading_whitespace();

        if self.offset == self.sass.len() {
            return None
        }

        let c = self.bytes[self.offset];
        if c == b'}' {
            self.offset += 1;
            return Some(self.end())
        }
        if c == b'$' {
            return Some(self.next_variable())
        }

        let d = self.bytes[self.offset + 1];
        if c == b'/' && d == b'*' {
            return Some(self.next_comment())
        }

        self.state = State::InSelectors;
        self.stack.push(Rule::SassRule);

        Some(Event::Start(Rule::SassRule))
    }

    fn end(&mut self) -> Event<'a> {
        let rule = match self.stack.pop() {
            Some(r) => r,
            None => {
                println!("Unexpected empty stack!");
                return Event::End(Rule::SassRule)
            },
        };
        self.state = State::StartRule;
        Event::End(rule)
    }

    fn scan_while<F>(&mut self, data: &str, f: F) -> usize
            where F: Fn(u8) -> bool {
        match data.as_bytes().iter().position(|&c| !f(c)) {
            Some(i) => i,
            None => data.len()
        }
    }

    fn skip_leading_whitespace(&mut self) {
        let mut i = self.offset;
        let limit = self.sass.len();

        while i < limit {
            let c = self.bytes[i];
            if is_ascii_whitespace(c) {
                i += self.scan_while(&self.sass[i..self.sass.len()], is_ascii_whitespace);
            } else if c == b'/' && i + 1 < limit && self.bytes[i + 1] == b'/' {
                i += self.scan_while(&self.sass[i..self.sass.len()], isnt_newline);
            } else {
                self.offset = i;
                return
            }
        }
        self.offset = limit;
    }

    fn next_comment(&mut self) -> Event<'a> {
        let comment_body_beginning = self.offset;
        let mut i = comment_body_beginning + 2;
        let limit = self.sass.len();

        while i < limit {
            match self.bytes[i..limit].iter().position(|&c| c == b'*' ) {
                Some(pos) => { i += pos; },
                None => { break; },
            }

            if self.bytes[i+1] == b'/' {
                self.offset = i + 2;
                return Event::Comment(Borrowed(&self.sass[comment_body_beginning..i + 2]))
            } else {
                i += 1;
            }
        }
        Event::End(Rule::SassRule)
    }

    fn next_variable(&mut self) -> Event<'a> {
        // TODO: can parts of this be deduplicated with properties?
        let name_beginning = self.offset;
        let mut i = name_beginning;
        let limit = self.sass.len();

        while i < limit {
            match self.bytes[i..limit].iter().position(|&c| c == b':' ) {
                Some(pos) => { i += pos; },
                None => { break; },
            }

            let name_end = i;

            i += 1;
            self.offset = i;
            self.skip_leading_whitespace();

            let value_beginning = self.offset;
            i = value_beginning;

            while i < limit {
                match self.bytes[i..limit].iter().position(|&c| c == b';') {
                    Some(pos) => { i += pos; },
                    None => { i = limit; break; },
                }

                let value_end = i;
                self.offset = i + 1;

                self.skip_leading_whitespace();

                return Event::Variable(
                    Borrowed(&self.sass[name_beginning..name_end]),
                    Borrowed(&self.sass[value_beginning..value_end])
                )
            }
        }
        self.offset = self.sass.len();
        Event::Property(Borrowed(""), Borrowed(""))
    }

    fn next_property(&mut self) -> Event<'a> {
        self.skip_leading_whitespace();

        let name_beginning = self.offset;
        let mut i = name_beginning;
        let limit = self.sass.len();

        let c = self.bytes[i];
        if c == b'}' {
            self.offset += 1;
            return self.end()
        }

        let d = self.bytes[i + 1];
        if c == b'/' && d == b'*' {
            return self.next_comment()
        }

        while i < limit {
            match self.bytes[i..limit].iter().position(|&c| c == b':' || c == b'{') {
                Some(pos) => { i += pos; },
                None => { break; },
            }

            // Inefficient since we already skipped the whitespace and we'll have to
            // do it again but oh well
            let c = self.bytes[i];
            if c == b'{' {
                self.state = State::InSelectors;
                self.stack.push(Rule::SassRule);
                return Event::Start(Rule::SassRule)
            }

            let name_end = i;

            i += 1;
            self.offset = i;
            self.skip_leading_whitespace();

            let value_beginning = self.offset;
            i = value_beginning;

            while i < limit {
                match self.bytes[i..limit].iter().position(|&c| c == b';') {
                    Some(pos) => { i += pos; },
                    None => { i = limit; break; },
                }

                let value_end = i;
                self.offset = i + 1;

                self.skip_leading_whitespace();

                return Event::Property(
                    Borrowed(&self.sass[name_beginning..name_end]),
                    Borrowed(&self.sass[value_beginning..value_end])
                )

            }
        }
        self.offset = self.sass.len();
        Event::Property(Borrowed(""), Borrowed(""))
    }

    fn next_selector(&mut self) -> Event<'a> {
        let beginning = self.offset;
        let mut i = beginning;
        let limit = self.sass.len();

        while i < limit {
            match self.bytes[i..limit].iter().position(|&c| c == b',' || c == b'{') {
                Some(pos) => { i += pos; },
                None => { i = limit; break; },
            }

            let c = self.bytes[i];

            if c == b',' || c == b'{' {
                let n = scan_trailing_whitespace(&self.sass[beginning..i]);
                let end = i - n;
                if end > beginning {
                    if c == b'{' {
                        self.state = State::InProperties;
                    }
                    self.offset = i + 1;
                    return Event::Selector(Borrowed(&self.sass[beginning..end]));
                }
            }

            self.offset = i;
            if i > beginning {
                return Event::Selector(Borrowed(&self.sass[beginning..i]))
            }
            i += 1;
        }

        if i > beginning {
            self.offset = i;
            Event::Selector(Borrowed(&self.sass[beginning..i]))
        } else {
            self.end()
        }
    }
}

impl<'a> Iterator for Tokenizer<'a> {
    type Item = Event<'a>;

    fn next(&mut self) -> Option<Event<'a>> {
        if self.offset < self.sass.len() {
            match self.state {
                State::StartRule => {
                    let ret = self.start_rule();
                    if ret.is_some() {
                        return ret
                    }
                },
                State::InSelectors => return Some(self.next_selector()),
                State::InProperties => return Some(self.next_property()),
            }
        }
        None
    }
}
<commit_msg>Clarify a part of code I believe to be unreachable<commit_after>use event::{Event, Rule, State};
use std::borrow::Cow::Borrowed;

fn is_ascii_whitespace(c: u8) -> bool {
   is_newline(c) || is_ascii_whitespace_no_nl(c)
}

fn is_ascii_whitespace_no_nl(c: u8) -> bool {
    c == b'\t' || c == 0x0b || c == 0x0c || c == b' '
}

fn is_newline(c: u8) -> bool {
    c == b'\n' || c == b'\r'
}

fn isnt_newline(c: u8) -> bool {
    !is_newline(c)
}

// unusual among "scan" functions in that it scans from the _back_ of the string
// TODO: should also scan unicode whitespace?
fn scan_trailing_whitespace(data: &str) -> usize {
    match data.as_bytes().iter().rev().position(|&c| !is_ascii_whitespace_no_nl(c)) {
        Some(i) => i,
        None => data.len()
    }
}

#[derive(Debug)]
pub struct Tokenizer<'a> {
    sass: &'a str,
    bytes: &'a [u8],
    offset: usize,
    stack: Vec<Rule>,
    state: State,
}

impl<'a> Tokenizer<'a> {
    pub fn new(sass: &'a str) -> Tokenizer<'a> {
        Tokenizer {
            sass: &sass,
            bytes: &sass.as_bytes(),
            offset: 0,
            stack: Vec::new(),
            state: State::StartRule,
        }
    }

    fn start_rule(&mut self) -> Option<Event<'a>> {
        self.skip_leading_whitespace();

        if self.offset == self.sass.len() {
            return None
        }

        let c = self.bytes[self.offset];
        if c == b'}' {
            self.offset += 1;
            return Some(self.end())
        }
        if c == b'$' {
            return Some(self.next_variable())
        }

        let d = self.bytes[self.offset + 1];
        if c == b'/' && d == b'*' {
            return Some(self.next_comment())
        }

        self.state = State::InSelectors;
        self.stack.push(Rule::SassRule);

        Some(Event::Start(Rule::SassRule))
    }

    fn end(&mut self) -> Event<'a> {
        let rule = match self.stack.pop() {
            Some(r) => r,
            None => {
                println!("Unexpected empty stack!");
                return Event::End(Rule::SassRule)
            },
        };
        self.state = State::StartRule;
        Event::End(rule)
    }

    fn scan_while<F>(&mut self, data: &str, f: F) -> usize
            where F: Fn(u8) -> bool {
        match data.as_bytes().iter().position(|&c| !f(c)) {
            Some(i) => i,
            None => data.len()
        }
    }

    fn skip_leading_whitespace(&mut self) {
        let mut i = self.offset;
        let limit = self.sass.len();

        while i < limit {
            let c = self.bytes[i];
            if is_ascii_whitespace(c) {
                i += self.scan_while(&self.sass[i..self.sass.len()], is_ascii_whitespace);
            } else if c == b'/' && i + 1 < limit && self.bytes[i + 1] == b'/' {
                i += self.scan_while(&self.sass[i..self.sass.len()], isnt_newline);
            } else {
                self.offset = i;
                return
            }
        }
        self.offset = limit;
    }

    fn next_comment(&mut self) -> Event<'a> {
        let comment_body_beginning = self.offset;
        let mut i = comment_body_beginning + 2;
        let limit = self.sass.len();

        while i < limit {
            match self.bytes[i..limit].iter().position(|&c| c == b'*' ) {
                Some(pos) => { i += pos; },
                None => { break; },
            }

            if self.bytes[i+1] == b'/' {
                self.offset = i + 2;
                return Event::Comment(Borrowed(&self.sass[comment_body_beginning..i + 2]))
            } else {
                i += 1;
            }
        }
        unreachable!()
    }

    fn next_variable(&mut self) -> Event<'a> {
        // TODO: can parts of this be deduplicated with properties?
        let name_beginning = self.offset;
        let mut i = name_beginning;
        let limit = self.sass.len();

        while i < limit {
            match self.bytes[i..limit].iter().position(|&c| c == b':' ) {
                Some(pos) => { i += pos; },
                None => { break; },
            }

            let name_end = i;

            i += 1;
            self.offset = i;
            self.skip_leading_whitespace();

            let value_beginning = self.offset;
            i = value_beginning;

            while i < limit {
                match self.bytes[i..limit].iter().position(|&c| c == b';') {
                    Some(pos) => { i += pos; },
                    None => { i = limit; break; },
                }

                let value_end = i;
                self.offset = i + 1;

                self.skip_leading_whitespace();

                return Event::Variable(
                    Borrowed(&self.sass[name_beginning..name_end]),
                    Borrowed(&self.sass[value_beginning..value_end])
                )
            }
        }
        self.offset = self.sass.len();
        Event::Property(Borrowed(""), Borrowed(""))
    }

    fn next_property(&mut self) -> Event<'a> {
        self.skip_leading_whitespace();

        let name_beginning = self.offset;
        let mut i = name_beginning;
        let limit = self.sass.len();

        let c = self.bytes[i];
        if c == b'}' {
            self.offset += 1;
            return self.end()
        }

        let d = self.bytes[i + 1];
        if c == b'/' && d == b'*' {
            return self.next_comment()
        }

        while i < limit {
            match self.bytes[i..limit].iter().position(|&c| c == b':' || c == b'{') {
                Some(pos) => { i += pos; },
                None => { break; },
            }

            // Inefficient since we already skipped the whitespace and we'll have to
            // do it again but oh well
            let c = self.bytes[i];
            if c == b'{' {
                self.state = State::InSelectors;
                self.stack.push(Rule::SassRule);
                return Event::Start(Rule::SassRule)
            }

            let name_end = i;

            i += 1;
            self.offset = i;
            self.skip_leading_whitespace();

            let value_beginning = self.offset;
            i = value_beginning;

            while i < limit {
                match self.bytes[i..limit].iter().position(|&c| c == b';') {
                    Some(pos) => { i += pos; },
                    None => { i = limit; break; },
                }

                let value_end = i;
                self.offset = i + 1;

                self.skip_leading_whitespace();

                return Event::Property(
                    Borrowed(&self.sass[name_beginning..name_end]),
                    Borrowed(&self.sass[value_beginning..value_end])
                )

            }
        }
        self.offset = self.sass.len();
        Event::Property(Borrowed(""), Borrowed(""))
    }

    fn next_selector(&mut self) -> Event<'a> {
        let beginning = self.offset;
        let mut i = beginning;
        let limit = self.sass.len();

        while i < limit {
            match self.bytes[i..limit].iter().position(|&c| c == b',' || c == b'{') {
                Some(pos) => { i += pos; },
                None => { i = limit; break; },
            }

            let c = self.bytes[i];

            if c == b',' || c == b'{' {
                let n = scan_trailing_whitespace(&self.sass[beginning..i]);
                let end = i - n;
                if end > beginning {
                    if c == b'{' {
                        self.state = State::InProperties;
                    }
                    self.offset = i + 1;
                    return Event::Selector(Borrowed(&self.sass[beginning..end]));
                }
            }

            self.offset = i;
            if i > beginning {
                return Event::Selector(Borrowed(&self.sass[beginning..i]))
            }
            i += 1;
        }

        if i > beginning {
            self.offset = i;
            Event::Selector(Borrowed(&self.sass[beginning..i]))
        } else {
            self.end()
        }
    }
}

impl<'a> Iterator for Tokenizer<'a> {
    type Item = Event<'a>;

    fn next(&mut self) -> Option<Event<'a>> {
        if self.offset < self.sass.len() {
            match self.state {
                State::StartRule => {
                    let ret = self.start_rule();
                    if ret.is_some() {
                        return ret
                    }
                },
                State::InSelectors => return Some(self.next_selector()),
                State::InProperties => return Some(self.next_property()),
            }
        }
        None
    }
}
<|endoftext|>


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

use std::iter::Peekable;

#[derive(PartialEq, Debug)]
pub enum Token {
    Comma,
    Number(f64),
    ParenClose,
    ParenOpen,
    Word(String),
}

fn is_whitespace(c: char) -> bool {
    match c {
        '\n' | '\r' | '\t' | ' ' => true,
        _                        => false,
    }
}

fn is_numberlike(c: char) -> bool {
    match c {
        c if c.is_numeric() => true,
        '.' | '-' | '+'     => true,
        _                   => false,
    }
}

pub type PeekableTokens = Peekable<Token, Tokens>;

pub struct Tokens {
    text: String
}

impl Tokens {
    pub fn from_str(input: &str) -> Self {
        Tokens {text: String::from_str(input)}
    }
}

impl Iterator for Tokens {
    type Item = Token;

    fn next(&mut self) -> Option<Token> {  // TODO: should this return Result?
        let next_char = match self.pop_front() {
            Some(c) => c,
            None => return None,
        };

        match next_char {
            '\0' => None,
            '(' => Some(Token::ParenOpen),
            ')' => Some(Token::ParenClose),
            ',' => Some(Token::Comma),
            c if is_whitespace(c) => self.next(),
            c if is_numberlike(c) => {
                let mut number = c.to_string() + &self.read_until_whitespace();
                number = number.trim_left_matches('+').to_string();
                match number.parse::<f64>() {
                    Ok(parsed_num) => Some(Token::Number(parsed_num)),
                    Err(e) => panic!("Could not parse number: {}", e),
                }
            }
            c => {
                let word = c.to_string() + &self.read_until_whitespace();
                Some(Token::Word(word))
            }
        }
    }
}

impl Tokens {
    fn pop_front(&mut self) -> Option<char> {
        match self.text.is_empty() {
            true => None,
            false => Some(self.text.remove(0))
        }
    }

    fn read_until_whitespace(&mut self) -> String {
        let next_char = match self.pop_front() {
            Some(c) => c,
            None => return "".to_string(),
        };

        match next_char {
            '\0' | '(' | ')' | ',' => {
                self.text.insert(0, next_char);
                "".to_string()
            }
            c if is_whitespace(c) => "".to_string(),
            _ => next_char.to_string() + &self.read_until_whitespace(),
        }
    }
}

#[test]
fn test_tokenizer_empty() {
    let test_str = "";
    let tokens: Vec<Token> = Tokens::from_str(test_str).collect();
    assert_eq!(tokens, vec![]);
}

#[test]
fn test_tokenizer_1word() {
    let test_str = "hello";
    let tokens: Vec<Token> = Tokens::from_str(test_str).collect();
    assert_eq!(tokens, vec![
        Token::Word("hello".to_string()),
    ]);
}

#[test]
fn test_tokenizer_2words() {
    let test_str = "hello world";
    let tokens: Vec<Token> = Tokens::from_str(test_str).collect();
    assert_eq!(tokens, vec![
        Token::Word("hello".to_string()),
        Token::Word("world".to_string()),
    ]);
}

#[test]
fn test_tokenizer_1number() {
    let test_str = "4.2";
    let tokens: Vec<Token> = Tokens::from_str(test_str).collect();
    assert_eq!(tokens, vec![
        Token::Number(4.2),
    ]);
}

#[test]
fn test_tokenizer_1number_plus() {
    let test_str = "+4.2";
    let tokens: Vec<Token> = Tokens::from_str(test_str).collect();
    assert_eq!(tokens, vec![
        Token::Number(4.2),
    ]);
}

#[test]
fn test_tokenizer_2numbers() {
    let test_str = ".4 -2";
    let tokens: Vec<Token> = Tokens::from_str(test_str).collect();
    assert_eq!(tokens, vec![
        Token::Number(0.4),
        Token::Number(-2.0),
    ]);
}

#[test]
fn test_tokenizer_point() {
    let test_str = "POINT (10 -20)";
    let tokens: Vec<Token> = Tokens::from_str(test_str).collect();
    assert_eq!(tokens, vec![
        Token::Word("POINT".to_string()),
        Token::ParenOpen,
        Token::Number(10.0),
        Token::Number(-20.0),
        Token::ParenClose,
    ]);
}
<commit_msg>Compile with latest Rust<commit_after>// Copyright 2014-2015 The GeoRust Developers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//	http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::iter::Peekable;

#[derive(PartialEq, Debug)]
pub enum Token {
    Comma,
    Number(f64),
    ParenClose,
    ParenOpen,
    Word(String),
}

fn is_whitespace(c: char) -> bool {
    match c {
        '\n' | '\r' | '\t' | ' ' => true,
        _                        => false,
    }
}

fn is_numberlike(c: char) -> bool {
    match c {
        c if c.is_numeric() => true,
        '.' | '-' | '+'     => true,
        _                   => false,
    }
}

pub type PeekableTokens = Peekable<Tokens>;

pub struct Tokens {
    text: String
}

impl Tokens {
    pub fn from_str(input: &str) -> Self {
        Tokens {text: String::from_str(input)}
    }
}

impl Iterator for Tokens {
    type Item = Token;

    fn next(&mut self) -> Option<Token> {  // TODO: should this return Result?
        let next_char = match self.pop_front() {
            Some(c) => c,
            None => return None,
        };

        match next_char {
            '\0' => None,
            '(' => Some(Token::ParenOpen),
            ')' => Some(Token::ParenClose),
            ',' => Some(Token::Comma),
            c if is_whitespace(c) => self.next(),
            c if is_numberlike(c) => {
                let mut number = c.to_string() + &self.read_until_whitespace();
                number = number.trim_left_matches('+').to_string();
                match number.parse::<f64>() {
                    Ok(parsed_num) => Some(Token::Number(parsed_num)),
                    Err(e) => panic!("Could not parse number: {}", e),
                }
            }
            c => {
                let word = c.to_string() + &self.read_until_whitespace();
                Some(Token::Word(word))
            }
        }
    }
}

impl Tokens {
    fn pop_front(&mut self) -> Option<char> {
        match self.text.is_empty() {
            true => None,
            false => Some(self.text.remove(0))
        }
    }

    fn read_until_whitespace(&mut self) -> String {
        let next_char = match self.pop_front() {
            Some(c) => c,
            None => return "".to_string(),
        };

        match next_char {
            '\0' | '(' | ')' | ',' => {
                self.text.insert(0, next_char);
                "".to_string()
            }
            c if is_whitespace(c) => "".to_string(),
            _ => next_char.to_string() + &self.read_until_whitespace(),
        }
    }
}

#[test]
fn test_tokenizer_empty() {
    let test_str = "";
    let tokens: Vec<Token> = Tokens::from_str(test_str).collect();
    assert_eq!(tokens, vec![]);
}

#[test]
fn test_tokenizer_1word() {
    let test_str = "hello";
    let tokens: Vec<Token> = Tokens::from_str(test_str).collect();
    assert_eq!(tokens, vec![
        Token::Word("hello".to_string()),
    ]);
}

#[test]
fn test_tokenizer_2words() {
    let test_str = "hello world";
    let tokens: Vec<Token> = Tokens::from_str(test_str).collect();
    assert_eq!(tokens, vec![
        Token::Word("hello".to_string()),
        Token::Word("world".to_string()),
    ]);
}

#[test]
fn test_tokenizer_1number() {
    let test_str = "4.2";
    let tokens: Vec<Token> = Tokens::from_str(test_str).collect();
    assert_eq!(tokens, vec![
        Token::Number(4.2),
    ]);
}

#[test]
fn test_tokenizer_1number_plus() {
    let test_str = "+4.2";
    let tokens: Vec<Token> = Tokens::from_str(test_str).collect();
    assert_eq!(tokens, vec![
        Token::Number(4.2),
    ]);
}

#[test]
fn test_tokenizer_2numbers() {
    let test_str = ".4 -2";
    let tokens: Vec<Token> = Tokens::from_str(test_str).collect();
    assert_eq!(tokens, vec![
        Token::Number(0.4),
        Token::Number(-2.0),
    ]);
}

#[test]
fn test_tokenizer_point() {
    let test_str = "POINT (10 -20)";
    let tokens: Vec<Token> = Tokens::from_str(test_str).collect();
    assert_eq!(tokens, vec![
        Token::Word("POINT".to_string()),
        Token::ParenOpen,
        Token::Number(10.0),
        Token::Number(-20.0),
        Token::ParenClose,
    ]);
}
<|endoftext|>


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

use std::iter::Peekable;

#[derive(PartialEq, Debug)]
pub enum Token {
    Comma,
    Number(f64),
    ParenClose,
    ParenOpen,
    Word(String),
}

fn is_whitespace(c: char) -> bool {
    match c {
        '\n' | '\r' | '\t' | ' ' => true,
        _ => false,
    }
}

fn is_numberlike(c: char) -> bool {
    match c {
        c if c.is_numeric() => true,
        '.' | '-' | '+' => true,
        _ => false,
    }
}

pub type PeekableTokens = Peekable<Tokens>;

pub struct Tokens {
    text: String,
}

impl Tokens {
    pub fn from_str(input: &str) -> Self {
        Tokens {
            text: input.to_string(),
        }
    }
}

impl Iterator for Tokens {
    type Item = Token;

    fn next(&mut self) -> Option<Token> {
        // TODO: should this return Result?
        let next_char = match self.pop_front() {
            Some(c) => c,
            None => return None,
        };

        match next_char {
            '\0' => None,
            '(' => Some(Token::ParenOpen),
            ')' => Some(Token::ParenClose),
            ',' => Some(Token::Comma),
            c if is_whitespace(c) => self.next(),
            c if is_numberlike(c) => {
                let mut number = c.to_string() + &self.read_until_whitespace();
                number = number.trim_left_matches('+').to_string();
                match number.parse::<f64>() {
                    Ok(parsed_num) => Some(Token::Number(parsed_num)),
                    Err(e) => panic!("Could not parse number: {}", e),
                }
            }
            c => {
                let word = c.to_string() + &self.read_until_whitespace();
                Some(Token::Word(word))
            }
        }
    }
}

impl Tokens {
    fn pop_front(&mut self) -> Option<char> {
        match self.text.is_empty() {
            true => None,
            false => Some(self.text.remove(0)),
        }
    }

    fn read_until_whitespace(&mut self) -> String {
        let next_char = match self.pop_front() {
            Some(c) => c,
            None => return "".to_string(),
        };

        match next_char {
            '\0' | '(' | ')' | ',' => {
                self.text.insert(0, next_char);
                "".to_string()
            }
            c if is_whitespace(c) => "".to_string(),
            _ => next_char.to_string() + &self.read_until_whitespace(),
        }
    }
}

#[test]
fn test_tokenizer_empty() {
    let test_str = "";
    let tokens: Vec<Token> = Tokens::from_str(test_str).collect();
    assert_eq!(tokens, vec![]);
}

#[test]
fn test_tokenizer_1word() {
    let test_str = "hello";
    let tokens: Vec<Token> = Tokens::from_str(test_str).collect();
    assert_eq!(tokens, vec![Token::Word("hello".to_string())]);
}

#[test]
fn test_tokenizer_2words() {
    let test_str = "hello world";
    let tokens: Vec<Token> = Tokens::from_str(test_str).collect();
    assert_eq!(
        tokens,
        vec![
            Token::Word("hello".to_string()),
            Token::Word("world".to_string()),
        ]
    );
}

#[test]
fn test_tokenizer_1number() {
    let test_str = "4.2";
    let tokens: Vec<Token> = Tokens::from_str(test_str).collect();
    assert_eq!(tokens, vec![Token::Number(4.2)]);
}

#[test]
fn test_tokenizer_1number_plus() {
    let test_str = "+4.2";
    let tokens: Vec<Token> = Tokens::from_str(test_str).collect();
    assert_eq!(tokens, vec![Token::Number(4.2)]);
}

#[test]
fn test_tokenizer_2numbers() {
    let test_str = ".4 -2";
    let tokens: Vec<Token> = Tokens::from_str(test_str).collect();
    assert_eq!(tokens, vec![Token::Number(0.4), Token::Number(-2.0)]);
}

#[test]
fn test_tokenizer_point() {
    let test_str = "POINT (10 -20)";
    let tokens: Vec<Token> = Tokens::from_str(test_str).collect();
    assert_eq!(
        tokens,
        vec![
            Token::Word("POINT".to_string()),
            Token::ParenOpen,
            Token::Number(10.0),
            Token::Number(-20.0),
            Token::ParenClose,
        ]
    );
}
<commit_msg>remove unnecessary allocation (10% improvement)<commit_after>// Copyright 2014-2015 The GeoRust Developers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//	http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::iter::Peekable;

#[derive(PartialEq, Debug)]
pub enum Token {
    Comma,
    Number(f64),
    ParenClose,
    ParenOpen,
    Word(String),
}

fn is_whitespace(c: char) -> bool {
    match c {
        '\n' | '\r' | '\t' | ' ' => true,
        _ => false,
    }
}

fn is_numberlike(c: char) -> bool {
    match c {
        c if c.is_numeric() => true,
        '.' | '-' | '+' => true,
        _ => false,
    }
}

pub type PeekableTokens = Peekable<Tokens>;

pub struct Tokens {
    text: String,
}

impl Tokens {
    pub fn from_str(input: &str) -> Self {
        Tokens {
            text: input.to_string(),
        }
    }
}

impl Iterator for Tokens {
    type Item = Token;

    fn next(&mut self) -> Option<Token> {
        // TODO: should this return Result?
        let next_char = match self.pop_front() {
            Some(c) => c,
            None => return None,
        };

        match next_char {
            '\0' => None,
            '(' => Some(Token::ParenOpen),
            ')' => Some(Token::ParenClose),
            ',' => Some(Token::Comma),
            c if is_whitespace(c) => self.next(),
            c if is_numberlike(c) => {
                let mut number = c.to_string() + &self.read_until_whitespace();
                match number.trim_left_matches('+').parse::<f64>() {
                    Ok(parsed_num) => Some(Token::Number(parsed_num)),
                    Err(e) => panic!("Could not parse number: {}", e),
                }
            }
            c => {
                let word = c.to_string() + &self.read_until_whitespace();
                Some(Token::Word(word))
            }
        }
    }
}

impl Tokens {
    fn pop_front(&mut self) -> Option<char> {
        match self.text.is_empty() {
            true => None,
            false => Some(self.text.remove(0)),
        }
    }

    fn read_until_whitespace(&mut self) -> String {
        let next_char = match self.pop_front() {
            Some(c) => c,
            None => return "".to_string(),
        };

        match next_char {
            '\0' | '(' | ')' | ',' => {
                self.text.insert(0, next_char);
                "".to_string()
            }
            c if is_whitespace(c) => "".to_string(),
            _ => next_char.to_string() + &self.read_until_whitespace(),
        }
    }
}

#[test]
fn test_tokenizer_empty() {
    let test_str = "";
    let tokens: Vec<Token> = Tokens::from_str(test_str).collect();
    assert_eq!(tokens, vec![]);
}

#[test]
fn test_tokenizer_1word() {
    let test_str = "hello";
    let tokens: Vec<Token> = Tokens::from_str(test_str).collect();
    assert_eq!(tokens, vec![Token::Word("hello".to_string())]);
}

#[test]
fn test_tokenizer_2words() {
    let test_str = "hello world";
    let tokens: Vec<Token> = Tokens::from_str(test_str).collect();
    assert_eq!(
        tokens,
        vec![
            Token::Word("hello".to_string()),
            Token::Word("world".to_string()),
        ]
    );
}

#[test]
fn test_tokenizer_1number() {
    let test_str = "4.2";
    let tokens: Vec<Token> = Tokens::from_str(test_str).collect();
    assert_eq!(tokens, vec![Token::Number(4.2)]);
}

#[test]
fn test_tokenizer_1number_plus() {
    let test_str = "+4.2";
    let tokens: Vec<Token> = Tokens::from_str(test_str).collect();
    assert_eq!(tokens, vec![Token::Number(4.2)]);
}

#[test]
fn test_tokenizer_2numbers() {
    let test_str = ".4 -2";
    let tokens: Vec<Token> = Tokens::from_str(test_str).collect();
    assert_eq!(tokens, vec![Token::Number(0.4), Token::Number(-2.0)]);
}

#[test]
fn test_tokenizer_point() {
    let test_str = "POINT (10 -20)";
    let tokens: Vec<Token> = Tokens::from_str(test_str).collect();
    assert_eq!(
        tokens,
        vec![
            Token::Word("POINT".to_string()),
            Token::ParenOpen,
            Token::Number(10.0),
            Token::Number(-20.0),
            Token::ParenClose,
        ]
    );
}
<|endoftext|>


<commit_before>use std::fs::File;
use std::io::prelude::*;
use std::io::stdout;
use std::mem;
use std::ptr;
use std::slice;

use super::value;
use value::{Value, SIZEOF_PAIR, HEADER_TAG, PAIR_HEADER};
#[cfg(debug_assertions)]
use value::Tags;
/// An allocator for RustyScheme objects
pub trait Allocator {
    /// Allocates a vector
    fn alloc_vector(&mut self, &[Value]) -> value::Vector;

    /// Allocates a pair
    fn alloc_pair(&mut self, car: Value, cdr: Value);

    /// Allocates a closure
    fn alloc_closure(&mut self, bytecode: &value::BCO, upvalues: &[Value]) -> value::Closure;

    /// Allocates a record
    fn alloc_record(&mut self,
                    descriptor: &value::RecordDescriptor,
                    fields: &[Value])
                    -> value::Record;

    /// Allocates a hash table
    fn alloc_hash_table(&mut self, size: usize) -> value::HashTable;

    /// Allocates a port
    fn alloc_port(&mut self, File) -> value::IOPort;

    /// Allocates a rustdata, which contains an arbitrary Rust object
    fn alloc_rustdata<T>(&mut self, object: &T) -> value::RustData;

// /// Allocates a boxed float on the top of the stack.
// fn alloc_float(&mut self, float: f64) -> value::Float;
}
#[derive(Debug)]
pub struct Heap {
    tospace: Vec<Value>,
    fromspace: Vec<Value>,
    pub stack: self::Stack,
}

/// Consistency checks on the whole heap (in debug mode only) – sloooow.
#[cfg(debug_assertions)]
unsafe fn consistency_check(heap: &Vec<Value>) {
    let assert_in_heap = |heap: &Vec<_>, ptr: usize| {
        assert!(ptr >= heap.as_ptr() as usize);
        let upper_limit = heap.as_ptr() as usize + heap.capacity()*size_of!(Value);
        assert!(ptr < upper_limit,
                "Heap pointer out of range: {} >= {}", ptr, upper_limit)
    };
    let mut index = 0;
    while index < heap.len() {
        let mut current = heap[index];
        let len = current.contents as usize & !HEADER_TAG;
        assert!(len > 0);
        index += 1;
        for _ in 1..len {
            current = heap[index];
            match current.tag() {
                Tags::Num | Tags::Num2 => {
                    assert!(current.contents & 0b11 == 0);
                }
                Tags::Pair => {
                    assert!(current.contents & 0b111 == 0b111);
                    assert!((*Ptr_Val!(current)).contents == PAIR_HEADER);
                    for i in 1..3 {
                        assert_in_heap(heap, Ptr_Val!(current).offset(i)
                                       as usize)
                    }
                }
                Tags::Vector => {
                    assert!(len > 0);
                    for i in 1..len {
                        assert_in_heap(heap, Ptr_Val!(current) as usize + i)
                    }
                }
                _ => unimplemented!(),
            }
            index += 1;
        }
    }
}
#[cfg(not(debug_assertions))]
unsafe fn consistency_check(_heap: &Vec<Value>) {}

/// Relocates a `Value` in the heap.
///
/// This function relocates a `Value` in the Scheme heap.  It takes two
/// arguments: `current`, the `Value` being relocated, and `end`, the current
/// end of tospace.
///
/// This function takes raw pointers because of aliasing concerns.
unsafe fn relocate(current: *mut Value,
                   tospace: &mut Vec<Value>,
                   fromspace: &mut Vec<Value>) {
    let size_of_value: usize = size_of!(Value);
    (*current).size().map(|size| {
        // pointer to head of object being copied
        let pointer = Ptr_Val!(*current);

        // Assert that the object header is nonzero.
        debug_assert!((*pointer).contents != 0,
                      "internal error: copy_value: invalid object header size");
        if (*pointer).contents == HEADER_TAG {
            // Forwarding pointer detected (this header tag is otherwise absurd,
            // since no object can have a size of zero).
            *current = *pointer.offset(1)
        } else {
            // End pointer
            let end = tospace.as_mut_ptr().offset(tospace.len() as isize);

            if cfg!(feature = "memcpy_gc") {
                debug_assert!(end as usize & 0b111 == 0);
                // The amount to copy
                let amount_to_copy = (size * size_of_value + 0b111) & !0b111;
                debug_assert!(end as usize + amount_to_copy <=
                              tospace.as_ptr() as usize + tospace.capacity());
                debug_assert!(pointer as usize >= end as usize + amount_to_copy ||
                              pointer as usize + amount_to_copy <= end as usize);
                ptr::copy_nonoverlapping(pointer, end, amount_to_copy);
                *current = Value { contents: end as usize | (*current).raw_tag() };
                let len = tospace.len();
                tospace.set_len(len + amount_to_copy / size_of_value);
            } else {
                let amount_to_copy = ((size * size_of_value + 0b111) & !0b111) / size_of_value;

                // Check that the amount to copy is reasonable
                debug_assert!(amount_to_copy > 0);

                // Check that the end pointer is aligned
                debug_assert!(end as usize & 0b111 == 0);

                let ptr = Ptr_Val!(*current);

                // Check that the pointer really is to tospace
                debug_assert!((ptr as usize) <
                              (fromspace.as_ptr() as usize +
                               fromspace.len()*size_of!(usize)));
                debug_assert!(ptr as usize >= fromspace.as_ptr() as usize);
                // NOTE: this MUST come before replacing the old object with
                // a forwarding pointer – otherwise, this replacement will
                // clobber the copied object's header!
                tospace.extend(slice::from_raw_parts(pointer, amount_to_copy));
                *ptr = Value{ contents: HEADER_TAG, };
                *current = Value { contents: end as usize |
                                        ((*current).contents & 0b111) };
                *ptr.offset(1) = Value{ contents: end as usize };
                *ptr.offset(1) = *current;
            }
        }
    });
}

/// Process the heap.
unsafe fn scavange_heap(tospace: &mut Vec<Value>, fromspace: &mut Vec<Value>) {
    let mut offset: isize = 0;
    let current = tospace.as_mut_ptr();
    while offset < tospace.len() as isize {
        let size = (*current.offset(offset)).contents & !HEADER_TAG;
        assert!(size > 0);
        offset += 1;
        if !(*current).leafp() {
            for _ in 1..size {
                relocate(current.offset(offset), tospace, fromspace);
                offset += 1
            }
        }
    }
}

unsafe fn scavange_stack(stack: &mut Vec<Value>,
                         tospace: &mut Vec<Value>,
                         fromspace: &mut Vec<Value>) {
    for i in stack.iter_mut() {
        relocate(i, tospace, fromspace);
    }
}

fn collect(heap: &mut Heap) {
    println!("Initiated garbage collection");
    unsafe {
        consistency_check(&heap.tospace);
        println!("Completed first consistency check");
        mem::swap(&mut heap.tospace, &mut heap.fromspace);
        heap.tospace.reserve(heap.fromspace.len() + heap.fromspace.len()/2);
        println!("Fromspace size is {}", heap.fromspace.len() + heap.fromspace.len() / 2);
        heap.tospace.resize(0, Value{contents:0});
        println!("Tospace resized to {}", heap.tospace.capacity());
        let _ = stdout().flush();
        scavange_stack(&mut heap.stack, &mut heap.tospace, &mut heap.fromspace);
        println!("Stack scavanged");
        scavange_heap(&mut heap.tospace, &mut heap.fromspace);
        println!("Heap scavanged");
        consistency_check(&heap.tospace);
        println!("Completed second consistency check");
        heap.fromspace.resize(0, Value{contents: 0});
    }
}

#[derive(Debug)]
pub struct Stack {
    pub innards: Vec<value::Value>,
}
#[cfg(none)]
impl ::std::ops::Index<usize> for Stack {
    type Output = value::Value;
    fn index(&self, other: u8) -> &value::Value {
        let val: usize = other.into();
        &self.innards[val]
    }
}

use std::ops::{Deref, DerefMut};
impl Deref for Stack {
    type Target = Vec<value::Value>;
    fn deref(&self) -> &Vec<value::Value> {
        &self.innards
    }
}

impl DerefMut for Stack {
    fn deref_mut<'a>(&'a mut self) -> &'a mut Vec<value::Value> {
        &mut self.innards
    }
}

impl Heap {
    /// Allocates a Scheme pair, which must be rooted by the caller.
    pub fn alloc_pair(&mut self, car: Value, cdr: Value) {
        let tospace_space = self.tospace.capacity() - self.tospace.len();
        if tospace_space < SIZEOF_PAIR {
            collect(self);
        }
        self.tospace.push(Value { contents: PAIR_HEADER });
        self.tospace.push(car);
        self.tospace.push(cdr);
        let len = self.tospace.len() - 3;
        let new_value = Value {
            contents: unsafe {
                self.tospace.as_ptr().offset(len as isize) as usize | value::PAIR_TAG
            },
        };
        self.stack.push(new_value);
        println!("Allocated a pair");
    }
    pub fn new(size: usize) -> Self {
        Heap {
            fromspace: Vec::with_capacity(size),
            tospace: Vec::with_capacity(size),
            stack: Stack { innards: Vec::with_capacity(1 << 16) },
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use value::*;
    const ZERO: Value = Value { contents: 0 };
    #[test]
    fn can_allocate_objects() {
        let mut heap = Heap::new(1 << 4);
        super::collect(&mut heap);
        println!("HEADER_TAG = {:x}, PAIR_TAG = {:x}, SIZEOF_PAIR = {:x}",
                 HEADER_TAG,
                 PAIR_HEADER,
                 SIZEOF_PAIR);
        heap.alloc_pair(ZERO, ZERO);
        //println!("{:?}", heap);
        for i in 1..((1 << 11)) {
            let old_pair = heap.stack[0];
            heap.alloc_pair(old_pair, old_pair);
            assert_eq!(heap.stack.len(), 2);
            assert_eq!(heap.stack[1].tag(), Tags::Pair);
            heap.stack[0] = heap.stack.pop().unwrap();
            let assert_valid = |heap: &Heap| {
                let new_pair = heap.stack[0];
                assert_eq!(heap.stack[0].tag(), Tags::Pair);
                assert_eq!(new_pair.size(), Some(3));
                if let EnumValue::Pair(ptr) = new_pair.enum_type() {
                    assert_eq!((unsafe { (*ptr).car.get().tag() }), Tags::Pair);
                    assert_eq!((unsafe { (*ptr).cdr.get().tag() }), Tags::Pair);
                } else {
                    unreachable!()
                }
            };
            assert_valid(&heap);
            super::collect(&mut heap);
            assert_valid(&heap);
            assert!(heap.tospace.len() >= 3*i)
        }
        heap.stack.pop();
        assert!(heap.stack.len() == 0);
        // assert!(heap.fromspace.capacity() > 3* (1 << 20));
        //println!("{:?}", heap);
        {
            super::collect(&mut heap);
            assert!(heap.tospace.len() == 0);
        }
    }
}
<commit_msg>Fixed memcpy_gc and added alloc_vector<commit_after>use std::fs::File;
use std::io::prelude::*;
use std::io::stdout;
use std::mem;
use std::ptr;
use std::slice;

use super::value;
use value::{Value, SIZEOF_PAIR, HEADER_TAG, PAIR_HEADER};
#[cfg(debug_assertions)]
use value::Tags;
/// An allocator for RustyScheme objects
pub trait Allocator {
    /// Allocates a vector
    fn alloc_vector(&mut self, &[Value]) -> value::Vector;

    /// Allocates a pair
    fn alloc_pair(&mut self, car: Value, cdr: Value);

    /// Allocates a closure
    fn alloc_closure(&mut self, bytecode: &value::BCO, upvalues: &[Value]) -> value::Closure;

    /// Allocates a record
    fn alloc_record(&mut self,
                    descriptor: &value::RecordDescriptor,
                    fields: &[Value])
                    -> value::Record;

    /// Allocates a hash table
    fn alloc_hash_table(&mut self, size: usize) -> value::HashTable;

    /// Allocates a port
    fn alloc_port(&mut self, File) -> value::IOPort;

    /// Allocates a rustdata, which contains an arbitrary Rust object
    fn alloc_rustdata<T>(&mut self, object: &T) -> value::RustData;

// /// Allocates a boxed float on the top of the stack.
// fn alloc_float(&mut self, float: f64) -> value::Float;
}

#[derive(Debug)]
pub struct Heap {
    tospace: Vec<Value>,
    fromspace: Vec<Value>,
    pub stack: self::Stack,
}

/// Consistency checks on the whole heap (in debug mode only) – sloooow.
#[cfg(debug_assertions)]
unsafe fn consistency_check(heap: &Vec<Value>) {
    let assert_in_heap = |heap: &Vec<_>, ptr: usize| {
        assert!(ptr >= heap.as_ptr() as usize);
        let upper_limit = heap.as_ptr() as usize + heap.capacity() * size_of!(Value);
        assert!(ptr < upper_limit,
                "Heap pointer out of range: {} >= {}",
                ptr,
                upper_limit)
    };
    let mut index = 0;
    while index < heap.len() {
        let mut current = heap[index];
        let len = current.contents as usize & !HEADER_TAG;
        assert!(len > 0);
        index += 1;
        for _ in 1..len {
            current = heap[index];
            match current.tag() {
                Tags::Num | Tags::Num2 => {
                    assert!(current.contents & 0b11 == 0);
                }
                Tags::Pair => {
                    assert!(current.contents & 0b111 == 0b111);
                    assert!((*Ptr_Val!(current)).contents == PAIR_HEADER);
                    for i in 1..3 {
                        assert_in_heap(heap, Ptr_Val!(current).offset(i) as usize)
                    }
                }
                Tags::Vector => {
                    assert!(len > 0);
                    for i in 1..len {
                        assert_in_heap(heap, Ptr_Val!(current) as usize + i)
                    }
                }
                _ => unimplemented!(),
            }
            index += 1;
        }
    }
}

#[cfg(not(debug_assertions))]
unsafe fn consistency_check(_heap: &Vec<Value>) {}

/// Relocates a `Value` in the heap.
///
/// This function relocates a `Value` in the Scheme heap.  It takes two
/// arguments: `current`, the `Value` being relocated, and `end`, the current
/// end of tospace.
///
/// This function takes raw pointers because of aliasing concerns.
unsafe fn relocate(current: *mut Value, tospace: &mut Vec<Value>, fromspace: &mut Vec<Value>) {
    debug_assert!(tospace.capacity() >= fromspace.len());
    debug!("Tospace capacity: {}, Fromspace length: {}",
           tospace.capacity(),
           fromspace.len());
    let size_of_value: usize = size_of!(Value);
    (*current).size().map(|size| {
        // pointer to head of object being copied
        let pointer: *mut Value = Ptr_Val!(*current);

        // Assert that the object header is nonzero.
        debug_assert!((*pointer).contents != 0,
                      "internal error: copy_value: invalid object header size");
        if (*pointer).contents == HEADER_TAG {
            // Forwarding pointer detected (this header tag is otherwise absurd,
            // since no object can have a size of zero).
            *current = *pointer.offset(1)
        } else {
            let len = tospace.len();

            // End pointer
            let end = tospace.as_mut_ptr().offset(len as isize);

            let amount_to_copy = ((size * size_of_value + 0b111) & !0b111) / size_of_value;

            // Check that the amount to copy is reasonable
            debug_assert!(amount_to_copy > 0);

            // Check that the end pointer is aligned
            debug_assert!(end as usize & 0b111 == 0);

            // Check that the pointer really is to fromspace
            debug_assert!((pointer as usize) <
                          (fromspace.as_ptr() as usize + fromspace.len() * size_of!(usize)));
            debug_assert!(pointer as usize >= fromspace.as_ptr() as usize);

            if cfg!(feature = "memcpy-gc") {
                let words_to_copy = amount_to_copy * size_of_value;
                // The amount to copy
                debug_assert!(amount_to_copy + len <= tospace.capacity());
                debug_assert!(pointer as usize >= end as usize + words_to_copy ||
                              pointer as usize + words_to_copy <= end as usize);
                // NOTE: reverse pointer argument order from `memcpy`.
                ptr::copy_nonoverlapping(pointer, end, amount_to_copy);
                tospace.set_len(len + amount_to_copy)
            } else {
                // NOTE: this MUST come before replacing the old object with
                // a forwarding pointer – otherwise, this replacement will
                // clobber the copied object's header!
                tospace.extend(slice::from_raw_parts(pointer, amount_to_copy));
            }
            *pointer = Value { contents: HEADER_TAG };
            *current = Value { contents: end as usize | ((*current).contents & 0b111) };
            *pointer.offset(1) = *current;
        }
    });
}

/// Process the heap.
unsafe fn scavange_heap(tospace: &mut Vec<Value>, fromspace: &mut Vec<Value>) {
    let mut offset: isize = 0;
    let current = tospace.as_mut_ptr();
    while offset < tospace.len() as isize {
        let size = (*current.offset(offset)).contents & !HEADER_TAG;
        assert!(size > 0);
        offset += 1;
        if !(*current).leafp() {
            for _ in 1..size {
                relocate(current.offset(offset), tospace, fromspace);
                offset += 1
            }
        }
    }
}

/// Handles all of the data on the stack.
unsafe fn scavange_stack(stack: &mut Vec<Value>,
                         tospace: &mut Vec<Value>,
                         fromspace: &mut Vec<Value>) {
    for i in stack.iter_mut() {
        relocate(i, tospace, fromspace);
    }
}

/// Performs a full garbage collection
fn collect(heap: &mut Heap) {
    debug!("Initiated garbage collection");
    unsafe {
        consistency_check(&heap.tospace);
        debug!("Completed first consistency check");
        mem::swap(&mut heap.tospace, &mut heap.fromspace);
        heap.tospace.reserve(heap.fromspace.len() + heap.fromspace.len() / 2);
        debug!("Fromspace size is {}",
               heap.fromspace.len() + heap.fromspace.len() / 2);
        heap.tospace.resize(0, Value { contents: 0 });
        debug!("Tospace resized to {}", heap.tospace.capacity());
        let _ = stdout().flush();
        scavange_stack(&mut heap.stack, &mut heap.tospace, &mut heap.fromspace);
        debug!("Stack scavanged");
        scavange_heap(&mut heap.tospace, &mut heap.fromspace);
        debug!("Heap scavanged");
        consistency_check(&heap.tospace);
        debug!("Completed second consistency check");
        heap.fromspace.resize(0, Value { contents: 0 });
    }
}

/// Represents the stack.
#[derive(Debug)]
pub struct Stack {
    pub innards: Vec<value::Value>,
}

use std::ops::{Deref, DerefMut};

/// A `Stack` acts like a `Vec`.
impl Deref for Stack {
    type Target = Vec<value::Value>;
    fn deref(&self) -> &Vec<value::Value> {
        &self.innards
    }
}

/// A `Stack` acts like a `Vec`.
impl DerefMut for Stack {
    fn deref_mut<'a>(&'a mut self) -> &'a mut Vec<value::Value> {
        &mut self.innards
    }
}

impl Heap {
    /// Allocates a Scheme pair, which must be rooted by the caller.
    pub fn alloc_pair(&mut self, car: Value, cdr: Value) {
        let tospace_space = self.tospace.capacity() - self.tospace.len();
        if tospace_space < SIZEOF_PAIR {
            collect(self);
        }
        self.tospace.push(Value { contents: PAIR_HEADER });
        self.tospace.push(car);
        self.tospace.push(cdr);
        let len = self.tospace.len() - 3;
        let new_value = Value {
            contents: unsafe {
                self.tospace.as_ptr().offset(len as isize) as usize | value::PAIR_TAG
            },
        };
        self.stack.push(new_value);
        debug!("Allocated a pair")
    }

    pub fn alloc_vector(&mut self, elements: &[Value]) {
        let len = elements.len();
        let tospace_space = self.tospace.capacity() - self.tospace.len();
        if tospace_space < (elements.len() + 0b111) & !0b111 {
            collect(self)
        }
        self.tospace.push(Value { contents: value::VECTOR_HEADER | elements.len() });
        let ptr = unsafe {
            self.tospace.as_ptr().offset(len as isize) as usize | value::VECTOR_TAG
        };
        self.tospace.extend(elements);
        self.stack.push(Value { contents: ptr });
    }

    pub fn new(size: usize) -> Self {
        Heap {
            fromspace: Vec::with_capacity(size),
            tospace: Vec::with_capacity(size),
            stack: Stack { innards: Vec::with_capacity(1 << 16) },
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use value::*;
    const ZERO: Value = Value { contents: 0 };
    #[test]
    fn can_allocate_objects() {
        let mut heap = Heap::new(1 << 4);
        super::collect(&mut heap);
        debug!("HEADER_TAG = {:x}, PAIR_TAG = {:x}, SIZEOF_PAIR = {:x}",
               HEADER_TAG,
               PAIR_HEADER,
               SIZEOF_PAIR);
        heap.alloc_pair(ZERO, ZERO);
        // debug!("{:?}", heap);
        for i in 1..((1 << 11)) {
            let old_pair = heap.stack[0];
            heap.alloc_pair(old_pair, old_pair);
            assert_eq!(heap.stack.len(), 2);
            assert_eq!(heap.stack[1].tag(), Tags::Pair);
            heap.stack[0] = heap.stack.pop().unwrap();
            let assert_valid = |heap: &Heap| {
                let new_pair = heap.stack[0];
                assert_eq!(heap.stack[0].tag(), Tags::Pair);
                assert_eq!(new_pair.size(), Some(3));
                if let EnumValue::Pair(ptr) = new_pair.enum_type() {
                    assert_eq!((unsafe { (*ptr).car.get().tag() }), Tags::Pair);
                    assert_eq!((unsafe { (*ptr).cdr.get().tag() }), Tags::Pair)
                } else {
                    unreachable!()
                }
            };
            assert_valid(&heap);
            super::collect(&mut heap);
            assert_valid(&heap);
            assert!(heap.tospace.len() >= 3 * i)
        }
        heap.stack.pop();
        assert!(heap.stack.len() == 0);
        // assert!(heap.fromspace.capacity() > 3* (1 << 20));
        // debug!("{:?}", heap);
        super::collect(&mut heap);
        assert!(heap.tospace.len() == 0)
    }
}
<|endoftext|>