<commit_before>#![crate_name = "bindgen"]
#![crate_type = "bin"]

extern crate bindgen;
#[macro_use] extern crate log;
extern crate syntax;

use bindgen::{Bindings, BindgenOptions, LinkType, Logger};
use std::{io, os, path};
use std::default::Default;
use std::io::fs;

struct StdLogger;

impl Logger for StdLogger {
    fn error(&self, msg: &str) {
        error!("{}", msg);
    }

    fn warn(&self, msg: &str) {
        warn!("{}", msg);
    }
}

enum ParseResult {
    CmdUsage,
    ParseOk(BindgenOptions, Box<io::Writer+'static>),
    ParseErr(String)
}

fn parse_args(args: &[String]) -> ParseResult {
    let args_len = args.len();

    let mut options: BindgenOptions = Default::default();
    let mut out = Box::new(io::BufferedWriter::new(io::stdout())) as Box<io::Writer>;

    if args_len == 0 {
        return ParseResult::CmdUsage;
    }

    let mut ix = 0us;
    while ix < args_len {
        if args[ix].len() > 2 && args[ix].as_slice().slice_to(2) == "-l" {
            options.links.push((args[ix].as_slice().slice_from(2).to_string(), LinkType::Default));
            ix += 1;
        } else {
            match args[ix].as_slice() {
                "--help" | "-h" => {
                    return ParseResult::CmdUsage;
                }
                "-emit-clang-ast" => {
                    options.emit_ast = true;
                    ix += 1;
                }
                "-o" => {
                    if ix + 1 >= args_len {
                        return ParseResult::ParseErr("Missing output filename".to_string());
                    }
                    let path = path::Path::new(args[ix + 1].clone());
                    match fs::File::create(&path) {
                        Ok(f) => { out = Box::new(io::BufferedWriter::new(f)) as Box<io::Writer>; }
                        Err(_) => { return ParseResult::ParseErr(format!("Open {} failed", args[ix + 1])); }
                    }
                    ix += 2;
                }
                "-l" => {
                    if ix + 1 >= args_len {
                        return ParseResult::ParseErr("Missing link name".to_string());
                    }
                    options.links.push((args[ix + 1].clone(), LinkType::Default));
                    ix += 2;
                }
                "-static-link" => {
                    if ix + 1 >= args_len {
                        return ParseResult::ParseErr("Missing link name".to_string());
                    }
                    options.links.push((args[ix + 1].clone(), LinkType::Static));
                    ix += 2;
                }
                "-framework-link" => {
                    if ix + 1 >= args_len {
                        return ParseResult::ParseErr("Missing link name".to_string());
                    }
                    options.links.push((args[ix + 1].clone(), LinkType::Framework));
                    ix += 2;
                }
                "-match" => {
                    if ix + 1 >= args_len {
                        return ParseResult::ParseErr("Missing match pattern".to_string());
                    }
                    options.match_pat.push(args[ix + 1].clone());
                    ix += 2;
                }
                "-builtins" => {
                    options.builtins = true;
                    ix += 1;
                }
                "-allow-unknown-types" => {
                    options.fail_on_unknown_type = false;
                    ix += 1;
                }
                "-override-enum-type" => {
                    if ix + 1 >= args_len {
                        return ParseResult::ParseErr("Missing enum type".to_string());
                    }
                    options.override_enum_ty = args[ix + 1].clone();
                    ix += 2;
                }
                _ => {
                    options.clang_args.push(args[ix].clone());
                    ix += 1;
                }
            }
        }
    }

    return ParseResult::ParseOk(options, out);
}

fn print_usage(bin: String) {
    let mut s = format!("Usage: {} [options] input.h", bin.as_slice());
    s.push_str(
"
Options:
    -h or --help               Display help message
    -l <name> or -l<name>      Link to a dynamic library, can be proivded
                               multiple times
    -static-link <name>        Link to a static library
    -framework-link <name>     Link to a framework
    -o <output.rs>             Write bindings to <output.rs> (default stdout)
    -match <name>              Only output bindings for definitions from files
                               whose name contains <name>
                               If multiple -match options are provided, files
                               matching any rule are bound to.
    -builtins                  Output bindings for builtin definitions
                               (for example __builtin_va_list)
    -allow-unknown-types       Don't fail if we encounter types we do not support,
                               instead treat them as void
    -emit-clang-ast            Output the ast (for debugging purposes)
    -override-enum-type <type> Override enum type, type name could be
                                 uchar
                                 schar
                                 ushort
                                 sshort
                                 uint
                                 sint
                                 ulong
                                 slong
                                 ulonglong
                                 slonglong

    Options other than stated above are passed to clang.
"
    );
    io::stdio::print(s.as_slice());
}

pub fn main() {
    let mut bind_args = os::args();
    let bin = bind_args.remove(0);

    match parse_args(bind_args.as_slice()) {
        ParseResult::ParseErr(e) => panic!(e),
        ParseResult::CmdUsage => print_usage(bin),
        ParseResult::ParseOk(options, mut out) => {
            let logger = StdLogger;
            match Bindings::generate(&options, Some(&logger as &Logger), None) {
                Ok(bindings) => match bindings.write(&mut out) {
                    Ok(()) => (),
                    Err(e) => {
                        logger.error(format!("Unable to write bindings to file. {}", e).as_slice());
                        os::set_exit_status(-1);
                    }
                },
                Err(()) => os::set_exit_status(-1)
            }
        }
    }
}
<commit_msg>Update bindgen bin for io -> old_io too.<commit_after>#![crate_name = "bindgen"]
#![crate_type = "bin"]

extern crate bindgen;
#[macro_use] extern crate log;
extern crate syntax;

use bindgen::{Bindings, BindgenOptions, LinkType, Logger};
use std::old_io as io;
use std::{os, path};
use std::default::Default;
use std::old_io::fs;

struct StdLogger;

impl Logger for StdLogger {
    fn error(&self, msg: &str) {
        error!("{}", msg);
    }

    fn warn(&self, msg: &str) {
        warn!("{}", msg);
    }
}

enum ParseResult {
    CmdUsage,
    ParseOk(BindgenOptions, Box<io::Writer+'static>),
    ParseErr(String)
}

fn parse_args(args: &[String]) -> ParseResult {
    let args_len = args.len();

    let mut options: BindgenOptions = Default::default();
    let mut out = Box::new(io::BufferedWriter::new(io::stdout())) as Box<io::Writer>;

    if args_len == 0 {
        return ParseResult::CmdUsage;
    }

    let mut ix = 0us;
    while ix < args_len {
        if args[ix].len() > 2 && args[ix].as_slice().slice_to(2) == "-l" {
            options.links.push((args[ix].as_slice().slice_from(2).to_string(), LinkType::Default));
            ix += 1;
        } else {
            match args[ix].as_slice() {
                "--help" | "-h" => {
                    return ParseResult::CmdUsage;
                }
                "-emit-clang-ast" => {
                    options.emit_ast = true;
                    ix += 1;
                }
                "-o" => {
                    if ix + 1 >= args_len {
                        return ParseResult::ParseErr("Missing output filename".to_string());
                    }
                    let path = path::Path::new(args[ix + 1].clone());
                    match fs::File::create(&path) {
                        Ok(f) => { out = Box::new(io::BufferedWriter::new(f)) as Box<io::Writer>; }
                        Err(_) => { return ParseResult::ParseErr(format!("Open {} failed", args[ix + 1])); }
                    }
                    ix += 2;
                }
                "-l" => {
                    if ix + 1 >= args_len {
                        return ParseResult::ParseErr("Missing link name".to_string());
                    }
                    options.links.push((args[ix + 1].clone(), LinkType::Default));
                    ix += 2;
                }
                "-static-link" => {
                    if ix + 1 >= args_len {
                        return ParseResult::ParseErr("Missing link name".to_string());
                    }
                    options.links.push((args[ix + 1].clone(), LinkType::Static));
                    ix += 2;
                }
                "-framework-link" => {
                    if ix + 1 >= args_len {
                        return ParseResult::ParseErr("Missing link name".to_string());
                    }
                    options.links.push((args[ix + 1].clone(), LinkType::Framework));
                    ix += 2;
                }
                "-match" => {
                    if ix + 1 >= args_len {
                        return ParseResult::ParseErr("Missing match pattern".to_string());
                    }
                    options.match_pat.push(args[ix + 1].clone());
                    ix += 2;
                }
                "-builtins" => {
                    options.builtins = true;
                    ix += 1;
                }
                "-allow-unknown-types" => {
                    options.fail_on_unknown_type = false;
                    ix += 1;
                }
                "-override-enum-type" => {
                    if ix + 1 >= args_len {
                        return ParseResult::ParseErr("Missing enum type".to_string());
                    }
                    options.override_enum_ty = args[ix + 1].clone();
                    ix += 2;
                }
                _ => {
                    options.clang_args.push(args[ix].clone());
                    ix += 1;
                }
            }
        }
    }

    return ParseResult::ParseOk(options, out);
}

fn print_usage(bin: String) {
    let mut s = format!("Usage: {} [options] input.h", bin.as_slice());
    s.push_str(
"
Options:
    -h or --help               Display help message
    -l <name> or -l<name>      Link to a dynamic library, can be proivded
                               multiple times
    -static-link <name>        Link to a static library
    -framework-link <name>     Link to a framework
    -o <output.rs>             Write bindings to <output.rs> (default stdout)
    -match <name>              Only output bindings for definitions from files
                               whose name contains <name>
                               If multiple -match options are provided, files
                               matching any rule are bound to.
    -builtins                  Output bindings for builtin definitions
                               (for example __builtin_va_list)
    -allow-unknown-types       Don't fail if we encounter types we do not support,
                               instead treat them as void
    -emit-clang-ast            Output the ast (for debugging purposes)
    -override-enum-type <type> Override enum type, type name could be
                                 uchar
                                 schar
                                 ushort
                                 sshort
                                 uint
                                 sint
                                 ulong
                                 slong
                                 ulonglong
                                 slonglong

    Options other than stated above are passed to clang.
"
    );
    io::stdio::print(s.as_slice());
}

pub fn main() {
    let mut bind_args = os::args();
    let bin = bind_args.remove(0);

    match parse_args(bind_args.as_slice()) {
        ParseResult::ParseErr(e) => panic!(e),
        ParseResult::CmdUsage => print_usage(bin),
        ParseResult::ParseOk(options, mut out) => {
            let logger = StdLogger;
            match Bindings::generate(&options, Some(&logger as &Logger), None) {
                Ok(bindings) => match bindings.write(&mut out) {
                    Ok(()) => (),
                    Err(e) => {
                        logger.error(format!("Unable to write bindings to file. {}", e).as_slice());
                        os::set_exit_status(-1);
                    }
                },
                Err(()) => os::set_exit_status(-1)
            }
        }
    }
}
<|endoftext|>


<commit_before>use common::util::*;

static UTIL_NAME: &'static str = "echo";
fn new_ucmd() -> UCommand {
    TestScenario::new(UTIL_NAME).ucmd()
}

#[test]
fn test_default() {
    assert_eq!(new_ucmd()
        .run().stdout, "\n");
}

#[test]
fn test_no_trailing_newline() {
    new_ucmd()
        .arg("-n")
        .arg("hello_world")
        .run()
        .stdout_is("hello_world");
}

#[test]
fn test_enable_escapes() {
    new_ucmd()
        .arg("-e")
        .arg("\\\\\\t\\r")
        .run()
        .stdout_is("\\\t\r\n");
}

#[test]
fn test_disable_escapes() {
    new_ucmd()
        .arg("-E")
        .arg("\\b\\c\\e")
        .run()
        .stdout_is("\\b\\c\\e\n");
}
<commit_msg>tests/echo: complete tests excepting help, version<commit_after>use common::util::*;

static UTIL_NAME: &'static str = "echo";
fn new_ucmd() -> UCommand {
    TestScenario::new(UTIL_NAME).ucmd()
}

#[test]
fn test_default() {
    //CmdResult.stdout_only(...) trims trailing newlines
    assert_eq!("hi\n", new_ucmd().arg("hi").succeeds().no_stderr().stdout);
}

#[test]
fn test_no_trailing_newline() {
    //CmdResult.stdout_only(...) trims trailing newlines
    assert_eq!("hi", new_ucmd().arg("-n").arg("hi").succeeds().no_stderr().stdout);
}

#[test]
fn test_escape_alert() {
    new_ucmd().args(&["-e", "\\a"]).succeeds().stdout_only("\x07\n");
}

#[test]
fn test_escape_backslash() {
    new_ucmd().args(&["-e", "\\\\"]).succeeds().stdout_only("\\\n");
}

#[test]
fn test_escape_backspace() {
    new_ucmd().args(&["-e", "\\b"]).succeeds().stdout_only("\x08\n");
}

#[test]
fn test_escape_carriage_return() {
    new_ucmd().args(&["-e", "\\r"]).succeeds().stdout_only("\r\n");
}

#[test]
fn test_escape_escape() {
    new_ucmd().args(&["-e", "\\e"]).succeeds().stdout_only("\x1B\n");
}

#[test]
fn test_escape_form_feed() {
    new_ucmd().args(&["-e", "\\f"]).succeeds().stdout_only("\x0C\n");
}

#[test]
fn test_escape_hex() {
    new_ucmd().args(&["-e", "\\x41"]).succeeds().stdout_only("A");
}

#[test]
fn test_escape_newline() {
    new_ucmd().args(&["-e", "\\na"]).succeeds().stdout_only("\na");
}

#[test]
fn test_escape_no_further_output() {
    new_ucmd().args(&["-e", "a\\cb"]).succeeds().stdout_only("a\n");
}

#[test]
fn test_escape_octal() {
    new_ucmd().args(&["-e", "\\0100"]).succeeds().stdout_only("@");
}

#[test]
fn test_escape_tab() {
    new_ucmd().args(&["-e", "\\t"]).succeeds().stdout_only("\t\n");
}

#[test]
fn test_escape_vertical_tab() {
    new_ucmd().args(&["-e", "\\v"]).succeeds().stdout_only("\x0B\n");
}

#[test]
fn test_disable_escapes() {
    let input_str = "\\a \\\\ \\b \\r \\e \\f \\x41 \\n a\\cb \\u0100 \\t \\v";
    new_ucmd()
        .arg("-E")
        .arg(input_str)
        .succeeds()
        .stdout_only(format!("{}\n", input_str));
}
<|endoftext|>


<commit_before>use std;
use std::iter::Peekable;

use ::opcodes::{AddressingMode, OpCode};
use assembler::token::{LexerToken, ParserToken};

#[derive(Debug, PartialEq)]
pub struct ParserError {
    pub message: String,
}

impl ParserError {
    fn expected_instruction(line: u32) -> ParserError {
        ParserError::from(format!("Instruction expected. Line {}", line))
    }

    fn invalid_opcode_addressing_mode_combination(line: u32) -> ParserError {
        ParserError::from(format!("Invalid addressing mode for opcode. Line {}", line))
    }

    fn unexpected_eol(line: u32) -> ParserError {
        ParserError::from(format!("Unexpected end of line. Line {}", line))
    }

    fn expected_eol(line: u32) -> ParserError {
        ParserError::from(format!("Expected end of line. Line {}", line))
    }

    fn cannot_parse_address(line: u32) -> ParserError {
        ParserError::from(format!("Unable to parse address. Line {}", line))
    }

    fn unexpected_token(line: u32) -> ParserError {
        ParserError::from(format!("Unexpected token. Line {}", line))
    }
}

impl From<String> for ParserError {
    fn from(error: String) -> ParserError {
        ParserError { message: error }
    }
}

impl<'a> From<&'a str> for ParserError {
    fn from(error: &str) -> ParserError {
        ParserError { message: error.into() }
    }
}

pub struct Parser {
    line: u32,
}

/// Parser processes a list of 6502 Assembly tokens
impl Parser {
    pub fn new() -> Parser {
        Parser { line: 0 }
    }

    pub fn parse(&mut self, tokens: Vec<Vec<LexerToken>>) -> Result<Vec<ParserToken>, ParserError> {
        let mut result = Vec::new();

        for line in &tokens {
            self.line += 1;

            let mut peeker = line.iter().peekable();

            // Skip blank lines
            if let None = peeker.peek() {
                continue;
            }

            let next = *peeker.peek().unwrap();

            if let &LexerToken::Ident(ref ident) = next {
                // Check if this is an opcode
                if Self::is_opcode(ident.clone()) {
                    // Yep its an opcode, lets figure out its addressing mode
                    let mut opcode = self.consume_opcode(&mut peeker, ident.clone())?;
                    result.append(&mut opcode);
                } else {
                    // Skip the ident and we'll check what is next
                    let original_ident = peeker.next().unwrap();
                    // if there is nothing else - lets mark this as a Label and move on
                    if let None = peeker.peek() {
                        result.push(ParserToken::Label(ident.clone()));
                        continue;
                    }

                    // A colon after the ident also indicates a label
                    let next = *peeker.peek().unwrap();
                    if let &LexerToken::Colon = next {
                        result.push(ParserToken::Label(ident.clone()));
                        continue;
                    }

                    // Is the next one a label as well? Thats an error:
                    if let &LexerToken::Ident(ref ident) = next {
                        // Lets add the original as a label
                        if let &LexerToken::Ident(ref original_ident) = original_ident {
                            result.push(ParserToken::Label(original_ident.clone()));
                        }

                        if !Self::is_opcode(ident.clone()) {
                            return Err(ParserError::expected_instruction(self.line));
                        } else {
                            // Oh it is an opcode after the label - consume it
                            let mut opcode = self.consume_opcode(&mut peeker, ident.clone())?;
                            result.append(&mut opcode);
                        }
                    }
                }
            }
        }

        Ok(result)
    }

    fn is_opcode<S>(mnemonic: S) -> bool
        where S: Into<String>
    {
        if let Some(opcode) = OpCode::from_mnemonic(mnemonic) {
            true
        } else {
            false
        }
    }

    fn consume_opcode<'a, I, S>(&mut self,
                                mut peeker: &mut Peekable<I>,
                                ident: S)
                                -> Result<Vec<ParserToken>, ParserError>
        where I: Iterator<Item = &'a LexerToken>,
              S: Into<String> + std::fmt::Display
    {
        // Jump over the opcode
        peeker.next();

        // If there is nothing else after this opcode.. lets check if there is
        // a matching opcode with an implied addressing mode
        if let None = peeker.peek() {
            if let Some(opcode) =
                   OpCode::from_mnemonic_and_addressing_mode(ident, AddressingMode::Implied) {
                return Ok(vec![ParserToken::OpCode(opcode)]);
            } else {
                return Err(ParserError::invalid_opcode_addressing_mode_combination(self.line));
            }
        } else {
            // Check the next token, is it an address?
            let next = *peeker.peek().unwrap();
            if let &LexerToken::Address(ref address) = next {
                // Its an address. What sort of address?
                if address.len() == 2 || address.len() == 4 {
                    // Its zero-page or absolute.. lets try and convert it to a raw byte
                    let (bytes, addressing_mode) = if address.len() == 2 {
                        // Its a 1 byte address
                        if let Ok(raw_byte) = u8::from_str_radix(&address[..], 16) {
                            (vec![raw_byte], AddressingMode::ZeroPage)
                        } else {
                            return Err(ParserError::cannot_parse_address(self.line));
                        }
                    } else {
                        // Its a 2 byte address
                        if let Ok(low_byte) = u8::from_str_radix(&address[2..], 16) {
                            if let Ok(high_byte) = u8::from_str_radix(&address[0..2], 16) {
                                (vec![low_byte, high_byte], AddressingMode::Absolute)
                            } else {
                                return Err(ParserError::cannot_parse_address(self.line));
                            }
                        } else {
                            return Err(ParserError::cannot_parse_address(self.line));
                        }
                    };
                    // consume the address and peek what is next:
                    peeker.next();
                    if let None = peeker.peek() {
                        // Nothing else.. find an opcode with this ident and addressing mode
                        if let Some(opcode) =
                               OpCode::from_mnemonic_and_addressing_mode(ident, addressing_mode) {
                            // We found one..
                            let mut final_vec = vec![ParserToken::OpCode(opcode)];
                            // Push the address bytes into the result
                            for b in bytes {
                                final_vec.push(ParserToken::RawByte(b));
                            }
                            return Ok(final_vec);
                        } else {
                            return Err(ParserError::invalid_opcode_addressing_mode_combination(self.line));
                        }
                    }

                    // There is something after this address - if its
                    // a comma, then we're peachy. If its something else.. Thats
                    // an error.
                    let next = *peeker.peek().unwrap();
                    if let &LexerToken::Comma = next {
                        // Yes, its a comma. Consume it and check what is next
                        peeker.next();
                        // If theres nothing after the comma thats an error
                        if let None = peeker.peek() {
                            return Err(ParserError::unexpected_eol(self.line));
                        }

                        let next = *peeker.peek().unwrap();
                        if let &LexerToken::Ident(ref register) = next {
                            let register = register.to_uppercase();
                            if register != "X" && register != "Y" {
                                return Err(ParserError::unexpected_token(self.line));
                            }
                            let addressing_mode = if register == "X" {
                                if addressing_mode == AddressingMode::ZeroPage {
                                    AddressingMode::ZeroPageX
                                } else {
                                    AddressingMode::AbsoluteX
                                }
                            } else {
                                if addressing_mode == AddressingMode::ZeroPage {
                                    AddressingMode::ZeroPageY
                                } else {
                                    AddressingMode::AbsoluteY
                                }
                            };
                            if let Some(opcode) =
                                   OpCode::from_mnemonic_and_addressing_mode(ident, addressing_mode) {
                                // We found one..
                                let mut final_vec = vec![ParserToken::OpCode(opcode)];
                                // Push the address bytes into the result
                                for b in bytes {
                                    final_vec.push(ParserToken::RawByte(b));
                                }
                                return Ok(final_vec);
                            } else {
                                return Err(ParserError::invalid_opcode_addressing_mode_combination(self.line));
                            }
                        } else {
                            return Err(ParserError::unexpected_token(self.line));
                        }
                    } else {
                        return Err(ParserError::unexpected_token(self.line));
                    }
                    let next = *peeker.peek().unwrap();

                } else {
                    return Err(ParserError::cannot_parse_address(self.line));
                }
            }
            Ok(vec![ParserToken::Label("BLAH".into())])
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use ::assembler::token::{ImmediateBase, LexerToken, ParserToken};
    use ::opcodes::{AddressingMode, OpCode};

    #[test]
    fn can_parse_labels_via_lonely_label() {
        let tokens = vec![vec![LexerToken::Ident("MAIN".into())],
                          vec![LexerToken::Ident("START".into())]];

        let mut parser = Parser::new();
        let result = parser.parse(tokens).unwrap();

        assert_eq!(&[ParserToken::Label("MAIN".into()), ParserToken::Label("START".into())],
                   &result[..]);
    }

    #[test]
    fn can_parse_labels_via_colon_terminator() {
        let tokens = vec![vec![LexerToken::Ident("MAIN".into())], vec![LexerToken::Colon]];

        let mut parser = Parser::new();
        let result = parser.parse(tokens).unwrap();

        assert_eq!(&[ParserToken::Label("MAIN".into())], &result[..]);
    }

    #[test]
    fn can_parse_opcodes_after_labels_on_one_line() {
        let tokens = vec![vec![LexerToken::Ident("MAIN".into()),
                               LexerToken::Ident("LDA".into()),
                               LexerToken::Address("4400".into())]];

        let mut parser = Parser::new();
        let result = parser.parse(tokens).unwrap();

        assert_eq!(&[ParserToken::Label("MAIN".into()),
                     ParserToken::OpCode(OpCode::from_mnemonic_and_addressing_mode("LDA", AddressingMode::Absolute).unwrap()),
                     ParserToken::RawByte(0),
                     ParserToken::RawByte(68)],
                   &result[..]);
    }

    #[test]
    fn can_detect_double_labels_on_one_line() {
        let tokens = vec![vec![LexerToken::Ident("MAIN".into()),
                               LexerToken::Ident("START".into())]];

        let mut parser = Parser::new();
        let result = parser.parse(tokens);

        assert_eq!(Err(ParserError::expected_instruction(1)), result);
    }

    #[test]
    fn can_detect_opcode_with_implied_addressing_mode() {
        let tokens = vec![vec![LexerToken::Ident("CLC".into())]];

        let mut parser = Parser::new();
        let result = parser.parse(tokens).unwrap();

        assert_eq!(&[ParserToken::OpCode(OpCode::from_mnemonic_and_addressing_mode("CLC", AddressingMode::Implied).unwrap())], &result[..]);
    }

    #[test]
    fn can_detect_opcode_with_correct_absolute_x_addressing_mode() {
        let tokens = vec![vec![LexerToken::Ident("MAIN".into()),
                               LexerToken::Ident("LDA".into()),
                               LexerToken::Address("4400".into()),
                               LexerToken::Comma,
                               LexerToken::Ident("X".into())]];

        let mut parser = Parser::new();
        let result = parser.parse(tokens).unwrap();

        assert_eq!(&[ParserToken::Label("MAIN".into()),
                     ParserToken::OpCode(OpCode::from_mnemonic_and_addressing_mode("LDA", AddressingMode::AbsoluteX).unwrap()),
                     ParserToken::RawByte(0),
                     ParserToken::RawByte(68)],
                   &result[..]);
    }

    #[test]
    fn errors_on_incorrect_zero_page_y_usage() {
        // LDA does not support the ZeroPageY addressing mode
        let tokens = vec![vec![LexerToken::Ident("LDA".into()),
                               LexerToken::Address("44".into()),
                               LexerToken::Comma,
                               LexerToken::Ident("Y".into())]];

        let mut parser = Parser::new();
        let result = parser.parse(tokens);

        assert_eq!(Err(ParserError::invalid_opcode_addressing_mode_combination(1)),
                   result);
    }

    #[test]
    fn can_handle_correct_zero_page_y_usage() {
        // LDX does support the ZeroPageY addressing mode
        let tokens = vec![vec![LexerToken::Ident("LDX".into()),
                               LexerToken::Address("44".into()),
                               LexerToken::Comma,
                               LexerToken::Ident("Y".into())]];

        let mut parser = Parser::new();
        let result = parser.parse(tokens).unwrap();

        assert_eq!(&[
                     ParserToken::OpCode(OpCode::from_mnemonic_and_addressing_mode("LDX", AddressingMode::ZeroPageY).unwrap()),
                     ParserToken::RawByte(68)],
                   &result[..]);
    }
}<commit_msg>Rename 'detect' tests to 'parse'<commit_after>use std;
use std::iter::Peekable;

use ::opcodes::{AddressingMode, OpCode};
use assembler::token::{LexerToken, ParserToken};

#[derive(Debug, PartialEq)]
pub struct ParserError {
    pub message: String,
}

impl ParserError {
    fn expected_instruction(line: u32) -> ParserError {
        ParserError::from(format!("Instruction expected. Line {}", line))
    }

    fn invalid_opcode_addressing_mode_combination(line: u32) -> ParserError {
        ParserError::from(format!("Invalid addressing mode for opcode. Line {}", line))
    }

    fn unexpected_eol(line: u32) -> ParserError {
        ParserError::from(format!("Unexpected end of line. Line {}", line))
    }

    fn expected_eol(line: u32) -> ParserError {
        ParserError::from(format!("Expected end of line. Line {}", line))
    }

    fn cannot_parse_address(line: u32) -> ParserError {
        ParserError::from(format!("Unable to parse address. Line {}", line))
    }

    fn unexpected_token(line: u32) -> ParserError {
        ParserError::from(format!("Unexpected token. Line {}", line))
    }
}

impl From<String> for ParserError {
    fn from(error: String) -> ParserError {
        ParserError { message: error }
    }
}

impl<'a> From<&'a str> for ParserError {
    fn from(error: &str) -> ParserError {
        ParserError { message: error.into() }
    }
}

pub struct Parser {
    line: u32,
}

/// Parser processes a list of 6502 Assembly tokens
impl Parser {
    pub fn new() -> Parser {
        Parser { line: 0 }
    }

    pub fn parse(&mut self, tokens: Vec<Vec<LexerToken>>) -> Result<Vec<ParserToken>, ParserError> {
        let mut result = Vec::new();

        for line in &tokens {
            self.line += 1;

            let mut peeker = line.iter().peekable();

            // Skip blank lines
            if let None = peeker.peek() {
                continue;
            }

            let next = *peeker.peek().unwrap();

            if let &LexerToken::Ident(ref ident) = next {
                // Check if this is an opcode
                if Self::is_opcode(ident.clone()) {
                    // Yep its an opcode, lets figure out its addressing mode
                    let mut opcode = self.consume_opcode(&mut peeker, ident.clone())?;
                    result.append(&mut opcode);
                } else {
                    // Skip the ident and we'll check what is next
                    let original_ident = peeker.next().unwrap();
                    // if there is nothing else - lets mark this as a Label and move on
                    if let None = peeker.peek() {
                        result.push(ParserToken::Label(ident.clone()));
                        continue;
                    }

                    // A colon after the ident also indicates a label
                    let next = *peeker.peek().unwrap();
                    if let &LexerToken::Colon = next {
                        result.push(ParserToken::Label(ident.clone()));
                        continue;
                    }

                    // Is the next one a label as well? Thats an error:
                    if let &LexerToken::Ident(ref ident) = next {
                        // Lets add the original as a label
                        if let &LexerToken::Ident(ref original_ident) = original_ident {
                            result.push(ParserToken::Label(original_ident.clone()));
                        }

                        if !Self::is_opcode(ident.clone()) {
                            return Err(ParserError::expected_instruction(self.line));
                        } else {
                            // Oh it is an opcode after the label - consume it
                            let mut opcode = self.consume_opcode(&mut peeker, ident.clone())?;
                            result.append(&mut opcode);
                        }
                    }
                }
            }
        }

        Ok(result)
    }

    fn is_opcode<S>(mnemonic: S) -> bool
        where S: Into<String>
    {
        if let Some(opcode) = OpCode::from_mnemonic(mnemonic) {
            true
        } else {
            false
        }
    }

    fn consume_opcode<'a, I, S>(&mut self,
                                mut peeker: &mut Peekable<I>,
                                ident: S)
                                -> Result<Vec<ParserToken>, ParserError>
        where I: Iterator<Item = &'a LexerToken>,
              S: Into<String> + std::fmt::Display
    {
        // Jump over the opcode
        peeker.next();

        // If there is nothing else after this opcode.. lets check if there is
        // a matching opcode with an implied addressing mode
        if let None = peeker.peek() {
            if let Some(opcode) =
                   OpCode::from_mnemonic_and_addressing_mode(ident, AddressingMode::Implied) {
                return Ok(vec![ParserToken::OpCode(opcode)]);
            } else {
                return Err(ParserError::invalid_opcode_addressing_mode_combination(self.line));
            }
        } else {
            // Check the next token, is it an address?
            let next = *peeker.peek().unwrap();
            if let &LexerToken::Address(ref address) = next {
                // Its an address. What sort of address?
                if address.len() == 2 || address.len() == 4 {
                    // Its zero-page or absolute.. lets try and convert it to a raw byte
                    let (bytes, addressing_mode) = if address.len() == 2 {
                        // Its a 1 byte address
                        if let Ok(raw_byte) = u8::from_str_radix(&address[..], 16) {
                            (vec![raw_byte], AddressingMode::ZeroPage)
                        } else {
                            return Err(ParserError::cannot_parse_address(self.line));
                        }
                    } else {
                        // Its a 2 byte address
                        if let Ok(low_byte) = u8::from_str_radix(&address[2..], 16) {
                            if let Ok(high_byte) = u8::from_str_radix(&address[0..2], 16) {
                                (vec![low_byte, high_byte], AddressingMode::Absolute)
                            } else {
                                return Err(ParserError::cannot_parse_address(self.line));
                            }
                        } else {
                            return Err(ParserError::cannot_parse_address(self.line));
                        }
                    };
                    // consume the address and peek what is next:
                    peeker.next();
                    if let None = peeker.peek() {
                        // Nothing else.. find an opcode with this ident and addressing mode
                        if let Some(opcode) =
                               OpCode::from_mnemonic_and_addressing_mode(ident, addressing_mode) {
                            // We found one..
                            let mut final_vec = vec![ParserToken::OpCode(opcode)];
                            // Push the address bytes into the result
                            for b in bytes {
                                final_vec.push(ParserToken::RawByte(b));
                            }
                            return Ok(final_vec);
                        } else {
                            return Err(ParserError::invalid_opcode_addressing_mode_combination(self.line));
                        }
                    }

                    // There is something after this address - if its
                    // a comma, then we're peachy. If its something else.. Thats
                    // an error.
                    let next = *peeker.peek().unwrap();
                    if let &LexerToken::Comma = next {
                        // Yes, its a comma. Consume it and check what is next
                        peeker.next();
                        // If theres nothing after the comma thats an error
                        if let None = peeker.peek() {
                            return Err(ParserError::unexpected_eol(self.line));
                        }

                        let next = *peeker.peek().unwrap();
                        if let &LexerToken::Ident(ref register) = next {
                            let register = register.to_uppercase();
                            if register != "X" && register != "Y" {
                                return Err(ParserError::unexpected_token(self.line));
                            }
                            let addressing_mode = if register == "X" {
                                if addressing_mode == AddressingMode::ZeroPage {
                                    AddressingMode::ZeroPageX
                                } else {
                                    AddressingMode::AbsoluteX
                                }
                            } else {
                                if addressing_mode == AddressingMode::ZeroPage {
                                    AddressingMode::ZeroPageY
                                } else {
                                    AddressingMode::AbsoluteY
                                }
                            };
                            if let Some(opcode) =
                                   OpCode::from_mnemonic_and_addressing_mode(ident, addressing_mode) {
                                // We found one..
                                let mut final_vec = vec![ParserToken::OpCode(opcode)];
                                // Push the address bytes into the result
                                for b in bytes {
                                    final_vec.push(ParserToken::RawByte(b));
                                }
                                return Ok(final_vec);
                            } else {
                                return Err(ParserError::invalid_opcode_addressing_mode_combination(self.line));
                            }
                        } else {
                            return Err(ParserError::unexpected_token(self.line));
                        }
                    } else {
                        return Err(ParserError::unexpected_token(self.line));
                    }
                    let next = *peeker.peek().unwrap();

                } else {
                    return Err(ParserError::cannot_parse_address(self.line));
                }
            }
            Ok(vec![ParserToken::Label("BLAH".into())])
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use ::assembler::token::{ImmediateBase, LexerToken, ParserToken};
    use ::opcodes::{AddressingMode, OpCode};

    #[test]
    fn can_parse_labels_via_lonely_label() {
        let tokens = vec![vec![LexerToken::Ident("MAIN".into())],
                          vec![LexerToken::Ident("START".into())]];

        let mut parser = Parser::new();
        let result = parser.parse(tokens).unwrap();

        assert_eq!(&[ParserToken::Label("MAIN".into()), ParserToken::Label("START".into())],
                   &result[..]);
    }

    #[test]
    fn can_parse_labels_via_colon_terminator() {
        let tokens = vec![vec![LexerToken::Ident("MAIN".into())], vec![LexerToken::Colon]];

        let mut parser = Parser::new();
        let result = parser.parse(tokens).unwrap();

        assert_eq!(&[ParserToken::Label("MAIN".into())], &result[..]);
    }

    #[test]
    fn can_parse_opcodes_after_labels_on_one_line() {
        let tokens = vec![vec![LexerToken::Ident("MAIN".into()),
                               LexerToken::Ident("LDA".into()),
                               LexerToken::Address("4400".into())]];

        let mut parser = Parser::new();
        let result = parser.parse(tokens).unwrap();

        assert_eq!(&[ParserToken::Label("MAIN".into()),
                     ParserToken::OpCode(OpCode::from_mnemonic_and_addressing_mode("LDA", AddressingMode::Absolute).unwrap()),
                     ParserToken::RawByte(0),
                     ParserToken::RawByte(68)],
                   &result[..]);
    }

    #[test]
    fn can_parse_double_labels_on_one_line() {
        let tokens = vec![vec![LexerToken::Ident("MAIN".into()),
                               LexerToken::Ident("START".into())]];

        let mut parser = Parser::new();
        let result = parser.parse(tokens);

        assert_eq!(Err(ParserError::expected_instruction(1)), result);
    }

    #[test]
    fn can_parse_opcode_with_implied_addressing_mode() {
        let tokens = vec![vec![LexerToken::Ident("CLC".into())]];

        let mut parser = Parser::new();
        let result = parser.parse(tokens).unwrap();

        assert_eq!(&[ParserToken::OpCode(OpCode::from_mnemonic_and_addressing_mode("CLC", AddressingMode::Implied).unwrap())], &result[..]);
    }

    #[test]
    fn can_parse_opcode_with_correct_absolute_x_addressing_mode() {
        let tokens = vec![vec![LexerToken::Ident("MAIN".into()),
                               LexerToken::Ident("LDA".into()),
                               LexerToken::Address("4400".into()),
                               LexerToken::Comma,
                               LexerToken::Ident("X".into())]];

        let mut parser = Parser::new();
        let result = parser.parse(tokens).unwrap();

        assert_eq!(&[ParserToken::Label("MAIN".into()),
                     ParserToken::OpCode(OpCode::from_mnemonic_and_addressing_mode("LDA", AddressingMode::AbsoluteX).unwrap()),
                     ParserToken::RawByte(0),
                     ParserToken::RawByte(68)],
                   &result[..]);
    }

    #[test]
    fn errors_on_incorrect_zero_page_y_usage() {
        // LDA does not support the ZeroPageY addressing mode
        let tokens = vec![vec![LexerToken::Ident("LDA".into()),
                               LexerToken::Address("44".into()),
                               LexerToken::Comma,
                               LexerToken::Ident("Y".into())]];

        let mut parser = Parser::new();
        let result = parser.parse(tokens);

        assert_eq!(Err(ParserError::invalid_opcode_addressing_mode_combination(1)),
                   result);
    }

    #[test]
    fn can_handle_correct_zero_page_y_usage() {
        // LDX does support the ZeroPageY addressing mode
        let tokens = vec![vec![LexerToken::Ident("LDX".into()),
                               LexerToken::Address("44".into()),
                               LexerToken::Comma,
                               LexerToken::Ident("Y".into())]];

        let mut parser = Parser::new();
        let result = parser.parse(tokens).unwrap();

        assert_eq!(&[
                     ParserToken::OpCode(OpCode::from_mnemonic_and_addressing_mode("LDX", AddressingMode::ZeroPageY).unwrap()),
                     ParserToken::RawByte(68)],
                   &result[..]);
    }
}<|endoftext|>


<commit_before>/*
* This file is part of the uutils coreutils package.
*
* (c) Alex Lyon <arcterus@mail.com>
* (c) Michael Gehring <mg@ebfe.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

use std::env;
use std::fs::File;
use std::io::Write;
use std::path::Path;

const CRC_TABLE_LEN: usize = 256;

#[path = "../../mkmain.rs"]
mod mkmain;

fn main() {
    mkmain::main();

    let out_dir = env::var("OUT_DIR").unwrap();

    let mut table = Vec::with_capacity(CRC_TABLE_LEN);
    for num in 0..CRC_TABLE_LEN {
        table.push(crc_entry(num as u8) as u32);
    }
    let file = File::create(&Path::new(&out_dir).join("crc_table.rs")).unwrap();
    write!(
        &file,
        "#[allow(clippy::unreadable_literal)]\nconst CRC_TABLE: [u32; {}] = {:?};",
        CRC_TABLE_LEN, table
    )
    .unwrap();
}

#[inline]
fn crc_entry(input: u8) -> u32 {
    let mut crc = (input as u32) << 24;

    for _ in 0..8 {
        if crc & 0x80000000 != 0 {
            crc <<= 1;
            crc ^= 0x04c11db7;
        } else {
            crc <<= 1;
        }
    }

    crc
}
<commit_msg>refactor/polish ~ fix `cargo clippy` complaints (unreadable_literal)<commit_after>/*
* This file is part of the uutils coreutils package.
*
* (c) Alex Lyon <arcterus@mail.com>
* (c) Michael Gehring <mg@ebfe.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

use std::env;
use std::fs::File;
use std::io::Write;
use std::path::Path;

const CRC_TABLE_LEN: usize = 256;

#[path = "../../mkmain.rs"]
mod mkmain;

fn main() {
    mkmain::main();

    let out_dir = env::var("OUT_DIR").unwrap();

    let mut table = Vec::with_capacity(CRC_TABLE_LEN);
    for num in 0..CRC_TABLE_LEN {
        table.push(crc_entry(num as u8) as u32);
    }
    let file = File::create(&Path::new(&out_dir).join("crc_table.rs")).unwrap();
    write!(
        &file,
        "#[allow(clippy::unreadable_literal)]\nconst CRC_TABLE: [u32; {}] = {:?};",
        CRC_TABLE_LEN, table
    )
    .unwrap();
}

#[inline]
fn crc_entry(input: u8) -> u32 {
    let mut crc = (input as u32) << 24;

    for _ in 0..8 {
        if crc & 0x8000_0000 != 0 {
            crc <<= 1;
            crc ^= 0x04c1_1db7;
        } else {
            crc <<= 1;
        }
    }

    crc
}
<|endoftext|>


<commit_before>#![feature(test)]

extern crate recycler;
extern crate abomonation;
extern crate test;

use recycler::{Recyclable, Recycler, make_recycler};
use abomonation::*;
use test::Bencher;
use std::io::Read;

#[bench] fn empty_own(bencher: &mut Bencher) { _bench_own(bencher, vec![(); 1024]); }
#[bench] fn u64_own(bencher: &mut Bencher) { _bench_own(bencher, vec![0u64; 1024]); }
#[bench] fn u8_u64_own(bencher: &mut Bencher) { _bench_own(bencher, vec![(0u8, 0u64); 512]); }
#[bench] fn string10_own(bencher: &mut Bencher) { _bench_own(bencher, vec![format!("grawwwwrr!"); 1024]); }
#[bench] fn string20_own(bencher: &mut Bencher) { _bench_own(bencher, vec![format!("grawwwwrr!!!!!!!!!!!"); 512]); }
#[bench] fn vec_u_s_own(bencher: &mut Bencher) { _bench_own(bencher, vec![vec![(0u64, format!("grawwwwrr!")); 32]; 32]); }
#[bench] fn vec_u_vn_s_own(bencher: &mut Bencher) { _bench_own(bencher, vec![vec![(0u64, vec![(); 1 << 40], format!("grawwwwrr!")); 32]; 32]); }

#[bench] fn empty_rec(bencher: &mut Bencher) { _bench_rec(bencher, vec![(); 1024]); }
#[bench] fn u64_rec(bencher: &mut Bencher) { _bench_rec(bencher, vec![0u64; 1024]); }
#[bench] fn u8_u64_rec(bencher: &mut Bencher) { _bench_rec(bencher, vec![(0u8, 0u64); 512]); }
#[bench] fn string10_rec(bencher: &mut Bencher) { _bench_rec(bencher, vec![format!("grawwwwrr!"); 1024]); }
#[bench] fn string20_rec(bencher: &mut Bencher) { _bench_rec(bencher, vec![format!("grawwwwrr!!!!!!!!!!!"); 512]); }
#[bench] fn vec_u_s_rec(bencher: &mut Bencher) { _bench_rec(bencher, vec![vec![(0u64, format!("grawwwwrr!")); 32]; 32]); }

// TODO : this reveals that working with a `vec![(); 1 << 40]` does not get optimized away.
// #[bench] fn vec_u_vn_s_rec(bencher: &mut Bencher) { _bench_rec(bencher, vec![vec![(0u64, vec![(); 1 << 40], format!("grawwwwrr!")); 32]; 32]); }

fn _bench_own<T: Abomonation+Clone>(bencher: &mut Bencher, record: T) {

    // prepare encoded data
    let mut bytes = Vec::new();
    encode(&record, &mut bytes);

    // repeatedly decode (and validate)
    bencher.bytes = bytes.len() as u64;
    bencher.iter(|| {
        (*unsafe {decode::<T>(&mut bytes[..]) }.unwrap().0).clone()
        // assert!(record == result);
    });
}

fn _bench_rec<T: Abomonation+Recyclable>(bencher: &mut Bencher, record: T) {

    // prepare encoded data
    let mut bytes = Vec::new();
    encode(&record, &mut bytes);
    let mut recycler = make_recycler::<T>();
    recycler.recycle(record);

    // repeatedly decode (and validate)
    bencher.bytes = bytes.len() as u64;
    bencher.iter(|| {
        let result = recycler.recreate(unsafe { decode::<T>(&mut bytes[..]) }.unwrap().0);
        recycler.recycle(result);
    });
}
<commit_msg>added unsafe<commit_after>#![feature(test)]

extern crate recycler;
extern crate abomonation;
extern crate test;

use recycler::{Recyclable, Recycler, make_recycler};
use abomonation::*;
use test::Bencher;
use std::io::Read;

#[bench] fn empty_own(bencher: &mut Bencher) { _bench_own(bencher, vec![(); 1024]); }
#[bench] fn u64_own(bencher: &mut Bencher) { _bench_own(bencher, vec![0u64; 1024]); }
#[bench] fn u8_u64_own(bencher: &mut Bencher) { _bench_own(bencher, vec![(0u8, 0u64); 512]); }
#[bench] fn string10_own(bencher: &mut Bencher) { _bench_own(bencher, vec![format!("grawwwwrr!"); 1024]); }
#[bench] fn string20_own(bencher: &mut Bencher) { _bench_own(bencher, vec![format!("grawwwwrr!!!!!!!!!!!"); 512]); }
#[bench] fn vec_u_s_own(bencher: &mut Bencher) { _bench_own(bencher, vec![vec![(0u64, format!("grawwwwrr!")); 32]; 32]); }
#[bench] fn vec_u_vn_s_own(bencher: &mut Bencher) { _bench_own(bencher, vec![vec![(0u64, vec![(); 1 << 40], format!("grawwwwrr!")); 32]; 32]); }

#[bench] fn empty_rec(bencher: &mut Bencher) { _bench_rec(bencher, vec![(); 1024]); }
#[bench] fn u64_rec(bencher: &mut Bencher) { _bench_rec(bencher, vec![0u64; 1024]); }
#[bench] fn u8_u64_rec(bencher: &mut Bencher) { _bench_rec(bencher, vec![(0u8, 0u64); 512]); }
#[bench] fn string10_rec(bencher: &mut Bencher) { _bench_rec(bencher, vec![format!("grawwwwrr!"); 1024]); }
#[bench] fn string20_rec(bencher: &mut Bencher) { _bench_rec(bencher, vec![format!("grawwwwrr!!!!!!!!!!!"); 512]); }
#[bench] fn vec_u_s_rec(bencher: &mut Bencher) { _bench_rec(bencher, vec![vec![(0u64, format!("grawwwwrr!")); 32]; 32]); }

// TODO : this reveals that working with a `vec![(); 1 << 40]` does not get optimized away.
// #[bench] fn vec_u_vn_s_rec(bencher: &mut Bencher) { _bench_rec(bencher, vec![vec![(0u64, vec![(); 1 << 40], format!("grawwwwrr!")); 32]; 32]); }

fn _bench_own<T: Abomonation+Clone>(bencher: &mut Bencher, record: T) {

    // prepare encoded data
    let mut bytes = Vec::new();
    unsafe { encode(&record, &mut bytes); }

    // repeatedly decode (and validate)
    bencher.bytes = bytes.len() as u64;
    bencher.iter(|| {
        (*unsafe {decode::<T>(&mut bytes[..]) }.unwrap().0).clone()
        // assert!(record == result);
    });
}

fn _bench_rec<T: Abomonation+Recyclable>(bencher: &mut Bencher, record: T) {

    // prepare encoded data
    let mut bytes = Vec::new();
    unsafe { encode(&record, &mut bytes); }
    let mut recycler = make_recycler::<T>();
    recycler.recycle(record);

    // repeatedly decode (and validate)
    bencher.bytes = bytes.len() as u64;
    bencher.iter(|| {
        let result = recycler.recreate(unsafe { decode::<T>(&mut bytes[..]) }.unwrap().0);
        recycler.recycle(result);
    });
}
<|endoftext|>


<commit_before>//! Collections for use with GC references.

use gc_ref::GcRef;
use ptr::Pointer;
use std::cmp::Ordering;
use std::mem;
use traits::{IntoHeap, IntoHeapAllocation, IntoHeapBase, Tracer};

impl<T: IntoHeapBase> IntoHeapBase for Vec<T> {
    type In = Vec<T::In>;

    fn into_heap(self) -> Vec<T::In> {
        self.into_iter().map(|x| x.into_heap()).collect()
    }

    unsafe fn from_heap(storage: &Vec<T::In>) -> Vec<T> {
        storage.iter().map(|x| T::from_heap(x)).collect()
    }

    unsafe fn trace<R>(storage: &Vec<T::In>, tracer: &mut R)
    where
        R: Tracer,
    {
        for r in storage {
            T::trace(r, tracer);
        }
    }
}

unsafe impl<'h, T: IntoHeap<'h>> IntoHeap<'h> for Vec<T> {}

impl<'h, T: IntoHeap<'h>> IntoHeapAllocation<'h> for Vec<T> {
    type Ref = VecRef<'h, T>;

    fn wrap_gc_ref(gc_ref: GcRef<'h, Vec<T>>) -> VecRef<'h, T> {
        VecRef(gc_ref)
    }

    fn into_gc_ref(wrapped_ref: VecRef<'h, T>) -> GcRef<'h, Vec<T>> {
        wrapped_ref.0
    }
}

/// A reference to a GC-heap-allocated `Vec`.
///
/// To allocate a vector in the heap, simply call `heap.alloc` on a plain old `Vec`.
/// It will return a `VecRef` object which has many of the familiar `Vec` methods,
/// as well as element accessors `get_at` and `set_at`.
///
/// ```rust
/// use cell_gc::collections::VecRef;
///
/// cell_gc::with_heap(|heap| {
///     let r: VecRef<i32> = heap.alloc(vec![20, 10, 40, 30]);
///     assert_eq!(r.len(), 4);
///     assert_eq!(r.get(0), 20);
///     r.push(100);
///     r.sort();
///     assert_eq!(r.get_all(), vec![10, 20, 30, 40, 100]);
/// });
/// ```
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct VecRef<'h, T: IntoHeap<'h>>(GcRef<'h, Vec<T>>);

impl<'h, T: IntoHeap<'h>> IntoHeapBase for VecRef<'h, T> {
    type In = Pointer<Vec<T::In>>;

    fn into_heap(self) -> Pointer<Vec<T::In>> {
        self.0.ptr()
    }

    unsafe fn from_heap(storage: &Pointer<Vec<T::In>>) -> VecRef<'h, T> {
        VecRef(GcRef::new(*storage))
    }

    unsafe fn trace<R>(storage: &Pointer<Vec<T::In>>, tracer: &mut R)
    where
        R: Tracer,
    {
        // BUG - should call a method mark_ref that checks the mark bit before
        // doing anything
        for r in storage.as_ref() {
            T::trace(r, tracer);
        }
    }
}

unsafe impl<'h, T: IntoHeap<'h>> IntoHeap<'h> for VecRef<'h, T> {}

impl<'h, T: IntoHeap<'h>> VecRef<'h, T> {
    /// Run the given closure, passing it a reference to the heap-internal
    /// storage vector.
    ///
    /// # Safety
    ///
    /// Anything that provides direct access to any heap storage is unsafe
    /// because Rust can't guarantee there aren't other ways to obtain `mut`
    /// references to that data at the same time.
    ///
    /// Therefore, during the call to `f`, it's the caller responsibility to
    /// ensure that no `mut` references to the vector exist. In other words,
    /// don't nest `with_storage` calls. Destructors and `from_heap` methods
    /// may be presumed not to nest. Avoid `to_heap`.
    ///
    unsafe fn with_storage<'v, 'b, R, F>(&'v self, f: F) -> R
    where
        F: FnOnce(&'b Vec<T::In>) -> R,
        'v: 'b,
    {
        // TODO: in debug builds at least there should be a RefCell-like "lock" here,
        // asserting that no direct Rust references exist to anything in the GC heap.
        f(&*self.0.as_ptr())
    }

    /// Run the given closure, passing it a `mut` reference to the heap-internal
    /// storage vector.
    ///
    /// # Safety
    ///
    /// Like `with_storage`, except the caller must ensure that no other
    /// references to the vector exist. It still amounts to: don't nest.
    unsafe fn with_storage_mut<'v, 'b, R, F>(&'v self, f: F) -> R
    where
        F: FnOnce(&'b mut Vec<T::In>) -> R,
        'v: 'b,
    {
        // TODO should assert here, like unsafe_deref
        f(&mut *self.0.as_mut_ptr())
    }

    /// Get the element `index` from the vector.
    ///
    /// This clones the element, so `VecRef<LargeStruct>::get()` would be slow.
    /// `VecRef<LargeStructRef>::get()` only clones a reference, so it is
    /// relatively quick.
    ///
    /// # Panics
    ///
    /// Panics if `index` is out of bounds.
    pub fn get(&self, index: usize) -> T {
        unsafe { self.with_storage(|v| T::from_heap(&v[index])) }
    }

    /// Copy the vector out of the GC-managed heap. This returns an ordinary,
    /// non-GC-managed Rust `Vec`.
    ///
    /// # Examples
    ///
    /// ```rust
    /// # use cell_gc::collections::VecRef;
    /// # cell_gc::with_heap(|heap| {
    /// let gc_vec: VecRef<i32> = heap.alloc(vec![]);
    /// for i in 0 .. 4 {
    ///     gc_vec.push(i);
    /// }
    /// let plain_vec = gc_vec.get_all();
    /// assert_eq!(plain_vec, vec![0, 1, 2, 3]);
    /// # });
    /// ```
    pub fn get_all(&self) -> Vec<T> {
        unsafe { <Vec<T>>::from_heap(&*self.0.as_ptr()) }
    }

    /// Set element `index` of the vector to `value`.
    ///
    /// # Panics
    ///
    /// Panics if `index` is out of bounds.
    pub fn set(&self, index: usize, value: T) {
        let u = value.into_heap();
        unsafe {
            self.with_storage_mut(|v| v[index] = u);
        }
    }

    pub fn capacity(&self) -> usize {
        unsafe { self.with_storage(|v| v.capacity()) }
    }

    pub fn reserve(&self, additional: usize) {
        unsafe {
            self.with_storage_mut(|v| v.reserve(additional));
        }
    }

    pub fn reserve_exact(&self, additional: usize) {
        unsafe {
            self.with_storage_mut(|v| v.reserve_exact(additional));
        }
    }

    pub fn shrink_to_fit(&self) {
        unsafe {
            self.with_storage_mut(|v| v.shrink_to_fit());
        }
    }

    pub fn truncate(&self, len: usize) {
        unsafe {
            self.with_storage_mut(|v| v.truncate(len));
        }
    }

    pub fn swap_remove(&self, index: usize) -> T {
        unsafe {
            let u = self.with_storage_mut(|v| v.swap_remove(index));
            T::from_heap(&u)
        }
    }

    pub fn insert(&self, index: usize, element: T) {
        let u = element.into_heap();
        unsafe {
            self.with_storage_mut(|v| v.insert(index, u));
        }
    }

    pub fn remove(&self, index: usize) -> T {
        unsafe {
            let u = self.with_storage_mut(|v| v.remove(index));
            T::from_heap(&u)
        }
    }

    pub fn push(&self, value: T) {
        let u = value.into_heap();
        unsafe {
            self.with_storage_mut(|v| v.push(u));
        }
    }

    pub fn pop(&self) -> Option<T> {
        unsafe { self.with_storage_mut(|v| v.pop().map(|u| T::from_heap(&u))) }
    }

    pub fn append(&self, other: &VecRef<'h, T>) {
        let mut tmp: Vec<T::In> = vec![];
        unsafe {
            other.with_storage_mut(|src| mem::swap(src, &mut tmp));
            self.with_storage_mut(|dest| dest.append(&mut tmp));
        }
    }

    pub fn clear(&self) {
        unsafe {
            self.with_storage_mut(|v| v.clear());
        }
    }

    pub fn len(&self) -> usize {
        unsafe { self.with_storage(|v| v.len()) }
    }

    pub fn is_empty(&self) -> bool {
        self.len() == 0
    }

    /// Get the first element of the vector, or `None` if the vector is empty.
    ///
    /// The standard `Vec<T>::first` returns an `&T` reference into the vector,
    /// but cell-gc never hands out Rust references to heap values. Instead
    /// this returns a clone of the value.
    pub fn first(&self) -> Option<T> {
        unsafe { self.with_storage(|v| v.first().map(|u| T::from_heap(u))) }
    }

    /// Get the last element of the vector, or `None` if the vector is empty.
    ///
    /// Like `first()`, this clones the value.
    pub fn last(&self) -> Option<T> {
        unsafe { self.with_storage(|v| v.last().map(|u| T::from_heap(u))) }
    }

    /// Sort the vector in place.
    pub fn sort(&self)
    where
        T: Ord,
    {
        self.sort_by(Ord::cmp);
    }

    pub fn sort_by<F>(&self, mut compare: F)
    where
        F: FnMut(&T, &T) -> Ordering,
    {
        // Make the heap vector empty while we're sorting it.  This way, the
        // user can do whatever malicious nonsense they like to the heap in
        // `compare`, and our copy of the vector is unaffected.
        let mut tmp = vec![];
        unsafe {
            self.with_storage_mut(|v| mem::swap(&mut tmp, v));
        }
        tmp.sort_by(|ru1, ru2| {
            let t1 = unsafe { T::from_heap(ru1) };
            let t2 = unsafe { T::from_heap(ru2) };
            compare(&t1, &t2)
        });
        unsafe {
            self.with_storage_mut(|v| mem::swap(&mut tmp, v));
        }
    }
}
<commit_msg>Fix marking for VecRef fields.<commit_after>//! Collections for use with GC references.

use gc_ref::GcRef;
use ptr::Pointer;
use std::cmp::Ordering;
use std::mem;
use traits::{IntoHeap, IntoHeapAllocation, IntoHeapBase, Tracer};

impl<T: IntoHeapBase> IntoHeapBase for Vec<T> {
    type In = Vec<T::In>;

    fn into_heap(self) -> Vec<T::In> {
        self.into_iter().map(|x| x.into_heap()).collect()
    }

    unsafe fn from_heap(storage: &Vec<T::In>) -> Vec<T> {
        storage.iter().map(|x| T::from_heap(x)).collect()
    }

    unsafe fn trace<R>(storage: &Vec<T::In>, tracer: &mut R)
    where
        R: Tracer,
    {
        for r in storage {
            T::trace(r, tracer);
        }
    }
}

unsafe impl<'h, T: IntoHeap<'h>> IntoHeap<'h> for Vec<T> {}

impl<'h, T: IntoHeap<'h>> IntoHeapAllocation<'h> for Vec<T> {
    type Ref = VecRef<'h, T>;

    fn wrap_gc_ref(gc_ref: GcRef<'h, Vec<T>>) -> VecRef<'h, T> {
        VecRef(gc_ref)
    }

    fn into_gc_ref(wrapped_ref: VecRef<'h, T>) -> GcRef<'h, Vec<T>> {
        wrapped_ref.0
    }
}

/// A reference to a GC-heap-allocated `Vec`.
///
/// To allocate a vector in the heap, simply call `heap.alloc` on a plain old `Vec`.
/// It will return a `VecRef` object which has many of the familiar `Vec` methods,
/// as well as element accessors `get_at` and `set_at`.
///
/// ```rust
/// use cell_gc::collections::VecRef;
///
/// cell_gc::with_heap(|heap| {
///     let r: VecRef<i32> = heap.alloc(vec![20, 10, 40, 30]);
///     assert_eq!(r.len(), 4);
///     assert_eq!(r.get(0), 20);
///     r.push(100);
///     r.sort();
///     assert_eq!(r.get_all(), vec![10, 20, 30, 40, 100]);
/// });
/// ```
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct VecRef<'h, T: IntoHeap<'h>>(GcRef<'h, Vec<T>>);

impl<'h, T: IntoHeap<'h>> IntoHeapBase for VecRef<'h, T> {
    type In = Pointer<Vec<T::In>>;

    fn into_heap(self) -> Pointer<Vec<T::In>> {
        self.0.ptr()
    }

    unsafe fn from_heap(storage: &Pointer<Vec<T::In>>) -> VecRef<'h, T> {
        VecRef(GcRef::new(*storage))
    }

    unsafe fn trace<R>(storage: &Pointer<Vec<T::In>>, tracer: &mut R)
    where
        R: Tracer,
    {
        tracer.visit::<Vec<T>>(*storage)
    }
}

unsafe impl<'h, T: IntoHeap<'h>> IntoHeap<'h> for VecRef<'h, T> {}

impl<'h, T: IntoHeap<'h>> VecRef<'h, T> {
    /// Run the given closure, passing it a reference to the heap-internal
    /// storage vector.
    ///
    /// # Safety
    ///
    /// Anything that provides direct access to any heap storage is unsafe
    /// because Rust can't guarantee there aren't other ways to obtain `mut`
    /// references to that data at the same time.
    ///
    /// Therefore, during the call to `f`, it's the caller responsibility to
    /// ensure that no `mut` references to the vector exist. In other words,
    /// don't nest `with_storage` calls. Destructors and `from_heap` methods
    /// may be presumed not to nest. Avoid `to_heap`.
    ///
    unsafe fn with_storage<'v, 'b, R, F>(&'v self, f: F) -> R
    where
        F: FnOnce(&'b Vec<T::In>) -> R,
        'v: 'b,
    {
        // TODO: in debug builds at least there should be a RefCell-like "lock" here,
        // asserting that no direct Rust references exist to anything in the GC heap.
        f(&*self.0.as_ptr())
    }

    /// Run the given closure, passing it a `mut` reference to the heap-internal
    /// storage vector.
    ///
    /// # Safety
    ///
    /// Like `with_storage`, except the caller must ensure that no other
    /// references to the vector exist. It still amounts to: don't nest.
    unsafe fn with_storage_mut<'v, 'b, R, F>(&'v self, f: F) -> R
    where
        F: FnOnce(&'b mut Vec<T::In>) -> R,
        'v: 'b,
    {
        // TODO should assert here, like unsafe_deref
        f(&mut *self.0.as_mut_ptr())
    }

    /// Get the element `index` from the vector.
    ///
    /// This clones the element, so `VecRef<LargeStruct>::get()` would be slow.
    /// `VecRef<LargeStructRef>::get()` only clones a reference, so it is
    /// relatively quick.
    ///
    /// # Panics
    ///
    /// Panics if `index` is out of bounds.
    pub fn get(&self, index: usize) -> T {
        unsafe { self.with_storage(|v| T::from_heap(&v[index])) }
    }

    /// Copy the vector out of the GC-managed heap. This returns an ordinary,
    /// non-GC-managed Rust `Vec`.
    ///
    /// # Examples
    ///
    /// ```rust
    /// # use cell_gc::collections::VecRef;
    /// # cell_gc::with_heap(|heap| {
    /// let gc_vec: VecRef<i32> = heap.alloc(vec![]);
    /// for i in 0 .. 4 {
    ///     gc_vec.push(i);
    /// }
    /// let plain_vec = gc_vec.get_all();
    /// assert_eq!(plain_vec, vec![0, 1, 2, 3]);
    /// # });
    /// ```
    pub fn get_all(&self) -> Vec<T> {
        unsafe { <Vec<T>>::from_heap(&*self.0.as_ptr()) }
    }

    /// Set element `index` of the vector to `value`.
    ///
    /// # Panics
    ///
    /// Panics if `index` is out of bounds.
    pub fn set(&self, index: usize, value: T) {
        let u = value.into_heap();
        unsafe {
            self.with_storage_mut(|v| v[index] = u);
        }
    }

    pub fn capacity(&self) -> usize {
        unsafe { self.with_storage(|v| v.capacity()) }
    }

    pub fn reserve(&self, additional: usize) {
        unsafe {
            self.with_storage_mut(|v| v.reserve(additional));
        }
    }

    pub fn reserve_exact(&self, additional: usize) {
        unsafe {
            self.with_storage_mut(|v| v.reserve_exact(additional));
        }
    }

    pub fn shrink_to_fit(&self) {
        unsafe {
            self.with_storage_mut(|v| v.shrink_to_fit());
        }
    }

    pub fn truncate(&self, len: usize) {
        unsafe {
            self.with_storage_mut(|v| v.truncate(len));
        }
    }

    pub fn swap_remove(&self, index: usize) -> T {
        unsafe {
            let u = self.with_storage_mut(|v| v.swap_remove(index));
            T::from_heap(&u)
        }
    }

    pub fn insert(&self, index: usize, element: T) {
        let u = element.into_heap();
        unsafe {
            self.with_storage_mut(|v| v.insert(index, u));
        }
    }

    pub fn remove(&self, index: usize) -> T {
        unsafe {
            let u = self.with_storage_mut(|v| v.remove(index));
            T::from_heap(&u)
        }
    }

    pub fn push(&self, value: T) {
        let u = value.into_heap();
        unsafe {
            self.with_storage_mut(|v| v.push(u));
        }
    }

    pub fn pop(&self) -> Option<T> {
        unsafe { self.with_storage_mut(|v| v.pop().map(|u| T::from_heap(&u))) }
    }

    pub fn append(&self, other: &VecRef<'h, T>) {
        let mut tmp: Vec<T::In> = vec![];
        unsafe {
            other.with_storage_mut(|src| mem::swap(src, &mut tmp));
            self.with_storage_mut(|dest| dest.append(&mut tmp));
        }
    }

    pub fn clear(&self) {
        unsafe {
            self.with_storage_mut(|v| v.clear());
        }
    }

    pub fn len(&self) -> usize {
        unsafe { self.with_storage(|v| v.len()) }
    }

    pub fn is_empty(&self) -> bool {
        self.len() == 0
    }

    /// Get the first element of the vector, or `None` if the vector is empty.
    ///
    /// The standard `Vec<T>::first` returns an `&T` reference into the vector,
    /// but cell-gc never hands out Rust references to heap values. Instead
    /// this returns a clone of the value.
    pub fn first(&self) -> Option<T> {
        unsafe { self.with_storage(|v| v.first().map(|u| T::from_heap(u))) }
    }

    /// Get the last element of the vector, or `None` if the vector is empty.
    ///
    /// Like `first()`, this clones the value.
    pub fn last(&self) -> Option<T> {
        unsafe { self.with_storage(|v| v.last().map(|u| T::from_heap(u))) }
    }

    /// Sort the vector in place.
    pub fn sort(&self)
    where
        T: Ord,
    {
        self.sort_by(Ord::cmp);
    }

    pub fn sort_by<F>(&self, mut compare: F)
    where
        F: FnMut(&T, &T) -> Ordering,
    {
        // Make the heap vector empty while we're sorting it.  This way, the
        // user can do whatever malicious nonsense they like to the heap in
        // `compare`, and our copy of the vector is unaffected.
        let mut tmp = vec![];
        unsafe {
            self.with_storage_mut(|v| mem::swap(&mut tmp, v));
        }
        tmp.sort_by(|ru1, ru2| {
            let t1 = unsafe { T::from_heap(ru1) };
            let t2 = unsafe { T::from_heap(ru2) };
            compare(&t1, &t2)
        });
        unsafe {
            self.with_storage_mut(|v| mem::swap(&mut tmp, v));
        }
    }
}
<|endoftext|>


<commit_before>use crate::constants::*;
use itertools::Itertools;

// Highlight lines starting with "commit: "

pub fn format_commit_line(line: &str) -> String {
    let parts = line.split('(').collect::<Vec<_>>();
    if parts.len() == 1 {
        // Just "commit: 123abc", color it all yellow
        return format!("{}{}{}", YELLOW, line, NORMAL);
    }

    let commit_part = parts[0].trim();
    let without_trailing_parenthesis = parts[1].strip_suffix(')');
    if without_trailing_parenthesis == None {
        // No final parenthesis, this is weird, fall back to showing everything
        // in yellow
        return format!("{}{}{}", YELLOW, line, NORMAL);
    }

    let parenthesis_parts = without_trailing_parenthesis
        .unwrap()
        .split(", ")
        .collect_vec();
    let current_branch = compute_current_branch(&parenthesis_parts);

    // FIXME: Placeholder logic until we have all tests
    let comma = format!("{}, {}", YELLOW, NORMAL);
    return format!(
        "{}{} ({}){}",
        YELLOW,
        commit_part,
        parenthesis_parts
            .iter()
            .map(|part| format_commit_part(part, &current_branch))
            .join(&comma),
        NORMAL
    );
}

fn format_commit_part(part: &str, current_branch: &Option<String>) -> String {
    if part.starts_with("tag: ") {
        return format!("{}{}{}{}", BOLD, YELLOW, part, NORMAL);
    }

    // FIXME: Can we do this without to_owned()? to_owned() cretes a new string,
    // but all we want to do is compare them. How to compare without allocating
    // a new string?
    if &Some(part.to_owned()) == current_branch {
        return format!("{}{}{}{}", BOLD, GREEN, part, NORMAL);
    }

    // Handle "HEAD -> current_branch"
    if let Some(head_branch) = part.strip_prefix("HEAD -> ") {
        return format!("{}{}HEAD -> {}{}{}", BOLD, CYAN, GREEN, head_branch, NORMAL);
    }

    // Assume this is a branch, but not the current one
    return format!("{}{}{}{}", BOLD, RED, part, NORMAL);
}

fn compute_current_branch(candidates: &Vec<&str>) -> Option<String> {
    // Filter out realistic candidates
    let mut possible_candidates: Vec<String> = vec![];
    for part in candidates {
        if part.starts_with("tag: ") {
            // This is not a branch name
            continue;
        }

        if let Some(headless) = part.strip_prefix("HEAD -> ") {
            // Found it, this is conclusive!
            return Some(headless.to_owned());
        }

        possible_candidates.push(part.to_string());
    }

    // Pick obvious choices
    if possible_candidates.is_empty() {
        return None;
    }
    if possible_candidates.len() == 1 {
        return Some(possible_candidates[0].to_owned());
    }

    // We have multiple options, pick the one with the lowest number of slashes
    // FIXME: Can we just start with this loop?
    let mut fewest_slashes: Vec<String> = vec![];
    let mut lowest_slash_count = usize::MAX;
    for candidate in possible_candidates {
        let candidate_slash_count = candidate.matches('/').count();
        if candidate_slash_count > lowest_slash_count {
            // This one is worse, never mind
            continue;
        }

        if candidate_slash_count < lowest_slash_count {
            // This one is better, replace the others
            fewest_slashes.clear();
        }

        lowest_slash_count = candidate_slash_count;
        fewest_slashes.push(candidate);
    }

    assert!(!fewest_slashes.is_empty());
    if fewest_slashes.len() == 1 {
        return Some(fewest_slashes[0].to_owned());
    }

    // Give up, we don't know
    return None;
}

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

    #[cfg(test)]
    use pretty_assertions::assert_eq;

    #[test]
    fn test_compute_current_branch() {
        // git show 686f3d7aefe9597395020ff0219eebc90e363d47
        assert_eq!(
            None,
            compute_current_branch(&"tag: 2.15".split(", ").collect_vec())
        );

        assert_eq!(
            Some(String::from("master")),
            compute_current_branch(
                &"HEAD -> master, tag: 2.20.0, origin/master, origin/HEAD"
                    .split(", ")
                    .collect_vec()
            )
        );

        assert_eq!(
            Some(String::from("master")),
            compute_current_branch(
                &"tag: 2.20.0, origin/master, origin/HEAD, master"
                    .split(", ")
                    .collect_vec()
            )
        );

        assert_eq!(
            Some(String::from("walles/threaded")),
            compute_current_branch(
                &"origin/walles/threaded, walles/threaded"
                    .split(", ")
                    .collect_vec()
            )
        );

        assert_eq!(
            Some(String::from("xeago-master")),
            compute_current_branch(&"xeago/master, xeago-master".split(", ").collect_vec())
        );
    }
}
<commit_msg>Self review fixes<commit_after>use crate::constants::*;
use itertools::Itertools;

// Highlight lines starting with "commit: "

pub fn format_commit_line(line: &str) -> String {
    let parts = line.split('(').collect::<Vec<_>>();
    if parts.len() == 1 {
        // Just "commit: 123abc", color it all yellow
        return format!("{}{}{}", YELLOW, line, NORMAL);
    }

    let commit_part = parts[0].trim();
    let without_trailing_parenthesis = parts[1].strip_suffix(')');
    if without_trailing_parenthesis == None {
        // No final parenthesis, this is weird, fall back to showing everything
        // in yellow
        return format!("{}{}{}", YELLOW, line, NORMAL);
    }

    let parenthesis_parts = without_trailing_parenthesis
        .unwrap()
        .split(", ")
        .collect_vec();
    let current_branch = compute_current_branch(&parenthesis_parts);

    let comma = format!("{}, {}", YELLOW, NORMAL);
    return format!(
        "{}{} ({}){}",
        YELLOW,
        commit_part,
        parenthesis_parts
            .iter()
            .map(|part| format_commit_part(part, &current_branch))
            .join(&comma),
        NORMAL
    );
}

fn format_commit_part(part: &str, current_branch: &Option<String>) -> String {
    if part.starts_with("tag: ") {
        return format!("{}{}{}{}", BOLD, YELLOW, part, NORMAL);
    }

    // FIXME: Can we do this without to_owned()? to_owned() cretes a new string,
    // but all we want to do is compare them. How to compare without allocating
    // a new string?
    if &Some(part.to_owned()) == current_branch {
        return format!("{}{}{}{}", BOLD, GREEN, part, NORMAL);
    }

    // Handle "HEAD -> current_branch"
    if let Some(head_branch) = part.strip_prefix("HEAD -> ") {
        return format!("{}{}HEAD -> {}{}{}", BOLD, CYAN, GREEN, head_branch, NORMAL);
    }

    // Assume this is a branch, but not the current one
    return format!("{}{}{}{}", BOLD, RED, part, NORMAL);
}

fn compute_current_branch(candidates: &Vec<&str>) -> Option<String> {
    // If we find multiple options, pick the one with the lowest number of
    // slashes
    let mut fewest_slashes: Vec<&str> = vec![];
    let mut lowest_slash_count = usize::MAX;
    for candidate in candidates {
        if candidate.starts_with("tag: ") {
            // This is not a branch name
            continue;
        }

        if let Some(headless) = candidate.strip_prefix("HEAD -> ") {
            // Found it, this is conclusive!
            return Some(headless.to_owned());
        }

        let candidate_slash_count = candidate.matches('/').count();
        if candidate_slash_count > lowest_slash_count {
            // This one is worse than what we already have, never mind
            continue;
        }

        if candidate_slash_count < lowest_slash_count {
            // This one is better, replace the others
            fewest_slashes.clear();
        }

        lowest_slash_count = candidate_slash_count;
        fewest_slashes.push(candidate);
    }

    if fewest_slashes.is_empty() {
        return None;
    }

    if fewest_slashes.len() == 1 {
        return Some(fewest_slashes[0].to_owned());
    }

    // Give up, we don't know
    return None;
}

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

    #[cfg(test)]
    use pretty_assertions::assert_eq;

    #[test]
    fn test_compute_current_branch() {
        // git show 686f3d7aefe9597395020ff0219eebc90e363d47
        assert_eq!(
            None,
            compute_current_branch(&"tag: 2.15".split(", ").collect_vec())
        );

        assert_eq!(
            Some(String::from("master")),
            compute_current_branch(
                &"HEAD -> master, tag: 2.20.0, origin/master, origin/HEAD"
                    .split(", ")
                    .collect_vec()
            )
        );

        assert_eq!(
            Some(String::from("master")),
            compute_current_branch(
                &"tag: 2.20.0, origin/master, origin/HEAD, master"
                    .split(", ")
                    .collect_vec()
            )
        );

        assert_eq!(
            Some(String::from("walles/threaded")),
            compute_current_branch(
                &"origin/walles/threaded, walles/threaded"
                    .split(", ")
                    .collect_vec()
            )
        );

        assert_eq!(
            Some(String::from("xeago-master")),
            compute_current_branch(&"xeago/master, xeago-master".split(", ").collect_vec())
        );
    }
}
<|endoftext|>


<commit_before>// Copyright (c) Aptos
// SPDX-License-Identifier: Apache-2.0

use crate::{pruner::*, AptosDB, ChangeSet, LedgerStore, TransactionStore};
use aptos_temppath::TempPath;
use proptest::proptest;

use aptos_types::{
    account_address::AccountAddress,
    block_metadata::BlockMetadata,
    transaction::{SignedTransaction, Transaction},
};

use aptos_types::{
    transaction::{TransactionInfo, Version},
    write_set::WriteSet,
};
use proptest::{collection::vec, prelude::*};

proptest! {
    #![proptest_config(ProptestConfig::with_cases(10))]

    #[test]
    fn test_txn_store_pruner(txns in vec(
        prop_oneof![
            any::<BlockMetadata>().prop_map(Transaction::BlockMetadata),
            any::<SignedTransaction>().prop_map(Transaction::UserTransaction),
        ], 1..100,),
        txn_infos in vec(any::<TransactionInfo>(),100,),
        step_size in 1usize..20,
    ) {
        verify_txn_store_pruner(txns, txn_infos, step_size)
    }

     #[test]
    fn test_write_set_pruner(
        write_set in vec(any::<WriteSet>(), 100),
        ) {
            verify_write_set_pruner(write_set);
        }
}

fn verify_write_set_pruner(write_sets: Vec<WriteSet>) {
    let tmp_dir = TempPath::new();
    let aptos_db = AptosDB::new_for_test(&tmp_dir);
    let transaction_store = &aptos_db.transaction_store;
    let num_write_sets = write_sets.len();

    let pruner = Pruner::new(
        Arc::clone(&aptos_db.ledger_db),
        Arc::clone(&aptos_db.state_merkle_db),
        StoragePrunerConfig {
            state_store_prune_window: Some(0),
            ledger_prune_window: Some(0),
            pruning_batch_size: 1,
        },
    );

    // write sets
    let mut cs = ChangeSet::new();
    for (ver, ws) in write_sets.iter().enumerate() {
        transaction_store
            .put_write_set(ver as Version, ws, &mut cs)
            .unwrap();
    }
    aptos_db.ledger_db.write_schemas(cs.batch).unwrap();
    // start pruning write sets in batches of size 2 and verify transactions have been pruned from DB
    for i in (0..=num_write_sets).step_by(2) {
        pruner
            .wake_and_wait(
                i as u64, /* latest_version */
                PrunerIndex::LedgerPrunerIndex as usize,
            )
            .unwrap();
        // ensure that all transaction up to i * 2 has been pruned
        for j in 0..i {
            assert!(transaction_store.get_write_set(j as u64).is_err());
        }
        // ensure all other are valid in DB
        for j in i..num_write_sets {
            let write_set_from_db = transaction_store.get_write_set(j as u64).unwrap();
            assert_eq!(write_set_from_db, *write_sets.get(j).unwrap());
        }
    }
}

fn verify_txn_store_pruner(
    txns: Vec<Transaction>,
    txn_infos: Vec<TransactionInfo>,
    step_size: usize,
) {
    let tmp_dir = TempPath::new();
    let aptos_db = AptosDB::new_for_test(&tmp_dir);
    let transaction_store = &aptos_db.transaction_store;
    let ledger_store = LedgerStore::new(Arc::clone(&aptos_db.ledger_db));
    let num_transaction = txns.len();

    let pruner = Pruner::new(
        Arc::clone(&aptos_db.ledger_db),
        Arc::clone(&aptos_db.state_merkle_db),
        StoragePrunerConfig {
            state_store_prune_window: Some(0),
            ledger_prune_window: Some(0),
            pruning_batch_size: 1,
        },
    );

    let ledger_version = num_transaction as Version - 1;
    put_txn_in_store(
        &aptos_db,
        transaction_store,
        &ledger_store,
        &txn_infos,
        &txns,
    );

    // start pruning transactions batches of size step_size and verify transactions have been pruned from DB
    for i in (0..=num_transaction).step_by(step_size) {
        pruner
            .wake_and_wait(
                i as u64, /* latest_version */
                PrunerIndex::LedgerPrunerIndex as usize,
            )
            .unwrap();
        // ensure that all transaction up to i * 2 has been pruned
        assert_eq!(*pruner.last_version_sent_to_pruners.lock(), i as u64);
        for j in 0..i {
            verify_txn_not_in_store(transaction_store, &txns, j as u64, ledger_version);
        }
        // ensure all other are valid in DB
        for j in i..num_transaction {
            verify_txn_in_store(
                transaction_store,
                &ledger_store,
                &txns,
                j as u64,
                ledger_version,
            );
        }
    }
}

fn verify_txn_not_in_store(
    transaction_store: &TransactionStore,
    txns: &[Transaction],
    index: u64,
    ledger_version: u64,
) {
    // Ensure that all transaction from transaction schema store has been pruned
    assert!(transaction_store.get_transaction(index).is_err());
    // Ensure that transaction by account store has been pruned
    if let Transaction::UserTransaction(txn) = txns.get(index as usize).unwrap() {
        assert!(transaction_store
            .get_account_transaction_version(txn.sender(), txn.sequence_number(), ledger_version,)
            .unwrap()
            .is_none());
    }
}

fn verify_txn_in_store(
    transaction_store: &TransactionStore,
    ledger_store: &LedgerStore,
    txns: &[Transaction],
    index: u64,
    ledger_version: u64,
) {
    verify_transaction_in_transaction_store(
        transaction_store,
        txns.get(index as usize).unwrap(),
        index as u64,
    );
    if let Transaction::UserTransaction(txn) = txns.get(index as usize).unwrap() {
        verify_transaction_in_account_txn_by_version_index(
            transaction_store,
            index as u64,
            txn.sender(),
            txn.sequence_number(),
            ledger_version,
        );
    }
    // Ensure that transaction accumulator is in DB. This can be done by trying
    // to read transaction proof
    assert!(ledger_store
        .get_transaction_proof(index, ledger_version)
        .is_ok());
}

fn put_txn_in_store(
    aptos_db: &AptosDB,
    transaction_store: &TransactionStore,
    ledger_store: &LedgerStore,
    txn_infos: &[TransactionInfo],
    txns: &[Transaction],
) {
    let mut cs = ChangeSet::new();
    for i in 0..txns.len() {
        transaction_store
            .put_transaction(i as u64, txns.get(i).unwrap(), &mut cs)
            .unwrap();
    }
    ledger_store
        .put_transaction_infos(0, txn_infos, &mut cs)
        .unwrap();
    aptos_db.ledger_db.write_schemas(cs.batch).unwrap();
}

fn verify_transaction_in_transaction_store(
    transaction_store: &TransactionStore,
    expected_value: &Transaction,
    version: Version,
) {
    let txn = transaction_store.get_transaction(version).unwrap();
    assert_eq!(txn, *expected_value)
}

fn verify_transaction_in_account_txn_by_version_index(
    transaction_store: &TransactionStore,
    expected_value: Version,
    address: AccountAddress,
    sequence_number: u64,
    ledger_version: Version,
) {
    let transaction = transaction_store
        .get_account_transaction_version(address, sequence_number, ledger_version)
        .unwrap()
        .unwrap();
    assert_eq!(transaction, expected_value)
}
<commit_msg>[Storage] Disable flaky test.<commit_after>// Copyright (c) Aptos
// SPDX-License-Identifier: Apache-2.0

use crate::{pruner::*, AptosDB, ChangeSet, LedgerStore, TransactionStore};
use aptos_temppath::TempPath;
use proptest::proptest;

use aptos_types::{
    account_address::AccountAddress,
    block_metadata::BlockMetadata,
    transaction::{SignedTransaction, Transaction},
};

use aptos_types::{
    transaction::{TransactionInfo, Version},
    write_set::WriteSet,
};
use proptest::{collection::vec, prelude::*};

proptest! {
    #![proptest_config(ProptestConfig::with_cases(10))]

    #[ignore]
    #[test]
    fn test_txn_store_pruner(txns in vec(
        prop_oneof![
            any::<BlockMetadata>().prop_map(Transaction::BlockMetadata),
            any::<SignedTransaction>().prop_map(Transaction::UserTransaction),
        ], 1..100,),
        txn_infos in vec(any::<TransactionInfo>(),100,),
        step_size in 1usize..20,
    ) {
        verify_txn_store_pruner(txns, txn_infos, step_size)
    }

     #[test]
    fn test_write_set_pruner(
        write_set in vec(any::<WriteSet>(), 100),
        ) {
            verify_write_set_pruner(write_set);
        }
}

fn verify_write_set_pruner(write_sets: Vec<WriteSet>) {
    let tmp_dir = TempPath::new();
    let aptos_db = AptosDB::new_for_test(&tmp_dir);
    let transaction_store = &aptos_db.transaction_store;
    let num_write_sets = write_sets.len();

    let pruner = Pruner::new(
        Arc::clone(&aptos_db.ledger_db),
        Arc::clone(&aptos_db.state_merkle_db),
        StoragePrunerConfig {
            state_store_prune_window: Some(0),
            ledger_prune_window: Some(0),
            pruning_batch_size: 1,
        },
    );

    // write sets
    let mut cs = ChangeSet::new();
    for (ver, ws) in write_sets.iter().enumerate() {
        transaction_store
            .put_write_set(ver as Version, ws, &mut cs)
            .unwrap();
    }
    aptos_db.ledger_db.write_schemas(cs.batch).unwrap();
    // start pruning write sets in batches of size 2 and verify transactions have been pruned from DB
    for i in (0..=num_write_sets).step_by(2) {
        pruner
            .wake_and_wait(
                i as u64, /* latest_version */
                PrunerIndex::LedgerPrunerIndex as usize,
            )
            .unwrap();
        // ensure that all transaction up to i * 2 has been pruned
        for j in 0..i {
            assert!(transaction_store.get_write_set(j as u64).is_err());
        }
        // ensure all other are valid in DB
        for j in i..num_write_sets {
            let write_set_from_db = transaction_store.get_write_set(j as u64).unwrap();
            assert_eq!(write_set_from_db, *write_sets.get(j).unwrap());
        }
    }
}

fn verify_txn_store_pruner(
    txns: Vec<Transaction>,
    txn_infos: Vec<TransactionInfo>,
    step_size: usize,
) {
    let tmp_dir = TempPath::new();
    let aptos_db = AptosDB::new_for_test(&tmp_dir);
    let transaction_store = &aptos_db.transaction_store;
    let ledger_store = LedgerStore::new(Arc::clone(&aptos_db.ledger_db));
    let num_transaction = txns.len();

    let pruner = Pruner::new(
        Arc::clone(&aptos_db.ledger_db),
        Arc::clone(&aptos_db.state_merkle_db),
        StoragePrunerConfig {
            state_store_prune_window: Some(0),
            ledger_prune_window: Some(0),
            pruning_batch_size: 1,
        },
    );

    let ledger_version = num_transaction as Version - 1;
    put_txn_in_store(
        &aptos_db,
        transaction_store,
        &ledger_store,
        &txn_infos,
        &txns,
    );

    // start pruning transactions batches of size step_size and verify transactions have been pruned from DB
    for i in (0..=num_transaction).step_by(step_size) {
        pruner
            .wake_and_wait(
                i as u64, /* latest_version */
                PrunerIndex::LedgerPrunerIndex as usize,
            )
            .unwrap();
        // ensure that all transaction up to i * 2 has been pruned
        assert_eq!(*pruner.last_version_sent_to_pruners.lock(), i as u64);
        for j in 0..i {
            verify_txn_not_in_store(transaction_store, &txns, j as u64, ledger_version);
        }
        // ensure all other are valid in DB
        for j in i..num_transaction {
            verify_txn_in_store(
                transaction_store,
                &ledger_store,
                &txns,
                j as u64,
                ledger_version,
            );
        }
    }
}

fn verify_txn_not_in_store(
    transaction_store: &TransactionStore,
    txns: &[Transaction],
    index: u64,
    ledger_version: u64,
) {
    // Ensure that all transaction from transaction schema store has been pruned
    assert!(transaction_store.get_transaction(index).is_err());
    // Ensure that transaction by account store has been pruned
    if let Transaction::UserTransaction(txn) = txns.get(index as usize).unwrap() {
        assert!(transaction_store
            .get_account_transaction_version(txn.sender(), txn.sequence_number(), ledger_version,)
            .unwrap()
            .is_none());
    }
}

fn verify_txn_in_store(
    transaction_store: &TransactionStore,
    ledger_store: &LedgerStore,
    txns: &[Transaction],
    index: u64,
    ledger_version: u64,
) {
    verify_transaction_in_transaction_store(
        transaction_store,
        txns.get(index as usize).unwrap(),
        index as u64,
    );
    if let Transaction::UserTransaction(txn) = txns.get(index as usize).unwrap() {
        verify_transaction_in_account_txn_by_version_index(
            transaction_store,
            index as u64,
            txn.sender(),
            txn.sequence_number(),
            ledger_version,
        );
    }
    // Ensure that transaction accumulator is in DB. This can be done by trying
    // to read transaction proof
    assert!(ledger_store
        .get_transaction_proof(index, ledger_version)
        .is_ok());
}

fn put_txn_in_store(
    aptos_db: &AptosDB,
    transaction_store: &TransactionStore,
    ledger_store: &LedgerStore,
    txn_infos: &[TransactionInfo],
    txns: &[Transaction],
) {
    let mut cs = ChangeSet::new();
    for i in 0..txns.len() {
        transaction_store
            .put_transaction(i as u64, txns.get(i).unwrap(), &mut cs)
            .unwrap();
    }
    ledger_store
        .put_transaction_infos(0, txn_infos, &mut cs)
        .unwrap();
    aptos_db.ledger_db.write_schemas(cs.batch).unwrap();
}

fn verify_transaction_in_transaction_store(
    transaction_store: &TransactionStore,
    expected_value: &Transaction,
    version: Version,
) {
    let txn = transaction_store.get_transaction(version).unwrap();
    assert_eq!(txn, *expected_value)
}

fn verify_transaction_in_account_txn_by_version_index(
    transaction_store: &TransactionStore,
    expected_value: Version,
    address: AccountAddress,
    sequence_number: u64,
    ledger_version: Version,
) {
    let transaction = transaction_store
        .get_account_transaction_version(address, sequence_number, ledger_version)
        .unwrap()
        .unwrap();
    assert_eq!(transaction, expected_value)
}
<|endoftext|>