<commit_before>use std::usize;
use lalrpop_util::ParseError;
use ::value::Value;
use super::parse;
use super::lexer::{Token, Error};

const EOF: usize = usize::MAX;

fn unrecognized(x: &ParseError<usize, Token, Error>) -> usize {
    if let &ParseError::UnrecognizedToken{expected: _, token: Some((pos, _, _))} = x {
        pos
    } else {
        panic!("expected: unrecognized, got: {:?}", x)
    }
}

fn invalid(x: &ParseError<usize, Token, Error>) -> usize {
    if let &ParseError::InvalidToken{location: pos} = x {
        pos
    } else {
        panic!("expected: invalid, got: {:?}", x)
    }
}

fn whatever(x: &ParseError<usize, Token, Error>) -> usize {
    match x {
        &ParseError::InvalidToken{location: pos} => pos,
        &ParseError::UnrecognizedToken{expected: _, token: Some((pos, _, _))} => pos,
        &ParseError::User{error: Error::UnexpectedToken(x)} => x,
        &ParseError::User{error: Error::UnexpectedEof} => EOF,
        &ParseError::User{error: Error::InvalidCharacter(x)} => x,
        x => panic!("got: {:?}", x),
    }
}

macro_rules! expect_error {
    // unrecognized token
    ($parser:ident, $input:expr, unrecognized: $position:expr) => (
        expect_error!($parser, $input, unrecognized, true, $position);
    );

    // invalid token
    ($parser:ident, $input:expr, invalid: $position:expr) => (
        expect_error!($parser, $input, invalid, true, $position);
    );

    // whatever
    ($parser:ident, $input:expr, $position:expr) => (
        expect_error!($parser, $input, whatever, true, $position);
    );

    // whatever wherever
    ($parser:ident, $input:expr) => (
        expect_error!($parser, $input, whatever, false, 0);
    );

    //internal
    ($parser:ident, $input:expr, $token_fn:ident, $use_pos:expr, $position:expr) => (
        let input = &*$input;
        let result = $parser(input);

        if let Err(ref err) = result {
            if $use_pos && $token_fn(err) != $position {
                panic!("for input: {:?}, got error {:?} at pos {}, expected pos: {}", input, result, $token_fn(err), $position)
            }
        } else {
            panic!("expected error for string: '{}', got: {:?}", input, result);
        }
    );
}

macro_rules! expect_ok {
    // with rest
    ($parser:ident, $input:expr, $expected:expr) => (
        let input = &*$input;
        let expected = $expected;
        let result = $parser(input);
        if let Ok(v) = result {
            if v != expected {
                panic!("parser ok, but input: {:?} got: Ok({:?}), expected: {:?}", input, v, &expected);
            }
        } else {
            panic!("input: {:?} got: {:?}, expected: {:?}", input, result, &expected);
        }
    );
}

#[test]
fn bool() {
    expect_ok!(parse, "true", Value::new_bool(true));
    expect_ok!(parse, "false", Value::new_bool(false));
    expect_ok!(parse, "trude", Value::new_symbol("trude"));
    expect_ok!(parse, "fale", Value::new_symbol("fale"));
}

#[test]
fn char() {
    use std::char;
    use std::iter;
    let mut input: String = "#\\".into();
    let printlable_asci = ('!' as u32)..('~' as u32) + 1;
    for x in printlable_asci {
        let c = char::from_u32(x).expect(&format!("tried to create invalid char with: 0x{:X}", x));
        input.push(c);
        { expect_ok!(parse, input, Value::new_char(c)); }
        input.pop();
    }

    let invalid_asci = (0..('!' as u32)).chain(iter::once(127));
    for x in invalid_asci {
        let c = char::from_u32(x).expect(&format!("tried to create invalid char with: 0x{:X}", x));
        input.push(c);
        if c != '\n' && c != '\t' && c != ' ' { expect_error!(parse, input); }
        input.pop();
    }

    // special forms
    expect_ok!(parse, r"#\\s", Value::new_char(' '));
    expect_ok!(parse, r"#\\t", Value::new_char('\t'));
    expect_ok!(parse, r"#\\n", Value::new_char('\n'));
    expect_ok!(parse, r"#\\", Value::new_char('\\'));
    expect_ok!(parse, r"#\", Value::new_symbol(r"#\"));

    expect_error!(parse, "#\\\0", 2);
}

#[test]
fn integer() {
    expect_ok!(parse, "007", Value::new_integer(7));
    expect_ok!(parse, "-007", Value::new_integer(-7));
    expect_ok!(parse, "123456789", Value::new_integer(123456789));
    expect_ok!(parse, "-123456789", Value::new_integer(-123456789));

    expect_error!(parse, "123b456789", 3);
    expect_error!(parse, "123456789c", 9);
    expect_error!(parse, "00-7", 2);
    expect_ok!(parse, "a123456789", Value::new_symbol("a123456789"));
    expect_ok!(parse, "--7", Value::new_symbol("--7"));
}

#[test]
fn symbol() {
    expect_ok!(parse, "+", Value::new_symbol("+"));
    expect_ok!(parse, "-", Value::new_symbol("-"));
    expect_ok!(parse, "#", Value::new_symbol("#"));
    expect_ok!(parse, "a1a", Value::new_symbol("a1a"));
    expect_ok!(parse, "num->str", Value::new_symbol("num->str"));
    expect_ok!(parse, "//", Value::new_symbol("//"));

    // error is at 1 bc lexer tries to lex integer
    expect_error!(parse, "1a", 1);
}

// TODO
/*
// test item error
expect_ok!(integer, "123b456789", Value::new_integer(123), "b456789");
expect_ok!(integer, "123456789c", Value::new_integer(123456789), "c");
expect_ok!(integer, "00-7", Value::new_integer(0), "-7");
expect_error!(integer, "a123456789", 0);
expect_error!(integer, "--7", 1);
*/

#[test]
fn string() {
    use std::iter;
    fn q(s: &str) -> String {
        iter::once('"')
        .chain(s.chars())
        .chain(iter::once('"'))
        .collect()
    }

    macro_rules! expect_str_ok {
        ($s:expr, $e:expr) => ({
            let s = q($s);
            println!("string: {}", s);
            expect_ok!(parse, s, Value::new_string($e));
        });

        ($s:expr) => (expect_str_ok!($s, $s));
    }

    expect_str_ok!("");
    expect_str_ok!("abc");
    expect_str_ok!("Hello, World!!");
    expect_str_ok!("\n");
    expect_str_ok!(r"\n", "\n");
    expect_str_ok!("\t");
    expect_str_ok!(r"\t", "\t");
    expect_str_ok!(r"\\\\", "\\\\");
    expect_str_ok!(r#"Hi there: \" \\ \n \t"#, "Hi there: \" \\ \n \t");

    expect_error!(parse, "\"", EOF);
    expect_error!(parse, "\"❤\"", 1); // non ascii
}

#[test]
fn pair() {
    let t = Value::new_bool(true);
    let f = Value::new_bool(false);
    let e = Value::empty_list();

    expect_ok!(parse, "(true . false)", Value::new_pair(t.clone(), f.clone()));
    expect_ok!(parse, "(true . (false . ()))", Value::new_pair(t, Value::new_pair(f.clone(), e.clone())));

    expect_error!(parse, "(1 .)", 4);
    expect_error!(parse, "(. 2)", 1);
    expect_error!(parse, "(1 . 2 3)", 7);
    expect_error!(parse, "(1 . 2 . 3)", 7);
    expect_error!(parse, "(1 2 . 3)", 5);
}

#[test]
fn list() {
    expect_ok!(parse, "()", Value::empty_list());
    expect_ok!(parse, r#"(1 "2" (3 . 4))"#, Value::new_list(&vec![Value::new_integer(1), Value::new_string("2"), Value::new_pair(Value::new_integer(3), Value::new_integer(4))]));
    expect_ok!(parse, "(() ())", Value::new_list(&vec![Value::empty_list(), Value::empty_list()]));

    expect_error!(parse, "(( ())");
}

#[test]
fn quote() {
    fn quoted(v: Value) -> Value { Value::new_list(&[Value::new_symbol("quote"), v]) }
    expect_ok!(parse, "'()", quoted(Value::empty_list()));
    expect_ok!(parse, "'1", quoted(Value::new_integer(1)));
    expect_ok!(parse, "'true", quoted(Value::new_bool(true)));
    expect_ok!(parse, r#"'"2""#, quoted(Value::new_string("2")));
    expect_ok!(parse, "'#\\a", quoted(Value::new_char('a')));
    expect_ok!(parse, "'sym", quoted(Value::new_symbol("sym")));
    expect_ok!(parse, "'(1 2)", quoted(Value::new_list(&[Value::new_integer(1), Value::new_integer(2)])));
}
<commit_msg>remove old test<commit_after>use std::usize;
use lalrpop_util::ParseError;
use ::value::Value;
use super::parse;
use super::lexer::{Token, Error};

const EOF: usize = usize::MAX;

fn unrecognized(x: &ParseError<usize, Token, Error>) -> usize {
    if let &ParseError::UnrecognizedToken{expected: _, token: Some((pos, _, _))} = x {
        pos
    } else {
        panic!("expected: unrecognized, got: {:?}", x)
    }
}

fn invalid(x: &ParseError<usize, Token, Error>) -> usize {
    if let &ParseError::InvalidToken{location: pos} = x {
        pos
    } else {
        panic!("expected: invalid, got: {:?}", x)
    }
}

fn whatever(x: &ParseError<usize, Token, Error>) -> usize {
    match x {
        &ParseError::InvalidToken{location: pos} => pos,
        &ParseError::UnrecognizedToken{expected: _, token: Some((pos, _, _))} => pos,
        &ParseError::User{error: Error::UnexpectedToken(x)} => x,
        &ParseError::User{error: Error::UnexpectedEof} => EOF,
        &ParseError::User{error: Error::InvalidCharacter(x)} => x,
        x => panic!("got: {:?}", x),
    }
}

macro_rules! expect_error {
    // unrecognized token
    ($parser:ident, $input:expr, unrecognized: $position:expr) => (
        expect_error!($parser, $input, unrecognized, true, $position);
    );

    // invalid token
    ($parser:ident, $input:expr, invalid: $position:expr) => (
        expect_error!($parser, $input, invalid, true, $position);
    );

    // whatever
    ($parser:ident, $input:expr, $position:expr) => (
        expect_error!($parser, $input, whatever, true, $position);
    );

    // whatever wherever
    ($parser:ident, $input:expr) => (
        expect_error!($parser, $input, whatever, false, 0);
    );

    //internal
    ($parser:ident, $input:expr, $token_fn:ident, $use_pos:expr, $position:expr) => (
        let input = &*$input;
        let result = $parser(input);

        if let Err(ref err) = result {
            if $use_pos && $token_fn(err) != $position {
                panic!("for input: {:?}, got error {:?} at pos {}, expected pos: {}", input, result, $token_fn(err), $position)
            }
        } else {
            panic!("expected error for string: '{}', got: {:?}", input, result);
        }
    );
}

macro_rules! expect_ok {
    // with rest
    ($parser:ident, $input:expr, $expected:expr) => (
        let input = &*$input;
        let expected = $expected;
        let result = $parser(input);
        if let Ok(v) = result {
            if v != expected {
                panic!("parser ok, but input: {:?} got: Ok({:?}), expected: {:?}", input, v, &expected);
            }
        } else {
            panic!("input: {:?} got: {:?}, expected: {:?}", input, result, &expected);
        }
    );
}

#[test]
fn bool() {
    expect_ok!(parse, "true", Value::new_bool(true));
    expect_ok!(parse, "false", Value::new_bool(false));
    expect_ok!(parse, "trude", Value::new_symbol("trude"));
    expect_ok!(parse, "fale", Value::new_symbol("fale"));
}

#[test]
fn char() {
    use std::char;
    use std::iter;
    let mut input: String = "#\\".into();
    let printlable_asci = ('!' as u32)..('~' as u32) + 1;
    for x in printlable_asci {
        let c = char::from_u32(x).expect(&format!("tried to create invalid char with: 0x{:X}", x));
        input.push(c);
        { expect_ok!(parse, input, Value::new_char(c)); }
        input.pop();
    }

    let invalid_asci = (0..('!' as u32)).chain(iter::once(127));
    for x in invalid_asci {
        let c = char::from_u32(x).expect(&format!("tried to create invalid char with: 0x{:X}", x));
        input.push(c);
        if c != '\n' && c != '\t' && c != ' ' { expect_error!(parse, input); }
        input.pop();
    }

    // special forms
    expect_ok!(parse, r"#\\s", Value::new_char(' '));
    expect_ok!(parse, r"#\\t", Value::new_char('\t'));
    expect_ok!(parse, r"#\\n", Value::new_char('\n'));
    expect_ok!(parse, r"#\\", Value::new_char('\\'));
    expect_ok!(parse, r"#\", Value::new_symbol(r"#\"));

    expect_error!(parse, "#\\\0", 2);
}

#[test]
fn integer() {
    expect_ok!(parse, "007", Value::new_integer(7));
    expect_ok!(parse, "-007", Value::new_integer(-7));
    expect_ok!(parse, "123456789", Value::new_integer(123456789));
    expect_ok!(parse, "-123456789", Value::new_integer(-123456789));

    expect_error!(parse, "123b456789", 3);
    expect_error!(parse, "123456789c", 9);
    expect_error!(parse, "00-7", 2);
    expect_ok!(parse, "a123456789", Value::new_symbol("a123456789"));
    expect_ok!(parse, "--7", Value::new_symbol("--7"));
}

#[test]
fn symbol() {
    expect_ok!(parse, "+", Value::new_symbol("+"));
    expect_ok!(parse, "-", Value::new_symbol("-"));
    expect_ok!(parse, "#", Value::new_symbol("#"));
    expect_ok!(parse, "a1a", Value::new_symbol("a1a"));
    expect_ok!(parse, "num->str", Value::new_symbol("num->str"));
    expect_ok!(parse, "//", Value::new_symbol("//"));

    // error is at 1 bc lexer tries to lex integer
    expect_error!(parse, "1a", 1);
}

#[test]
fn string() {
    use std::iter;
    fn q(s: &str) -> String {
        iter::once('"')
        .chain(s.chars())
        .chain(iter::once('"'))
        .collect()
    }

    macro_rules! expect_str_ok {
        ($s:expr, $e:expr) => ({
            let s = q($s);
            println!("string: {}", s);
            expect_ok!(parse, s, Value::new_string($e));
        });

        ($s:expr) => (expect_str_ok!($s, $s));
    }

    expect_str_ok!("");
    expect_str_ok!("abc");
    expect_str_ok!("Hello, World!!");
    expect_str_ok!("\n");
    expect_str_ok!(r"\n", "\n");
    expect_str_ok!("\t");
    expect_str_ok!(r"\t", "\t");
    expect_str_ok!(r"\\\\", "\\\\");
    expect_str_ok!(r#"Hi there: \" \\ \n \t"#, "Hi there: \" \\ \n \t");

    expect_error!(parse, "\"", EOF);
    expect_error!(parse, "\"❤\"", 1); // non ascii
}

#[test]
fn pair() {
    let t = Value::new_bool(true);
    let f = Value::new_bool(false);
    let e = Value::empty_list();

    expect_ok!(parse, "(true . false)", Value::new_pair(t.clone(), f.clone()));
    expect_ok!(parse, "(true . (false . ()))", Value::new_pair(t, Value::new_pair(f.clone(), e.clone())));

    expect_error!(parse, "(1 .)", 4);
    expect_error!(parse, "(. 2)", 1);
    expect_error!(parse, "(1 . 2 3)", 7);
    expect_error!(parse, "(1 . 2 . 3)", 7);
    expect_error!(parse, "(1 2 . 3)", 5);
}

#[test]
fn list() {
    expect_ok!(parse, "()", Value::empty_list());
    expect_ok!(parse, r#"(1 "2" (3 . 4))"#, Value::new_list(&vec![Value::new_integer(1), Value::new_string("2"), Value::new_pair(Value::new_integer(3), Value::new_integer(4))]));
    expect_ok!(parse, "(() ())", Value::new_list(&vec![Value::empty_list(), Value::empty_list()]));

    expect_error!(parse, "(( ())");
}

#[test]
fn quote() {
    fn quoted(v: Value) -> Value { Value::new_list(&[Value::new_symbol("quote"), v]) }
    expect_ok!(parse, "'()", quoted(Value::empty_list()));
    expect_ok!(parse, "'1", quoted(Value::new_integer(1)));
    expect_ok!(parse, "'true", quoted(Value::new_bool(true)));
    expect_ok!(parse, r#"'"2""#, quoted(Value::new_string("2")));
    expect_ok!(parse, "'#\\a", quoted(Value::new_char('a')));
    expect_ok!(parse, "'sym", quoted(Value::new_symbol("sym")));
    expect_ok!(parse, "'(1 2)", quoted(Value::new_list(&[Value::new_integer(1), Value::new_integer(2)])));
}
<|endoftext|>


<commit_before>use parking_lot::{Mutex, MutexGuard};

use gc::swiper::card::CardTable;
use gc::swiper::controller::SharedHeapConfig;
use gc::swiper::crossing::CrossingMap;
use gc::swiper::{CARD_SIZE, CARD_SIZE_BITS};
use gc::{arena, gen_aligned, Address, Region, GEN_SIZE};
use mem;
use object::offset_of_array_data;

pub struct OldGen {
    total: Region,
    protected: Mutex<OldGenProtected>,

    crossing_map: CrossingMap,
    card_table: CardTable,
    config: SharedHeapConfig,
}

impl OldGen {
    pub fn new(
        start: Address,
        end: Address,
        crossing_map: CrossingMap,
        card_table: CardTable,
        config: SharedHeapConfig,
    ) -> OldGen {
        let total = Region::new(start, end);
        let old = OldGen {
            total: total.clone(),
            protected: Mutex::new(OldGenProtected::new(total)),

            crossing_map: crossing_map,
            card_table: card_table,
            config: config,
        };

        old
    }

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

    pub fn committed_size(&self) -> usize {
        let protected = self.protected.lock();

        protected.size
    }

    pub fn active_size(&self) -> usize {
        let protected = self.protected.lock();
        let mut size = 0;

        for old_region in &protected.regions {
            size += old_region.active_size();
        }

        size
    }

    pub fn top(&self) -> Address {
        let protected = self.protected.lock();
        protected.top()
    }

    pub fn limit(&self) -> Address {
        let protected = self.protected.lock();
        protected.limit()
    }

    pub fn update_top(&self, top: Address) {
        let mut protected = self.protected.lock();
        protected.update_top(top);
    }

    pub fn set_committed_size(&self, new_size: usize) {
        let mut protected = self.protected.lock();
        protected.set_committed_size(new_size);
    }

    pub fn grow(&self) -> Address {
        let mut protected = self.protected.lock();
        protected.grow(&self.config)
    }

    pub fn alloc(&self, size: usize) -> Address {
        let mut protected = self.protected.lock();
        protected.alloc(&self.config, size)
    }

    pub fn update_crossing(&self, old: Address, new: Address, array_ref: bool) {
        if (old.to_usize() >> CARD_SIZE_BITS) == (new.to_usize() >> CARD_SIZE_BITS) {
            // object does not span multiple cards
            if (old.to_usize() & (CARD_SIZE - 1)) == 0 {
                let card = self.card_table.card_idx(old.into());
                self.crossing_map.set_first_object(card, 0);
            }
        } else if array_ref {
            let new_card_idx = self.card_table.card_idx(new.into());
            let new_card_start = self.card_table.to_address(new_card_idx);

            let old_card_idx = self.card_table.card_idx(old);
            let old_card_end = self.card_table.to_address(old_card_idx).offset(CARD_SIZE);

            let refs_per_card = CARD_SIZE / mem::ptr_width_usize();
            let mut loop_card_start = old_card_idx.to_usize() + 1;

            // If you allocate an object array just before the card end,
            // it could happen that the card starts with part of the header
            // or the length-field.
            if old.offset(offset_of_array_data() as usize) > old_card_end {
                let diff = old_card_end.offset_from(old) / mem::ptr_width_usize();
                self.crossing_map
                    .set_array_start(loop_card_start.into(), diff);

                loop_card_start += 1;
            }

            // all cards between ]old_card; new_card[ are full with references
            for c in loop_card_start..new_card_idx.to_usize() {
                self.crossing_map
                    .set_references_at_start(c.into(), refs_per_card);
            }

            // new_card starts with x references, then next object
            if new_card_idx.to_usize() >= loop_card_start {
                let refs_dist = new.offset_from(new_card_start) / mem::ptr_width_usize();

                if refs_dist > 0 {
                    self.crossing_map
                        .set_references_at_start(new_card_idx, refs_dist);
                } else {
                    self.crossing_map.set_first_object(new_card_idx, 0);
                }
            }
        } else {
            let new_card_idx = self.card_table.card_idx(new.into());
            let new_card_start = self.card_table.to_address(new_card_idx);

            let old_card_idx = self.card_table.card_idx(old.into());

            // all cards between ]old_card; new_card[ are set to NoRefs
            for c in old_card_idx.to_usize() + 1..new_card_idx.to_usize() {
                self.crossing_map.set_no_references(c.into());
            }

            // new_card stores x words of object, then next object
            self.crossing_map.set_first_object(
                new_card_idx,
                new.offset_from(new_card_start) / mem::ptr_width_usize(),
            );
        }
    }

    pub fn protected(&self) -> MutexGuard<OldGenProtected> {
        self.protected.lock()
    }
}

pub struct OldGenProtected {
    pub total: Region,
    pub size: usize,
    pub regions: Vec<OldRegion>,
}

impl OldGenProtected {
    fn new(total: Region) -> OldGenProtected {
        OldGenProtected {
            total: total.clone(),
            size: 0,
            regions: vec![OldRegion::new(total)],
        }
    }

    pub fn contains_slow(&self, addr: Address) -> bool {
        for old_region in &self.regions {
            if old_region.start <= addr && addr < old_region.alloc_top {
                return true;
            }
        }

        false
    }

    pub fn top(&self) -> Address {
        self.single_region().alloc_top
    }

    pub fn limit(&self) -> Address {
        self.single_region().alloc_limit
    }

    pub fn update_top(&mut self, top: Address) {
        assert!(self.total.valid_top(top));
        let region = self.single_region_mut();
        region.alloc_top = top;
    }

    pub fn set_committed_size(&mut self, new_size: usize) {
        assert!(gen_aligned(new_size));
        let total = self.total.clone();
        let region = self.single_region_mut();

        let old_committed = region.alloc_limit;
        let new_committed = total.start.offset(new_size);
        assert!(new_committed <= total.end);
        assert!(new_committed <= region.end);
        assert!(region.alloc_top <= new_committed);

        if old_committed < new_committed {
            let size = new_committed.offset_from(old_committed);
            arena::commit(old_committed.into(), size, false);
        } else if old_committed > new_committed {
            let size = old_committed.offset_from(new_committed);
            arena::forget(new_committed.into(), size);
        }

        region.alloc_limit = new_committed;
    }

    pub fn grow(&mut self, config: &SharedHeapConfig) -> Address {
        let mut config = config.lock();

        let new_alloc_limit = self.single_region().alloc_limit.offset(GEN_SIZE);

        if !config.grow_old(GEN_SIZE) || !self.total.valid_top(new_alloc_limit) {
            return self.single_region().alloc_limit;
        }

        self.size += GEN_SIZE;

        let region = self.single_region_mut();
        arena::commit(region.alloc_limit, GEN_SIZE, false);
        region.alloc_limit = new_alloc_limit;

        region.alloc_limit
    }

    fn single_region(&self) -> &OldRegion {
        assert!(self.regions.len() == 1);
        self.regions.first().unwrap()
    }

    fn single_region_mut(&mut self) -> &mut OldRegion {
        assert!(self.regions.len() == 1);
        self.regions.first_mut().unwrap()
    }

    pub fn alloc(&mut self, config: &SharedHeapConfig, size: usize) -> Address {
        let ptr = self.pure_alloc(size);

        if ptr.is_non_null() {
            return ptr;
        }

        {
            let mut config = config.lock();

            if !config.grow_old(GEN_SIZE) {
                return Address::null();
            }
        }

        if !self.extend(GEN_SIZE) {
            return Address::null();
        }

        self.pure_alloc(size)
    }

    fn pure_alloc(&mut self, size: usize) -> Address {
        for old_region in &mut self.regions {
            let new_alloc_top = old_region.alloc_top.offset(size);

            if new_alloc_top <= old_region.alloc_limit {
                let addr = old_region.alloc_top;
                old_region.alloc_top = new_alloc_top;
                return addr;
            }
        }

        Address::null()
    }

    fn extend(&mut self, size: usize) -> bool {
        for old_region in &mut self.regions {
            let new_alloc_limit = old_region.alloc_limit.offset(size);

            if new_alloc_limit <= old_region.end {
                arena::commit(old_region.alloc_limit, size, false);
                old_region.alloc_limit = new_alloc_limit;
                return true;
            }
        }

        false
    }
}

#[derive(Clone)]
pub struct OldRegion {
    start: Address,
    end: Address,

    alloc_top: Address,
    alloc_limit: Address,
}

impl OldRegion {
    fn new(region: Region) -> OldRegion {
        OldRegion {
            start: region.start,
            end: region.end,

            alloc_top: region.start,
            alloc_limit: region.start,
        }
    }

    pub fn start(&self) -> Address {
        self.start
    }

    pub fn top(&self) -> Address {
        self.alloc_top
    }

    pub fn size(&self) -> usize {
        self.end.offset_from(self.start)
    }

    pub fn active_size(&self) -> usize {
        self.alloc_top.offset_from(self.start)
    }

    pub fn active_region(&self) -> Region {
        Region::new(self.start, self.alloc_top)
    }

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

    pub fn committed_region(&self) -> Region {
        Region::new(self.start, self.alloc_limit)
    }
}
<commit_msg>swiper: remove grow from OldGenProtected<commit_after>use parking_lot::{Mutex, MutexGuard};

use gc::swiper::card::CardTable;
use gc::swiper::controller::SharedHeapConfig;
use gc::swiper::crossing::CrossingMap;
use gc::swiper::{CARD_SIZE, CARD_SIZE_BITS};
use gc::{arena, gen_aligned, Address, Region, GEN_SIZE};
use mem;
use object::offset_of_array_data;

pub struct OldGen {
    total: Region,
    protected: Mutex<OldGenProtected>,

    crossing_map: CrossingMap,
    card_table: CardTable,
    config: SharedHeapConfig,
}

impl OldGen {
    pub fn new(
        start: Address,
        end: Address,
        crossing_map: CrossingMap,
        card_table: CardTable,
        config: SharedHeapConfig,
    ) -> OldGen {
        let total = Region::new(start, end);
        let old = OldGen {
            total: total.clone(),
            protected: Mutex::new(OldGenProtected::new(total)),

            crossing_map: crossing_map,
            card_table: card_table,
            config: config,
        };

        old
    }

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

    pub fn committed_size(&self) -> usize {
        let protected = self.protected.lock();

        protected.size
    }

    pub fn active_size(&self) -> usize {
        let protected = self.protected.lock();
        let mut size = 0;

        for old_region in &protected.regions {
            size += old_region.active_size();
        }

        size
    }

    pub fn top(&self) -> Address {
        let protected = self.protected.lock();
        protected.top()
    }

    pub fn limit(&self) -> Address {
        let protected = self.protected.lock();
        protected.limit()
    }

    pub fn update_top(&self, top: Address) {
        let mut protected = self.protected.lock();
        protected.update_top(top);
    }

    pub fn set_committed_size(&self, new_size: usize) {
        let mut protected = self.protected.lock();
        protected.set_committed_size(new_size);
    }

    pub fn alloc(&self, size: usize) -> Address {
        let mut protected = self.protected.lock();
        protected.alloc(&self.config, size)
    }

    pub fn update_crossing(&self, old: Address, new: Address, array_ref: bool) {
        if (old.to_usize() >> CARD_SIZE_BITS) == (new.to_usize() >> CARD_SIZE_BITS) {
            // object does not span multiple cards
            if (old.to_usize() & (CARD_SIZE - 1)) == 0 {
                let card = self.card_table.card_idx(old.into());
                self.crossing_map.set_first_object(card, 0);
            }
        } else if array_ref {
            let new_card_idx = self.card_table.card_idx(new.into());
            let new_card_start = self.card_table.to_address(new_card_idx);

            let old_card_idx = self.card_table.card_idx(old);
            let old_card_end = self.card_table.to_address(old_card_idx).offset(CARD_SIZE);

            let refs_per_card = CARD_SIZE / mem::ptr_width_usize();
            let mut loop_card_start = old_card_idx.to_usize() + 1;

            // If you allocate an object array just before the card end,
            // it could happen that the card starts with part of the header
            // or the length-field.
            if old.offset(offset_of_array_data() as usize) > old_card_end {
                let diff = old_card_end.offset_from(old) / mem::ptr_width_usize();
                self.crossing_map
                    .set_array_start(loop_card_start.into(), diff);

                loop_card_start += 1;
            }

            // all cards between ]old_card; new_card[ are full with references
            for c in loop_card_start..new_card_idx.to_usize() {
                self.crossing_map
                    .set_references_at_start(c.into(), refs_per_card);
            }

            // new_card starts with x references, then next object
            if new_card_idx.to_usize() >= loop_card_start {
                let refs_dist = new.offset_from(new_card_start) / mem::ptr_width_usize();

                if refs_dist > 0 {
                    self.crossing_map
                        .set_references_at_start(new_card_idx, refs_dist);
                } else {
                    self.crossing_map.set_first_object(new_card_idx, 0);
                }
            }
        } else {
            let new_card_idx = self.card_table.card_idx(new.into());
            let new_card_start = self.card_table.to_address(new_card_idx);

            let old_card_idx = self.card_table.card_idx(old.into());

            // all cards between ]old_card; new_card[ are set to NoRefs
            for c in old_card_idx.to_usize() + 1..new_card_idx.to_usize() {
                self.crossing_map.set_no_references(c.into());
            }

            // new_card stores x words of object, then next object
            self.crossing_map.set_first_object(
                new_card_idx,
                new.offset_from(new_card_start) / mem::ptr_width_usize(),
            );
        }
    }

    pub fn protected(&self) -> MutexGuard<OldGenProtected> {
        self.protected.lock()
    }
}

pub struct OldGenProtected {
    pub total: Region,
    pub size: usize,
    pub regions: Vec<OldRegion>,
}

impl OldGenProtected {
    fn new(total: Region) -> OldGenProtected {
        OldGenProtected {
            total: total.clone(),
            size: 0,
            regions: vec![OldRegion::new(total)],
        }
    }

    pub fn contains_slow(&self, addr: Address) -> bool {
        for old_region in &self.regions {
            if old_region.start <= addr && addr < old_region.alloc_top {
                return true;
            }
        }

        false
    }

    pub fn top(&self) -> Address {
        self.single_region().alloc_top
    }

    pub fn limit(&self) -> Address {
        self.single_region().alloc_limit
    }

    pub fn update_top(&mut self, top: Address) {
        assert!(self.total.valid_top(top));
        let region = self.single_region_mut();
        region.alloc_top = top;
    }

    pub fn set_committed_size(&mut self, new_size: usize) {
        assert!(gen_aligned(new_size));
        let total = self.total.clone();
        let region = self.single_region_mut();

        let old_committed = region.alloc_limit;
        let new_committed = total.start.offset(new_size);
        assert!(new_committed <= total.end);
        assert!(new_committed <= region.end);
        assert!(region.alloc_top <= new_committed);

        if old_committed < new_committed {
            let size = new_committed.offset_from(old_committed);
            arena::commit(old_committed.into(), size, false);
        } else if old_committed > new_committed {
            let size = old_committed.offset_from(new_committed);
            arena::forget(new_committed.into(), size);
        }

        region.alloc_limit = new_committed;
    }

    fn single_region(&self) -> &OldRegion {
        assert!(self.regions.len() == 1);
        self.regions.first().unwrap()
    }

    fn single_region_mut(&mut self) -> &mut OldRegion {
        assert!(self.regions.len() == 1);
        self.regions.first_mut().unwrap()
    }

    pub fn alloc(&mut self, config: &SharedHeapConfig, size: usize) -> Address {
        let ptr = self.pure_alloc(size);

        if ptr.is_non_null() {
            return ptr;
        }

        {
            let mut config = config.lock();

            if !config.grow_old(GEN_SIZE) {
                return Address::null();
            }
        }

        if !self.extend(GEN_SIZE) {
            return Address::null();
        }

        self.pure_alloc(size)
    }

    fn pure_alloc(&mut self, size: usize) -> Address {
        for old_region in &mut self.regions {
            let new_alloc_top = old_region.alloc_top.offset(size);

            if new_alloc_top <= old_region.alloc_limit {
                let addr = old_region.alloc_top;
                old_region.alloc_top = new_alloc_top;
                return addr;
            }
        }

        Address::null()
    }

    fn extend(&mut self, size: usize) -> bool {
        for old_region in &mut self.regions {
            let new_alloc_limit = old_region.alloc_limit.offset(size);

            if new_alloc_limit <= old_region.end {
                arena::commit(old_region.alloc_limit, size, false);
                old_region.alloc_limit = new_alloc_limit;
                return true;
            }
        }

        false
    }
}

#[derive(Clone)]
pub struct OldRegion {
    start: Address,
    end: Address,

    alloc_top: Address,
    alloc_limit: Address,
}

impl OldRegion {
    fn new(region: Region) -> OldRegion {
        OldRegion {
            start: region.start,
            end: region.end,

            alloc_top: region.start,
            alloc_limit: region.start,
        }
    }

    pub fn start(&self) -> Address {
        self.start
    }

    pub fn top(&self) -> Address {
        self.alloc_top
    }

    pub fn size(&self) -> usize {
        self.end.offset_from(self.start)
    }

    pub fn active_size(&self) -> usize {
        self.alloc_top.offset_from(self.start)
    }

    pub fn active_region(&self) -> Region {
        Region::new(self.start, self.alloc_top)
    }

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

    pub fn committed_region(&self) -> Region {
        Region::new(self.start, self.alloc_limit)
    }
}
<|endoftext|>


<commit_before><commit_msg>Remove offset arg from MergeBreaks::new<commit_after><|endoftext|>


<commit_before>//! Workspace information we get from cargo consists of two pieces. The first is
//! the output of `cargo metadata`. The second is the output of running
//! `build.rs` files (`OUT_DIR` env var, extra cfg flags) and compiling proc
//! macro.
//!
//! This module implements this second part. We use "build script" terminology
//! here, but it covers procedural macros as well.

use std::{cell::RefCell, io, path::PathBuf, process::Command};

use cargo_metadata::{camino::Utf8Path, Message};
use la_arena::ArenaMap;
use paths::AbsPathBuf;
use rustc_hash::FxHashMap;
use semver::Version;
use serde::Deserialize;

use crate::{cfg_flag::CfgFlag, CargoConfig, CargoFeatures, CargoWorkspace, Package};

#[derive(Debug, Default, Clone, PartialEq, Eq)]
pub struct WorkspaceBuildScripts {
    outputs: ArenaMap<Package, Option<BuildScriptOutput>>,
    error: Option<String>,
}

#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub(crate) struct BuildScriptOutput {
    /// List of config flags defined by this package's build script.
    pub(crate) cfgs: Vec<CfgFlag>,
    /// List of cargo-related environment variables with their value.
    ///
    /// If the package has a build script which defines environment variables,
    /// they can also be found here.
    pub(crate) envs: Vec<(String, String)>,
    /// Directory where a build script might place its output.
    pub(crate) out_dir: Option<AbsPathBuf>,
    /// Path to the proc-macro library file if this package exposes proc-macros.
    pub(crate) proc_macro_dylib_path: Option<AbsPathBuf>,
}

impl WorkspaceBuildScripts {
    fn build_command(config: &CargoConfig) -> Command {
        if let Some([program, args @ ..]) = config.run_build_script_command.as_deref() {
            let mut cmd = Command::new(program);
            cmd.args(args);
            cmd.envs(&config.extra_env);
            return cmd;
        }

        let mut cmd = Command::new(toolchain::cargo());
        cmd.envs(&config.extra_env);
        cmd.args(&["check", "--quiet", "--workspace", "--message-format=json"]);

        // --all-targets includes tests, benches and examples in addition to the
        // default lib and bins. This is an independent concept from the --targets
        // flag below.
        cmd.arg("--all-targets");

        if let Some(target) = &config.target {
            cmd.args(&["--target", target]);
        }

        match &config.features {
            CargoFeatures::All => {
                cmd.arg("--all-features");
            }
            CargoFeatures::Selected { features, no_default_features } => {
                if *no_default_features {
                    cmd.arg("--no-default-features");
                }
                if !features.is_empty() {
                    cmd.arg("--features");
                    cmd.arg(features.join(" "));
                }
            }
        }

        cmd
    }

    pub(crate) fn run(
        config: &CargoConfig,
        workspace: &CargoWorkspace,
        progress: &dyn Fn(String),
        toolchain: &Option<Version>,
    ) -> io::Result<WorkspaceBuildScripts> {
        const RUST_1_62: Version = Version::new(1, 62, 0);

        match Self::run_(Self::build_command(config), config, workspace, progress) {
            Ok(WorkspaceBuildScripts { error: Some(error), .. })
                if toolchain.as_ref().map_or(false, |it| *it >= RUST_1_62) =>
            {
                // building build scripts failed, attempt to build with --keep-going so
                // that we potentially get more build data
                let mut cmd = Self::build_command(config);
                cmd.args(&["-Z", "unstable-options", "--keep-going"]).env("RUSTC_BOOTSTRAP", "1");
                let mut res = Self::run_(cmd, config, workspace, progress)?;
                res.error = Some(error);
                Ok(res)
            }
            res => res,
        }
    }

    fn run_(
        mut cmd: Command,
        config: &CargoConfig,
        workspace: &CargoWorkspace,
        progress: &dyn Fn(String),
    ) -> io::Result<WorkspaceBuildScripts> {
        if config.wrap_rustc_in_build_scripts {
            // Setup RUSTC_WRAPPER to point to `rust-analyzer` binary itself. We use
            // that to compile only proc macros and build scripts during the initial
            // `cargo check`.
            let myself = std::env::current_exe()?;
            cmd.env("RUSTC_WRAPPER", myself);
            cmd.env("RA_RUSTC_WRAPPER", "1");
        }

        cmd.current_dir(workspace.workspace_root());

        let mut res = WorkspaceBuildScripts::default();
        let outputs = &mut res.outputs;
        // NB: Cargo.toml could have been modified between `cargo metadata` and
        // `cargo check`. We shouldn't assume that package ids we see here are
        // exactly those from `config`.
        let mut by_id: FxHashMap<String, Package> = FxHashMap::default();
        for package in workspace.packages() {
            outputs.insert(package, None);
            by_id.insert(workspace[package].id.clone(), package);
        }

        let errors = RefCell::new(String::new());
        let push_err = |err: &str| {
            let mut e = errors.borrow_mut();
            e.push_str(err);
            e.push('\n');
        };

        tracing::info!("Running build scripts: {:?}", cmd);
        let output = stdx::process::spawn_with_streaming_output(
            cmd,
            &mut |line| {
                // Copy-pasted from existing cargo_metadata. It seems like we
                // should be using serde_stacker here?
                let mut deserializer = serde_json::Deserializer::from_str(line);
                deserializer.disable_recursion_limit();
                let message = Message::deserialize(&mut deserializer)
                    .unwrap_or_else(|_| Message::TextLine(line.to_string()));

                match message {
                    Message::BuildScriptExecuted(message) => {
                        let package = match by_id.get(&message.package_id.repr) {
                            Some(&it) => it,
                            None => return,
                        };
                        let cfgs = {
                            let mut acc = Vec::new();
                            for cfg in message.cfgs {
                                match cfg.parse::<CfgFlag>() {
                                    Ok(it) => acc.push(it),
                                    Err(err) => {
                                        push_err(&format!(
                                            "invalid cfg from cargo-metadata: {}",
                                            err
                                        ));
                                        return;
                                    }
                                };
                            }
                            acc
                        };
                        // cargo_metadata crate returns default (empty) path for
                        // older cargos, which is not absolute, so work around that.
                        let out_dir = message.out_dir.into_os_string();
                        if !out_dir.is_empty() {
                            let data = outputs[package].get_or_insert_with(Default::default);
                            data.out_dir = Some(AbsPathBuf::assert(PathBuf::from(out_dir)));
                            data.cfgs = cfgs;
                        }
                        if !message.env.is_empty() {
                            outputs[package].get_or_insert_with(Default::default).envs =
                                message.env;
                        }
                    }
                    Message::CompilerArtifact(message) => {
                        let package = match by_id.get(&message.package_id.repr) {
                            Some(it) => *it,
                            None => return,
                        };

                        progress(format!("metadata {}", message.target.name));

                        if message.target.kind.iter().any(|k| k == "proc-macro") {
                            // Skip rmeta file
                            if let Some(filename) =
                                message.filenames.iter().find(|name| is_dylib(name))
                            {
                                let filename = AbsPathBuf::assert(PathBuf::from(&filename));
                                outputs[package]
                                    .get_or_insert_with(Default::default)
                                    .proc_macro_dylib_path = Some(filename);
                            }
                        }
                    }
                    Message::CompilerMessage(message) => {
                        progress(message.target.name);

                        if let Some(diag) = message.message.rendered.as_deref() {
                            push_err(diag);
                        }
                    }
                    Message::BuildFinished(_) => {}
                    Message::TextLine(_) => {}
                    _ => {}
                }
            },
            &mut |line| {
                push_err(line);
            },
        )?;

        for package in workspace.packages() {
            if let Some(package_build_data) = &mut outputs[package] {
                tracing::info!(
                    "{}: {:?}",
                    workspace[package].manifest.parent().display(),
                    package_build_data,
                );
                // inject_cargo_env(package, package_build_data);
                if let Some(out_dir) = &package_build_data.out_dir {
                    // NOTE: cargo and rustc seem to hide non-UTF-8 strings from env! and option_env!()
                    if let Some(out_dir) = out_dir.as_os_str().to_str().map(|s| s.to_owned()) {
                        package_build_data.envs.push(("OUT_DIR".to_string(), out_dir));
                    }
                }
            }
        }

        let mut errors = errors.into_inner();
        if !output.status.success() {
            if errors.is_empty() {
                errors = "cargo check failed".to_string();
            }
            res.error = Some(errors);
        }

        Ok(res)
    }

    pub fn error(&self) -> Option<&str> {
        self.error.as_deref()
    }

    pub(crate) fn get_output(&self, idx: Package) -> Option<&BuildScriptOutput> {
        self.outputs.get(idx)?.as_ref()
    }
}

// FIXME: File a better way to know if it is a dylib.
fn is_dylib(path: &Utf8Path) -> bool {
    match path.extension().map(|e| e.to_string().to_lowercase()) {
        None => false,
        Some(ext) => matches!(ext.as_str(), "dll" | "dylib" | "so"),
    }
}
<commit_msg>Auto merge of #13385 - Veykril:progress, r=Veykril<commit_after>//! Workspace information we get from cargo consists of two pieces. The first is
//! the output of `cargo metadata`. The second is the output of running
//! `build.rs` files (`OUT_DIR` env var, extra cfg flags) and compiling proc
//! macro.
//!
//! This module implements this second part. We use "build script" terminology
//! here, but it covers procedural macros as well.

use std::{cell::RefCell, io, path::PathBuf, process::Command};

use cargo_metadata::{camino::Utf8Path, Message};
use la_arena::ArenaMap;
use paths::AbsPathBuf;
use rustc_hash::FxHashMap;
use semver::Version;
use serde::Deserialize;

use crate::{cfg_flag::CfgFlag, CargoConfig, CargoFeatures, CargoWorkspace, Package};

#[derive(Debug, Default, Clone, PartialEq, Eq)]
pub struct WorkspaceBuildScripts {
    outputs: ArenaMap<Package, Option<BuildScriptOutput>>,
    error: Option<String>,
}

#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub(crate) struct BuildScriptOutput {
    /// List of config flags defined by this package's build script.
    pub(crate) cfgs: Vec<CfgFlag>,
    /// List of cargo-related environment variables with their value.
    ///
    /// If the package has a build script which defines environment variables,
    /// they can also be found here.
    pub(crate) envs: Vec<(String, String)>,
    /// Directory where a build script might place its output.
    pub(crate) out_dir: Option<AbsPathBuf>,
    /// Path to the proc-macro library file if this package exposes proc-macros.
    pub(crate) proc_macro_dylib_path: Option<AbsPathBuf>,
}

impl WorkspaceBuildScripts {
    fn build_command(config: &CargoConfig) -> Command {
        if let Some([program, args @ ..]) = config.run_build_script_command.as_deref() {
            let mut cmd = Command::new(program);
            cmd.args(args);
            cmd.envs(&config.extra_env);
            return cmd;
        }

        let mut cmd = Command::new(toolchain::cargo());
        cmd.envs(&config.extra_env);
        cmd.args(&["check", "--quiet", "--workspace", "--message-format=json"]);

        // --all-targets includes tests, benches and examples in addition to the
        // default lib and bins. This is an independent concept from the --targets
        // flag below.
        cmd.arg("--all-targets");

        if let Some(target) = &config.target {
            cmd.args(&["--target", target]);
        }

        match &config.features {
            CargoFeatures::All => {
                cmd.arg("--all-features");
            }
            CargoFeatures::Selected { features, no_default_features } => {
                if *no_default_features {
                    cmd.arg("--no-default-features");
                }
                if !features.is_empty() {
                    cmd.arg("--features");
                    cmd.arg(features.join(" "));
                }
            }
        }

        cmd
    }

    pub(crate) fn run(
        config: &CargoConfig,
        workspace: &CargoWorkspace,
        progress: &dyn Fn(String),
        toolchain: &Option<Version>,
    ) -> io::Result<WorkspaceBuildScripts> {
        const RUST_1_62: Version = Version::new(1, 62, 0);

        match Self::run_(Self::build_command(config), config, workspace, progress) {
            Ok(WorkspaceBuildScripts { error: Some(error), .. })
                if toolchain.as_ref().map_or(false, |it| *it >= RUST_1_62) =>
            {
                // building build scripts failed, attempt to build with --keep-going so
                // that we potentially get more build data
                let mut cmd = Self::build_command(config);
                cmd.args(&["-Z", "unstable-options", "--keep-going"]).env("RUSTC_BOOTSTRAP", "1");
                let mut res = Self::run_(cmd, config, workspace, progress)?;
                res.error = Some(error);
                Ok(res)
            }
            res => res,
        }
    }

    fn run_(
        mut cmd: Command,
        config: &CargoConfig,
        workspace: &CargoWorkspace,
        progress: &dyn Fn(String),
    ) -> io::Result<WorkspaceBuildScripts> {
        if config.wrap_rustc_in_build_scripts {
            // Setup RUSTC_WRAPPER to point to `rust-analyzer` binary itself. We use
            // that to compile only proc macros and build scripts during the initial
            // `cargo check`.
            let myself = std::env::current_exe()?;
            cmd.env("RUSTC_WRAPPER", myself);
            cmd.env("RA_RUSTC_WRAPPER", "1");
        }

        cmd.current_dir(workspace.workspace_root());

        let mut res = WorkspaceBuildScripts::default();
        let outputs = &mut res.outputs;
        // NB: Cargo.toml could have been modified between `cargo metadata` and
        // `cargo check`. We shouldn't assume that package ids we see here are
        // exactly those from `config`.
        let mut by_id: FxHashMap<String, Package> = FxHashMap::default();
        for package in workspace.packages() {
            outputs.insert(package, None);
            by_id.insert(workspace[package].id.clone(), package);
        }

        let errors = RefCell::new(String::new());
        let push_err = |err: &str| {
            let mut e = errors.borrow_mut();
            e.push_str(err);
            e.push('\n');
        };

        tracing::info!("Running build scripts: {:?}", cmd);
        let output = stdx::process::spawn_with_streaming_output(
            cmd,
            &mut |line| {
                // Copy-pasted from existing cargo_metadata. It seems like we
                // should be using serde_stacker here?
                let mut deserializer = serde_json::Deserializer::from_str(line);
                deserializer.disable_recursion_limit();
                let message = Message::deserialize(&mut deserializer)
                    .unwrap_or_else(|_| Message::TextLine(line.to_string()));

                match message {
                    Message::BuildScriptExecuted(message) => {
                        let package = match by_id.get(&message.package_id.repr) {
                            Some(&it) => it,
                            None => return,
                        };
                        progress(format!("running build-script: {}", workspace[package].name));

                        let cfgs = {
                            let mut acc = Vec::new();
                            for cfg in message.cfgs {
                                match cfg.parse::<CfgFlag>() {
                                    Ok(it) => acc.push(it),
                                    Err(err) => {
                                        push_err(&format!(
                                            "invalid cfg from cargo-metadata: {}",
                                            err
                                        ));
                                        return;
                                    }
                                };
                            }
                            acc
                        };
                        // cargo_metadata crate returns default (empty) path for
                        // older cargos, which is not absolute, so work around that.
                        let out_dir = message.out_dir.into_os_string();
                        if !out_dir.is_empty() {
                            let data = outputs[package].get_or_insert_with(Default::default);
                            data.out_dir = Some(AbsPathBuf::assert(PathBuf::from(out_dir)));
                            data.cfgs = cfgs;
                        }
                        if !message.env.is_empty() {
                            outputs[package].get_or_insert_with(Default::default).envs =
                                message.env;
                        }
                    }
                    Message::CompilerArtifact(message) => {
                        let package = match by_id.get(&message.package_id.repr) {
                            Some(it) => *it,
                            None => return,
                        };

                        progress(format!("building proc-macros: {}", message.target.name));

                        if message.target.kind.iter().any(|k| k == "proc-macro") {
                            // Skip rmeta file
                            if let Some(filename) =
                                message.filenames.iter().find(|name| is_dylib(name))
                            {
                                let filename = AbsPathBuf::assert(PathBuf::from(&filename));
                                outputs[package]
                                    .get_or_insert_with(Default::default)
                                    .proc_macro_dylib_path = Some(filename);
                            }
                        }
                    }
                    Message::CompilerMessage(message) => {
                        progress(message.target.name);

                        if let Some(diag) = message.message.rendered.as_deref() {
                            push_err(diag);
                        }
                    }
                    Message::BuildFinished(_) => {}
                    Message::TextLine(_) => {}
                    _ => {}
                }
            },
            &mut |line| {
                push_err(line);
            },
        )?;

        for package in workspace.packages() {
            if let Some(package_build_data) = &mut outputs[package] {
                tracing::info!(
                    "{}: {:?}",
                    workspace[package].manifest.parent().display(),
                    package_build_data,
                );
                // inject_cargo_env(package, package_build_data);
                if let Some(out_dir) = &package_build_data.out_dir {
                    // NOTE: cargo and rustc seem to hide non-UTF-8 strings from env! and option_env!()
                    if let Some(out_dir) = out_dir.as_os_str().to_str().map(|s| s.to_owned()) {
                        package_build_data.envs.push(("OUT_DIR".to_string(), out_dir));
                    }
                }
            }
        }

        let mut errors = errors.into_inner();
        if !output.status.success() {
            if errors.is_empty() {
                errors = "cargo check failed".to_string();
            }
            res.error = Some(errors);
        }

        Ok(res)
    }

    pub fn error(&self) -> Option<&str> {
        self.error.as_deref()
    }

    pub(crate) fn get_output(&self, idx: Package) -> Option<&BuildScriptOutput> {
        self.outputs.get(idx)?.as_ref()
    }
}

// FIXME: File a better way to know if it is a dylib.
fn is_dylib(path: &Utf8Path) -> bool {
    match path.extension().map(|e| e.to_string().to_lowercase()) {
        None => false,
        Some(ext) => matches!(ext.as_str(), "dll" | "dylib" | "so"),
    }
}
<|endoftext|>


<commit_before>extern crate deeplearn;
extern crate gpuarray as ga;

use std::collections::{HashMap, HashSet};
use std::fs::File;
use std::io::{
    self,
    BufReader,
    Write,
};
use std::path::Path;
use std::rc::Rc;

use deeplearn::{init, layers, util, train, Graph};
use deeplearn::op::Relu;
use ga::Array;

fn main() {
    let batch_size = 1;

    let (char_map, rev_char_map, lines) = load_char_rnn_data("data/bible.txt").unwrap();
    let char_classes = char_map.len();
    println!("Loaded char rnn data");
    println!("Character types: {}", char_classes);

    ////////////////////////////////////////////////////////////////////////////////////////////////
    // Build the graph

    let ctx = Rc::new(ga::Context::new());
    let ref mut graph = Graph::new(ctx.clone());

    // Input. 1 batch of rows*columns inputs
    let input = graph.add_variable(vec![batch_size, char_classes], false, 0.0);

    // Layer 1
    // LSTM layer with 200 cells
    let (l1_out, _) = layers::lstm(graph, input, 200, init::Normal(0.1, 0.5));

    // Layer 2
    // LSTM layer with 200 cells
    let (l2_out, _) = layers::lstm(graph, l1_out, 200, init::Normal(0.1, 0.5));

    // Layer 3
    // Biased fully connected layer with 26 neurons and ReLU activation
    let (l3_fcb, _, _) = layers::dense_biased(graph, l2_out, char_classes,
                                              init::Normal(0.001, 0.005),  // Weights initializer
                                              init::Normal(0.001, 0.005)); // Bias initializer
    let l3_out = layers::activation(graph, Relu(l3_fcb));

    // Loss
    let (loss_out, train_out) = layers::mse(graph, l3_out);
    let loss_d = graph.add_gradient(loss_out); // Create a gradient to apply to the loss function

    ////////////////////////////////////////////////////////////////////////////////////////////////
    // Train and validate the network

    // We apply a gradient of -0.001 to the loss function
    let loss_d_cpu = Array::new(vec![batch_size, char_classes], -0.01);
    loss_d.write(graph, &loss_d_cpu);

    let samples = 500;
    let mut i = 0;
    for line in &lines {
        for t in 1..line.len() {
            input.write(graph, &char_map[&line[t-1]]);
            train_out.write(graph, &char_map[&line[t]]);
            graph.forward_rnn(t-1);
        }
        for t in (1..line.len()).rev() {
            graph.backward_rnn(t-1);
        }

        graph.reset_rnn();
        train::apply_gradients(graph);

        println!("{}", i);

        i += 1;
        if i > samples {
            break;
        }
    }

    let mut l3_out_cpu = Array::new(vec![batch_size, char_classes], 0.0);
    let mut argmax_out = Array::new(vec![batch_size], 0usize);

    loop {
        // Print prompt
        print!(">");
        io::stdout().flush().unwrap();

        // Get seed string
        let mut seed = String::new();
        io::stdin().read_line(&mut seed).unwrap();

        // Input the seed string
        let mut last_char = 0u8;
        for c in seed.trim_right().as_bytes() {
            print!("{}", *c as char);
            input.write(graph, &char_map[c]);
            graph.forward();
            l3_out.read(graph, &mut l3_out_cpu);
            util::argmax_rows(&l3_out_cpu, &mut argmax_out);
            last_char = rev_char_map[&argmax_out[&[0]]];
        }

        // Generate the rest of the output
        for _ in 0..150 {
            print!("{}", last_char as char);
            io::stdout().flush().unwrap();

            input.write(graph, &char_map[&last_char]);
            graph.forward();
            l3_out.read(graph, &mut l3_out_cpu);
            //println!("{:?}", l3_out_cpu);
            util::argmax_rows(&l3_out_cpu, &mut argmax_out);
            last_char = rev_char_map[&argmax_out[&[0]]];

            if last_char == b'\n' {
                break;
            }
        }
        println!("");
    }
}

pub fn load_char_rnn_data<P: AsRef<Path>>(path: P)
    -> io::Result<(HashMap<u8, Array<f32>>, HashMap<usize, u8>, Vec<Vec<u8>>)>
{
    use std::io::BufRead;

    let ref mut file = BufReader::new(File::open(path).unwrap());

    let mut unique_chars = HashSet::new();
    let mut lines = vec![];

    for line in file.lines() {
        let line: String = try!(line) + "\n";
        for c in line.as_bytes() {
            unique_chars.insert(*c);
        }
        lines.push(line.as_bytes().to_owned());
    }

    let char_classes = unique_chars.len();
    let mut char_map = HashMap::new();
    let mut rev_char_map = HashMap::new();
    for (i, c) in unique_chars.into_iter().enumerate() {
        char_map.insert(c, util::one_hot_row(i, char_classes));
        rev_char_map.insert(i, c);
    }

    Ok((char_map, rev_char_map, lines))
}
<commit_msg>Removed char_rnn example. Use char_rnn_unrolled instead<commit_after><|endoftext|>


<commit_before>use crate::data_access::jisho_repository::JishoRepository;
use crate::models::jisho_definition::JishoDefinition;
use reqwest;
use serde_derive::Deserialize;

#[derive(Deserialize)]
struct JishoOrgApiResponse {
    data: Vec<Data>,
}

#[derive(Deserialize)]
struct Data {
    japanese: Vec<Japanese>,
    senses: Vec<Senses>,
}

#[derive(Deserialize)]
struct Japanese {
    word: Option<String>,
    reading: String,
}

#[derive(Deserialize)]
struct Senses {
    english_definitions: Vec<String>,
    parts_of_speech: Vec<String>,
}

pub struct JishoOrgRepository;

#[derive(Debug)]
struct JishoOrgRepositoryError {
    description: String,
}

impl JishoOrgRepository {
    pub fn default() -> JishoOrgRepository {
        JishoOrgRepository
    }
}

fn format_search_parameters(parameters: Vec<&str>) -> String {
    parameters.join("%20")
}

impl JishoRepository for JishoOrgRepository {
    fn get_definition(&self, query: Vec<&str>) -> Result<JishoDefinition, Box<std::error::Error>> {
        let parameters = format_search_parameters(query);
        let request_uri = format!(
            "https://jisho.org/api/v1/search/words?keyword={}",
            parameters
        );

        let response_body: String = reqwest::get(&request_uri)?.text()?;

        let response_models: JishoOrgApiResponse = serde_json::from_str(&response_body)?;

        let first_response_model_data = response_models.data.first().ok_or_else(|| {
            JishoOrgRepositoryError::new(String::from("Got no item in response from jisho api"))
        })?;

        let japanese_section = first_response_model_data.japanese.first().ok_or_else(|| {
            JishoOrgRepositoryError::new(String::from("japanese section was empty"))
        })?;

        let senses_section = first_response_model_data.senses.first().ok_or_else(|| {
            JishoOrgRepositoryError::new(String::from("senses section was empty"))
        })?;

        Ok(JishoDefinition {
            word: match japanese_section
                .word.clone() {
                    Some(word) => word,
                    None => japanese_section.reading.clone(),
                },
            reading: japanese_section.reading.clone(),
            english_definitions: senses_section.english_definitions.clone(),
            parts_of_speech: senses_section.parts_of_speech.clone(),
            link_for_more: format!("https://jisho.org/search/{}", parameters),
        })
    }
}

impl JishoOrgRepositoryError {
    fn new(description: String) -> JishoOrgRepositoryError {
        JishoOrgRepositoryError { description }
    }
}

impl std::error::Error for JishoOrgRepositoryError {}

impl std::fmt::Display for JishoOrgRepositoryError {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "{}", self.description)
    }
}
<commit_msg>run rustfmt<commit_after>use crate::data_access::jisho_repository::JishoRepository;
use crate::models::jisho_definition::JishoDefinition;
use reqwest;
use serde_derive::Deserialize;

#[derive(Deserialize)]
struct JishoOrgApiResponse {
    data: Vec<Data>,
}

#[derive(Deserialize)]
struct Data {
    japanese: Vec<Japanese>,
    senses: Vec<Senses>,
}

#[derive(Deserialize)]
struct Japanese {
    word: Option<String>,
    reading: String,
}

#[derive(Deserialize)]
struct Senses {
    english_definitions: Vec<String>,
    parts_of_speech: Vec<String>,
}

pub struct JishoOrgRepository;

#[derive(Debug)]
struct JishoOrgRepositoryError {
    description: String,
}

impl JishoOrgRepository {
    pub fn default() -> JishoOrgRepository {
        JishoOrgRepository
    }
}

fn format_search_parameters(parameters: Vec<&str>) -> String {
    parameters.join("%20")
}

impl JishoRepository for JishoOrgRepository {
    fn get_definition(&self, query: Vec<&str>) -> Result<JishoDefinition, Box<std::error::Error>> {
        let parameters = format_search_parameters(query);
        let request_uri = format!(
            "https://jisho.org/api/v1/search/words?keyword={}",
            parameters
        );

        let response_body: String = reqwest::get(&request_uri)?.text()?;

        let response_models: JishoOrgApiResponse = serde_json::from_str(&response_body)?;

        let first_response_model_data = response_models.data.first().ok_or_else(|| {
            JishoOrgRepositoryError::new(String::from("Got no item in response from jisho api"))
        })?;

        let japanese_section = first_response_model_data.japanese.first().ok_or_else(|| {
            JishoOrgRepositoryError::new(String::from("japanese section was empty"))
        })?;

        let senses_section = first_response_model_data.senses.first().ok_or_else(|| {
            JishoOrgRepositoryError::new(String::from("senses section was empty"))
        })?;

        Ok(JishoDefinition {
            word: match japanese_section.word.clone() {
                Some(word) => word,
                None => japanese_section.reading.clone(),
            },
            reading: japanese_section.reading.clone(),
            english_definitions: senses_section.english_definitions.clone(),
            parts_of_speech: senses_section.parts_of_speech.clone(),
            link_for_more: format!("https://jisho.org/search/{}", parameters),
        })
    }
}

impl JishoOrgRepositoryError {
    fn new(description: String) -> JishoOrgRepositoryError {
        JishoOrgRepositoryError { description }
    }
}

impl std::error::Error for JishoOrgRepositoryError {}

impl std::fmt::Display for JishoOrgRepositoryError {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "{}", self.description)
    }
}
<|endoftext|>


<commit_before>//! An extension trait that provides the ability to strip IRC colors from a string
use std::borrow::Cow;

#[derive(PartialEq)]
enum ParserState {
    Text,
    ColorCode,
    Foreground1,
    Foreground2,
    Comma,
    Background1,
}
struct Parser {
    state: ParserState,
}

/// An extension trait giving strings a function to strip IRC colors
pub trait FormattedStringExt {

    /// Returns true if the string contains color, bold, underline or italics
    fn is_formatted(&self) -> bool;

    /// Returns the string with all color, bold, underline and italics stripped
    fn strip_formatting(&self) -> Cow<str>;

}

impl FormattedStringExt for str {
    fn is_formatted(&self) -> bool {
        self.contains('\x02') || // bold
            self.contains('\x1F') || // underline
            self.contains('\x16') || // reverse
            self.contains('\x0F') || // normal
            self.contains('\x03') // color
    }

    fn strip_formatting(&self) -> Cow<str> {
        let mut parser = Parser {
            state: ParserState::Text,
        };
        let mut prev: char = '\x00';
        let result: Cow<str> = self
            .chars()
            .filter(move |cur| {
                let result = match parser.state {
                    ParserState::Text | ParserState::Foreground1 | ParserState::Foreground2 if *cur == '\x03' => {
                        parser.state = ParserState::ColorCode;
                        false
                    },
                    ParserState::Text => !['\x02', '\x1F', '\x16', '\x0F'].contains(cur),
                    ParserState::ColorCode if  (*cur).is_digit(10) => {
                        parser.state = ParserState::Foreground1;
                        false
                    },
                    ParserState::Foreground1 if (*cur).is_digit(6) => {
                        // can only consume another digit if previous char was 1.
                        if (prev) == '1' {
                            parser.state = ParserState::Foreground2;
                            false
                        } else {
                            parser.state = ParserState::Text;
                            true
                        }
                    },
                    ParserState::Foreground1 if *cur == ','  => {
                        parser.state = ParserState::Comma;
                        false
                    },
                    ParserState::Foreground2 if *cur == ',' => {
                        parser.state = ParserState::Comma;
                        false
                    },
                    ParserState::Comma if ((*cur).is_digit(10)) => {
                        parser.state = ParserState::Background1;
                        false
                    },
                    ParserState::Background1 if (*cur).is_digit(6) => {
                        // can only consume another digit if previous char was 1.
                        parser.state = ParserState::Text;
                        if (prev) == '1' {
                            false
                        } else {
                            true
                        }
                    }
                    _ => {
                        parser.state = ParserState::Text;
                        true
                    }
                };
                prev = *cur;
                return result
            })
            .collect();

        result
    }

}

impl FormattedStringExt for String {
    fn is_formatted(&self) -> bool {
        (&self[..]).is_formatted()
    }
    fn strip_formatting(&self) -> Cow<str> {
        (&self[..]).strip_formatting()
    }
}

#[cfg(test)]
mod test {
    use proto::colors::FormattedStringExt;

    #[test]
    fn test_strip_bold() {
        assert_eq!("l\x02ol".strip_formatting(), "lol");
    }

    #[test]
    fn test_strip_fg_color() {
        assert_eq!("l\x033ol".strip_formatting(), "lol");
    }

    #[test]
    fn test_strip_fg_color2() {
        assert_eq!("l\x0312ol".strip_formatting(), "lol");
    }

    #[test]
    fn test_strip_fg_bg_11() {
        assert_eq!("l\x031,2ol".strip_formatting(), "lol");
    }
    #[test]
    fn test_strip_fg_bg_21() {
        assert_eq!("l\x0312,3ol".strip_formatting(), "lol");
    }
    #[test]
    fn test_strip_fg_bg_12() {
        assert_eq!("l\x031,12ol".strip_formatting(), "lol");
    }
    #[test]
    fn test_strip_fg_bg_22() {
        assert_eq!("l\x0312,13ol".strip_formatting(), "lol");
    }
    #[test]
    fn test_strip_string_with_multiple_colors() {
        assert_eq!("hoo\x034r\x033a\x0312y".strip_formatting(), "hooray");
    }
    #[test]
    fn test_strip_string_with_digit_after_color() {
        assert_eq!("\x0344\x0355\x0366".strip_formatting(), "456");
    }
    #[test]
    fn test_strip_string_with_multiple_2digit_colors() {
        assert_eq!("hoo\x0310r\x0311a\x0312y".strip_formatting(), "hooray");
    }
    #[test]
    fn test_strip_string_with_digit_after_2digit_color() {
        assert_eq!("\x031212\x031111\x031010".strip_formatting(), "121110");
    }
}<commit_msg>Fixed the tests in irc_proto::colors.<commit_after>//! An extension trait that provides the ability to strip IRC colors from a string
use std::borrow::Cow;

#[derive(PartialEq)]
enum ParserState {
    Text,
    ColorCode,
    Foreground1,
    Foreground2,
    Comma,
    Background1,
}
struct Parser {
    state: ParserState,
}

/// An extension trait giving strings a function to strip IRC colors
pub trait FormattedStringExt {

    /// Returns true if the string contains color, bold, underline or italics
    fn is_formatted(&self) -> bool;

    /// Returns the string with all color, bold, underline and italics stripped
    fn strip_formatting(&self) -> Cow<str>;

}

impl FormattedStringExt for str {
    fn is_formatted(&self) -> bool {
        self.contains('\x02') || // bold
            self.contains('\x1F') || // underline
            self.contains('\x16') || // reverse
            self.contains('\x0F') || // normal
            self.contains('\x03') // color
    }

    fn strip_formatting(&self) -> Cow<str> {
        let mut parser = Parser {
            state: ParserState::Text,
        };
        let mut prev: char = '\x00';
        let result: Cow<str> = self
            .chars()
            .filter(move |cur| {
                let result = match parser.state {
                    ParserState::Text | ParserState::Foreground1 | ParserState::Foreground2 if *cur == '\x03' => {
                        parser.state = ParserState::ColorCode;
                        false
                    },
                    ParserState::Text => !['\x02', '\x1F', '\x16', '\x0F'].contains(cur),
                    ParserState::ColorCode if  (*cur).is_digit(10) => {
                        parser.state = ParserState::Foreground1;
                        false
                    },
                    ParserState::Foreground1 if (*cur).is_digit(6) => {
                        // can only consume another digit if previous char was 1.
                        if (prev) == '1' {
                            parser.state = ParserState::Foreground2;
                            false
                        } else {
                            parser.state = ParserState::Text;
                            true
                        }
                    },
                    ParserState::Foreground1 if *cur == ','  => {
                        parser.state = ParserState::Comma;
                        false
                    },
                    ParserState::Foreground2 if *cur == ',' => {
                        parser.state = ParserState::Comma;
                        false
                    },
                    ParserState::Comma if ((*cur).is_digit(10)) => {
                        parser.state = ParserState::Background1;
                        false
                    },
                    ParserState::Background1 if (*cur).is_digit(6) => {
                        // can only consume another digit if previous char was 1.
                        parser.state = ParserState::Text;
                        if (prev) == '1' {
                            false
                        } else {
                            true
                        }
                    }
                    _ => {
                        parser.state = ParserState::Text;
                        true
                    }
                };
                prev = *cur;
                return result
            })
            .collect();

        result
    }

}

impl FormattedStringExt for String {
    fn is_formatted(&self) -> bool {
        (&self[..]).is_formatted()
    }
    fn strip_formatting(&self) -> Cow<str> {
        (&self[..]).strip_formatting()
    }
}

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

    #[test]
    fn test_strip_bold() {
        assert_eq!("l\x02ol".strip_formatting(), "lol");
    }

    #[test]
    fn test_strip_fg_color() {
        assert_eq!("l\x033ol".strip_formatting(), "lol");
    }

    #[test]
    fn test_strip_fg_color2() {
        assert_eq!("l\x0312ol".strip_formatting(), "lol");
    }

    #[test]
    fn test_strip_fg_bg_11() {
        assert_eq!("l\x031,2ol".strip_formatting(), "lol");
    }
    #[test]
    fn test_strip_fg_bg_21() {
        assert_eq!("l\x0312,3ol".strip_formatting(), "lol");
    }
    #[test]
    fn test_strip_fg_bg_12() {
        assert_eq!("l\x031,12ol".strip_formatting(), "lol");
    }
    #[test]
    fn test_strip_fg_bg_22() {
        assert_eq!("l\x0312,13ol".strip_formatting(), "lol");
    }
    #[test]
    fn test_strip_string_with_multiple_colors() {
        assert_eq!("hoo\x034r\x033a\x0312y".strip_formatting(), "hooray");
    }
    #[test]
    fn test_strip_string_with_digit_after_color() {
        assert_eq!("\x0344\x0355\x0366".strip_formatting(), "456");
    }
    #[test]
    fn test_strip_string_with_multiple_2digit_colors() {
        assert_eq!("hoo\x0310r\x0311a\x0312y".strip_formatting(), "hooray");
    }
    #[test]
    fn test_strip_string_with_digit_after_2digit_color() {
        assert_eq!("\x031212\x031111\x031010".strip_formatting(), "121110");
    }
}
<|endoftext|>


<commit_before>//! The interface for interacting with TMUX sessions. All the commands that are
/// built up during the parsing phase get matched to functions here. The
/// functions in this module all build up strings the get passed to a private
/// call. The private call converts them to `CStrings` and makes an "unsafe" system
/// call. All functions go through this `call` function as a common gateway to
/// system calls and can all be easily logged there.
pub mod config;

use libc::system;
use std::ffi::CString;
use std::io;
use std::process::{Command, ExitStatus, Output};

/// The program to call commands on.
static TMUX_NAME: &'static str = "tmux";

/// The gateway to calling any functions on tmux. Most public functions in this
/// module will be fed through this `call` function. This safely creates a new
/// thread to execute the command on. We say "Most" public functions will use
/// this as `attach` specificaly does not use it.
///
/// args: The command we will send to tmux on the host system for execution.
///
/// # Examples
///
/// ```
/// let _ = call(&["new-window", "-t", "muxed", "-c", "~/Projects/muxed/"]);
/// ```
fn call(args: &[&str]) -> Result<Output, io::Error> {
    //println!("{:?}", &args);
    Command::new(TMUX_NAME).args(args).output()
}

/// Attach is called as the last function in a set of commands. After the tmux
/// env has been setup by all previous commands this attaches the user to their
/// daemonized tmux session.
///
/// # Examples
///
/// ```
/// let session_name = "muxed".to_string();
/// tmux::attach(muxed);
/// ```
/// `session_name: The active tmux session name.
pub fn attach(session_name: &str) {
    let line = format!(
        "{} attach -t '{}' {}",
        TMUX_NAME, session_name, ">/dev/null"
    );
    let system_call = CString::new(line.clone()).unwrap();
    //println!("{}", line.clone());
    unsafe {
        system(system_call.as_ptr());
    };
}

/// New session is the first call made in any sequence of commands. It initiates
/// a new tmux session with a default first window name.
/// *Why a randomized first window name?*
///   This is because the `new-session` tmux command does not support the `-c`
/// flag as `new-window` and `split-window` do. So if the first window requires
/// being changed to a default directory `send-keys` must be used after the
/// session and first window are opened. This means the first window is treated in
/// a different code path than any other window that is opened.
/// Instead I've opted to open the session with a randomized (no conflicts)
/// named window, and we close this window out before attaching the user. This
/// allows for us to treat all user defined windows the same.
///
/// `session_name`: The name of the session being created.
/// `tmp_name`: The named first window that we will close out.
///
/// # Examples
///
/// ```
/// use rand::random;
///
/// let session_name = "muxed".to_string();
/// let tmp_window_name = random::<u16>().to_string();
/// tmux::new_session(session_name, tmp_window_name);
/// ```
///
/// `session_name`: The active tmux session name.
/// `tmp_name`: The name for the temp initial window that is created with a new
/// session.
pub fn new_session(session_name: &str, window_name: &str) {
    let _ = call(&["new", "-d", "-s", session_name, "-n", window_name]);
}

/// Split window acts as the command to target named windows in a session to
/// send a make separate a single pane into two panes. Split window should target
/// a specific pane even if only a single pane exists.
///
/// # Examples
///
/// ```
/// let target = "muxed:cargo.0".to_string();
/// tmux::split_window(target, None);
/// ```
///
/// `target`: A string represented by the `{named_session}:{named_window}.{pane}`
/// `root`: An `Option<String>` passed to the `-c` argument to change the current
/// directory.
pub fn split_window(target: &str) {
    let _ = call(&["split-window", "-t", target]);
}

/// New window opens a new window in the named session with the provided window
/// name.
///
/// # Examples
///
/// ```
/// tmux::new_window("muxed".to_string(), "vim".to_string(), None);
/// ```
///
/// `session_name`: The active tmux session name.
/// `window_name`: The desired window name for the new window.
/// `root`: An `Option<String>` passed to the `-c` argument to change the current
/// directory.
pub fn new_window(session_name: &str, window_name: &str) {
    let _ = call(&["new-window", "-t", session_name, "-n", window_name]);
}

/// The layout function will adjust the layout of a tmux window that already has
/// multiple panes. Currently it will only accept standard tmux layout types.
///
/// Possible layout options: even-horizontal, even-vertical, main-horizontal,
/// main-vertical, or tiled.
///
/// # Examples
///
/// ```
/// tmux::layout("muxed:cargo.0".to_string(), "main-vertical".to_string(), None);
/// ```
///
/// `target`: A string represented by the `{named_session}:{named_window}.{pane}`
/// `layout`: The predefined tmux named layout.
pub fn layout(target: &str, layout: &str) {
    let _ = call(&["select-layout", "-t", target, layout]);
}

/// Send Keys executes literal key commands in specified target windows. This is
/// the execution method to perform actions like tailing logs, starting servers,
/// or opening editors.
///
/// `KPEnter` represent the keyboard press of the "Enter" key. So the execution
/// is a user input command followed by "enter"
///
/// # Examples
///
/// ```
/// tmux::send_keys("muxed:cargo.0".to_string(), "vim .".to_string());
/// ```
///
/// `target`: A string represented by the `{named_session}:{named_window}.{pane}`
/// `exec`: The system command to be executed in a particular pane.
pub fn send_keys(target: &str, exec: &str) {
    let _ = call(&["send-keys", "-t", target, exec, "KPEnter"]);
}

/// This is used to re-select the first window before
/// attaching to the session.
///
/// # Examples
///
/// ```
/// tmux::select_window("muxed:cargo.0".to_string());
/// ```
///
/// `target`: A string represented by the `{named_session}:{named_window}.{pane}`
pub fn select_window(target: &str) {
    let _ = call(&["select-window", "-t", target]);
}

/// This is used to re-select the top pane in the first window before
/// attaching to the session.
///
/// # Examples
///
/// ```
/// tmux::select_pane("muxed:cargo.top".to_string());
/// ```
///
/// `target`: A string represented by the `{named_session}:{named_window}.{pane}`
pub fn select_pane(target: &str) {
    let _ = call(&["select-pane", "-t", target]);
}

/// List Windows is used firgure out if a named session is already running.
///
/// # Examples
///
/// ```
/// tmux::has_session("muxed".to_string());
/// => ExitStatus
/// ```
///
/// `target`: A string represented by the `{named_session}`
pub fn has_session(target: &str) -> ExitStatus {
    let output =
        call(&["has-session", "-t", target]).expect("failed to see if the session existed");
    output.status
}

/// Read the tmux config and return a config object
///
/// # Examples
///
/// ```
/// tmux::get_config();
/// => "some-option false\npane-base-index 0"
/// ```
pub fn get_config() -> String {
    let output =
        call(&["start-server", ";", "show-options", "-g"]).expect("couldn't get tmux options");
    String::from_utf8_lossy(&output.stdout).to_string()
}
<commit_msg>fix(pane-index): Use pane-base-index properly<commit_after>//! The interface for interacting with TMUX sessions. All the commands that are
/// built up during the parsing phase get matched to functions here. The
/// functions in this module all build up strings the get passed to a private
/// call. The private call converts them to `CStrings` and makes an "unsafe" system
/// call. All functions go through this `call` function as a common gateway to
/// system calls and can all be easily logged there.
pub mod config;

use libc::system;
use std::ffi::CString;
use std::io;
use std::process::{Command, ExitStatus, Output};

/// The program to call commands on.
static TMUX_NAME: &'static str = "tmux";

/// The gateway to calling any functions on tmux. Most public functions in this
/// module will be fed through this `call` function. This safely creates a new
/// thread to execute the command on. We say "Most" public functions will use
/// this as `attach` specificaly does not use it.
///
/// args: The command we will send to tmux on the host system for execution.
///
/// # Examples
///
/// ```
/// let _ = call(&["new-window", "-t", "muxed", "-c", "~/Projects/muxed/"]);
/// ```
fn call(args: &[&str]) -> Result<Output, io::Error> {
    //println!("{:?}", &args);
    Command::new(TMUX_NAME).args(args).output()
}

/// Attach is called as the last function in a set of commands. After the tmux
/// env has been setup by all previous commands this attaches the user to their
/// daemonized tmux session.
///
/// # Examples
///
/// ```
/// let session_name = "muxed".to_string();
/// tmux::attach(muxed);
/// ```
/// `session_name: The active tmux session name.
pub fn attach(session_name: &str) {
    let line = format!(
        "{} attach -t '{}' {}",
        TMUX_NAME, session_name, ">/dev/null"
    );
    let system_call = CString::new(line.clone()).unwrap();
    //println!("{}", line.clone());
    unsafe {
        system(system_call.as_ptr());
    };
}

/// New session is the first call made in any sequence of commands. It initiates
/// a new tmux session with a default first window name.
/// *Why a randomized first window name?*
///   This is because the `new-session` tmux command does not support the `-c`
/// flag as `new-window` and `split-window` do. So if the first window requires
/// being changed to a default directory `send-keys` must be used after the
/// session and first window are opened. This means the first window is treated in
/// a different code path than any other window that is opened.
/// Instead I've opted to open the session with a randomized (no conflicts)
/// named window, and we close this window out before attaching the user. This
/// allows for us to treat all user defined windows the same.
///
/// `session_name`: The name of the session being created.
/// `tmp_name`: The named first window that we will close out.
///
/// # Examples
///
/// ```
/// use rand::random;
///
/// let session_name = "muxed".to_string();
/// let tmp_window_name = random::<u16>().to_string();
/// tmux::new_session(session_name, tmp_window_name);
/// ```
///
/// `session_name`: The active tmux session name.
/// `tmp_name`: The name for the temp initial window that is created with a new
/// session.
pub fn new_session(session_name: &str, window_name: &str) {
    let _ = call(&["new", "-d", "-s", session_name, "-n", window_name]);
}

/// Split window acts as the command to target named windows in a session to
/// send a make separate a single pane into two panes. Split window should target
/// a specific pane even if only a single pane exists.
///
/// # Examples
///
/// ```
/// let target = "muxed:cargo.0".to_string();
/// tmux::split_window(target, None);
/// ```
///
/// `target`: A string represented by the `{named_session}:{named_window}.{pane}`
/// `root`: An `Option<String>` passed to the `-c` argument to change the current
/// directory.
pub fn split_window(target: &str) {
    let _ = call(&["split-window", "-t", target]);
}

/// New window opens a new window in the named session with the provided window
/// name.
///
/// # Examples
///
/// ```
/// tmux::new_window("muxed".to_string(), "vim".to_string(), None);
/// ```
///
/// `session_name`: The active tmux session name.
/// `window_name`: The desired window name for the new window.
/// `root`: An `Option<String>` passed to the `-c` argument to change the current
/// directory.
pub fn new_window(session_name: &str, window_name: &str) {
    let _ = call(&["new-window", "-t", session_name, "-n", window_name]);
}

/// The layout function will adjust the layout of a tmux window that already has
/// multiple panes. Currently it will only accept standard tmux layout types.
///
/// Possible layout options: even-horizontal, even-vertical, main-horizontal,
/// main-vertical, or tiled.
///
/// # Examples
///
/// ```
/// tmux::layout("muxed:cargo.0".to_string(), "main-vertical".to_string(), None);
/// ```
///
/// `target`: A string represented by the `{named_session}:{named_window}.{pane}`
/// `layout`: The predefined tmux named layout.
pub fn layout(target: &str, layout: &str) {
    let _ = call(&["select-layout", "-t", target, layout]);
}

/// Send Keys executes literal key commands in specified target windows. This is
/// the execution method to perform actions like tailing logs, starting servers,
/// or opening editors.
///
/// `KPEnter` represent the keyboard press of the "Enter" key. So the execution
/// is a user input command followed by "enter"
///
/// # Examples
///
/// ```
/// tmux::send_keys("muxed:cargo.0".to_string(), "vim .".to_string());
/// ```
///
/// `target`: A string represented by the `{named_session}:{named_window}.{pane}`
/// `exec`: The system command to be executed in a particular pane.
pub fn send_keys(target: &str, exec: &str) {
    let _ = call(&["send-keys", "-t", target, exec, "KPEnter"]);
}

/// This is used to re-select the first window before
/// attaching to the session.
///
/// # Examples
///
/// ```
/// tmux::select_window("muxed:cargo.0".to_string());
/// ```
///
/// `target`: A string represented by the `{named_session}:{named_window}.{pane}`
pub fn select_window(target: &str) {
    let _ = call(&["select-window", "-t", target]);
}

/// This is used to re-select the top pane in the first window before
/// attaching to the session.
///
/// # Examples
///
/// ```
/// tmux::select_pane("muxed:cargo.top".to_string());
/// ```
///
/// `target`: A string represented by the `{named_session}:{named_window}.{pane}`
pub fn select_pane(target: &str) {
    let _ = call(&["select-pane", "-t", target]);
}

/// List Windows is used firgure out if a named session is already running.
///
/// # Examples
///
/// ```
/// tmux::has_session("muxed".to_string());
/// => ExitStatus
/// ```
///
/// `target`: A string represented by the `{named_session}`
pub fn has_session(target: &str) -> ExitStatus {
    let output =
        call(&["has-session", "-t", target]).expect("failed to see if the session existed");
    output.status
}

/// Read the tmux config and return a config object
///
/// # Examples
///
/// ```
/// tmux::get_config();
/// => "some-option false\npane-base-index 0"
/// ```
pub fn get_config() -> String {
    let output =
        call(&["start-server", ";", "show-options", "-g", ";", "show-options", "-g", "-w"]).expect("couldn't get tmux options");
    String::from_utf8_lossy(&output.stdout).to_string()
}
<|endoftext|>


<commit_before>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// General test of maybe_uninits state computed by MIR dataflow.

#![feature(rustc_attrs)]
#![feature(stmt_expr_attributes)]

use std::intrinsics::rustc_peek;
use std::mem::{drop, replace};

struct S(i32);

#[rustc_mir_borrowck]
#[rustc_mir(rustc_peek_maybe_uninit,stop_after_dataflow,borrowck_graphviz_postflow="/tmp/uninits-2.dot")]
fn foo(x: &mut S) {
    // `x` is initialized here, so maybe-uninit bit is 0.

    unsafe { *rustc_peek(&x) }; //~ ERROR rustc_peek: bit not set

    ::std::mem::drop(x);

    // `x` definitely uninitialized here, so maybe-uninit bit is 1.
    unsafe { rustc_peek(&x) };
}
fn main() {
    foo(&mut S(13));
    foo(&mut S(13));
}
<commit_msg>placate tidy in compile-fail test.<commit_after>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// General test of maybe_uninits state computed by MIR dataflow.

#![feature(rustc_attrs)]
#![feature(stmt_expr_attributes)]

use std::intrinsics::rustc_peek;
use std::mem::{drop, replace};

struct S(i32);

#[rustc_mir_borrowck]
#[rustc_mir(rustc_peek_maybe_uninit,stop_after_dataflow)]
fn foo(x: &mut S) {
    // `x` is initialized here, so maybe-uninit bit is 0.

    unsafe { *rustc_peek(&x) }; //~ ERROR rustc_peek: bit not set

    ::std::mem::drop(x);

    // `x` definitely uninitialized here, so maybe-uninit bit is 1.
    unsafe { rustc_peek(&x) };
}
fn main() {
    foo(&mut S(13));
    foo(&mut S(13));
}
<|endoftext|>