<commit_before>//! An example of using syntect for testing syntax definitions.
//! Basically exactly the same as what Sublime Text can do,
//! but without needing ST installed
extern crate syntect;
extern crate walkdir;
#[macro_use]
extern crate lazy_static;
extern crate regex;
//extern crate onig;
use syntect::parsing::{SyntaxSet, ParseState, ScopeStack, Scope};
use syntect::highlighting::ScopeSelectors;
use syntect::easy::{ScopeRegionIterator};

use std::path::Path;
use std::io::{BufRead, BufReader};
use std::fs::File;
use std::cmp::{min, max};
use walkdir::{DirEntry, WalkDir, WalkDirIterator};
use std::str::FromStr;
use regex::Regex;

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum SyntaxTestHeaderError {
    MalformedHeader,
    SyntaxDefinitionNotFound,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum SyntaxTestFileResult {
    FailedAssertions(usize, usize),
    Success(usize),
}

lazy_static! {
    pub static ref SYNTAX_TEST_HEADER_PATTERN: Regex = Regex::new(r#"(?xm)
            ^(?P<testtoken_start>\s*\S+)
            \s+SYNTAX\sTEST\s+
            "(?P<syntax_file>[^"]+)"
            \s*(?P<testtoken_end>\S+)?$
        "#).unwrap();
    pub static ref SYNTAX_TEST_ASSERTION_PATTERN: Regex = Regex::new(r#"(?xm)
        \s*(?:
            (?P<begin_of_token><-)|(?P<range>\^+)
        )(.+)$"#).unwrap();
}

#[derive(Debug)]
struct AssertionRange<'a> {
    begin_char: usize,
    end_char: usize,
    scope_selector_text: &'a str
}

#[derive(Debug)]
struct ScopedText {
    scope: Vec<Scope>,
    char_start: usize,
    text_len: usize,
}

#[derive(Debug)]
struct RangeTestResult {
    column_begin: usize,
    column_end: usize,
    success: bool,
}

fn get_line_assertion_details<'a>(testtoken_start: &str, testtoken_end: Option<&str>, line: &'a str) -> Option<AssertionRange<'a>> {
    // if the test start token specified in the test file's header is on the line
    if let Some(index) = line.find(testtoken_start) {
        let (before_token_start, token_and_rest_of_line) = line.split_at(index);
        if before_token_start.trim_left().is_empty() { // if only whitespace precedes the test token on the line
            if let Some(captures) = SYNTAX_TEST_ASSERTION_PATTERN.captures(&token_and_rest_of_line[testtoken_start.len()..]) {
                let mut sst = captures.get(3).unwrap().as_str().trim_right(); // get the scope selector text
                if let Some(token) = testtoken_end { // if there is an end token defined in the test file header
                    sst = sst.trim_right_matches(&token); // trim it from the scope selector text
                }
                return Some(AssertionRange {
                    begin_char: index + if captures.get(2).is_some() { testtoken_start.len() + captures.get(2).unwrap().start() } else { 0 },
                    end_char: index + if captures.get(2).is_some() { testtoken_start.len() + captures.get(2).unwrap().end() } else { 1 },
                    scope_selector_text: sst
                })
            }
        }
    }
    None
}

fn process_assertions(assertion: &AssertionRange, test_against_line_scopes: &Vec<ScopedText>) -> Vec<RangeTestResult> {
    let selector = ScopeSelectors::from_str(assertion.scope_selector_text).unwrap();
    // find the scope at the specified start column, and start matching the selector through the rest of the tokens on the line from there until the end column is reached
    let mut results = Vec::new();
    for scoped_text in test_against_line_scopes.iter().skip_while(|s|s.char_start + s.text_len <= assertion.begin_char).take_while(|s|s.char_start < assertion.end_char) {
        let match_value = selector.does_match(scoped_text.scope.as_slice());
        let result = RangeTestResult {
            column_begin: max(scoped_text.char_start, assertion.begin_char),
            column_end: min(scoped_text.char_start + scoped_text.text_len, assertion.end_char),
            success: match_value.is_some()
        };
        results.push(result);
    }
    // don't ignore assertions after the newline, they should be treated as though they are asserting against the newline
    let last = test_against_line_scopes.last().unwrap();
    if last.char_start + last.text_len < assertion.end_char {
        let match_value = selector.does_match(last.scope.as_slice());
        let result = RangeTestResult {
            column_begin: max(last.char_start + last.text_len, assertion.begin_char),
            column_end: assertion.end_char,
            success: match_value.is_some()
        };
        results.push(result);
    }
    results
}

fn test_file(ss: &SyntaxSet, path: &Path, parse_test_lines: bool) -> Result<SyntaxTestFileResult, SyntaxTestHeaderError> {
    let f = File::open(path).unwrap();
    let mut reader = BufReader::new(f);
    let mut line = String::new();

    // read the first line from the file - if we have reached EOF already, it's an invalid file
    if reader.read_line(&mut line).unwrap() == 0 {
        return Err(SyntaxTestHeaderError::MalformedHeader);
    }

    line = line.replace("\r", &"");

    // parse the syntax test header in the first line of the file
    match SYNTAX_TEST_HEADER_PATTERN.captures(&line.to_string()) {
        Some(captures) => {
            let testtoken_start = captures.name("testtoken_start").unwrap().as_str();
            let testtoken_end = captures.name("testtoken_end").map_or(None, |c|Some(c.as_str()));
            let syntax_file = captures.name("syntax_file").unwrap().as_str();

            // find the relevant syntax definition to parse the file with - case is important!
            println!("The test file references syntax definition file: {}", syntax_file);
            let syntax = match ss.find_syntax_by_path(syntax_file) {
                Some(syntax) => syntax,
                None => return Err(SyntaxTestHeaderError::SyntaxDefinitionNotFound)
            };

            let mut state = ParseState::new(syntax);
            let mut stack = ScopeStack::new();

            let mut current_line_number = 1;
            let mut test_against_line_number = 1;
            let mut scopes_on_line_being_tested = Vec::new();
            let mut previous_non_assertion_line = line.to_string();

            let mut assertion_failures: usize = 0;
            let mut total_assertions: usize = 0;

            loop {
                let mut line_has_assertion = false;
                if let Some(assertion) = get_line_assertion_details(testtoken_start, testtoken_end, &line) {
                    let result = process_assertions(&assertion, &scopes_on_line_being_tested);
                    total_assertions += &assertion.end_char - &assertion.begin_char;
                    for failure in result.iter().filter(|r|!r.success) {
                        let chars: Vec<char> = previous_non_assertion_line.chars().skip(failure.column_begin).take(failure.column_end - failure.column_begin).collect();
                        println!("  Assertion selector {:?} \
                            from line {:?} failed against line {:?}, column range {:?}-{:?} \
                            (with text {:?}) \
                            has scope {:?}",
                            assertion.scope_selector_text.trim(),
                            current_line_number, test_against_line_number, failure.column_begin, failure.column_end,
                            chars,
                            scopes_on_line_being_tested.iter().skip_while(|s|s.char_start + s.text_len <= failure.column_begin).next().unwrap_or(scopes_on_line_being_tested.last().unwrap()).scope
                        );
                        assertion_failures += failure.column_end - failure.column_begin;
                    }
                    line_has_assertion = true;
                }
                if !line_has_assertion || parse_test_lines {
                    if !line_has_assertion {
                        scopes_on_line_being_tested.clear();
                        test_against_line_number = current_line_number;
                        previous_non_assertion_line = line.to_string();
                    }
                    let ops = state.parse_line(&line);
                    let mut col: usize = 0;
                    for (s, op) in ScopeRegionIterator::new(&ops, &line) {
                        stack.apply(op);
                        if s.is_empty() { // in this case we don't care about blank tokens
                            continue;
                        } else if !line_has_assertion {
                            // if the line has no assertions on it, remember the scopes on the line so we can test against them later
                            let len = s.chars().count();
                            scopes_on_line_being_tested.push(
                                ScopedText {
                                    char_start: col,
                                    text_len: len,
                                    scope: stack.as_slice().to_vec()
                                }
                            );
                            // TODO: warn when there are duplicate adjacent (non-meta?) scopes, as it is almost always undesired
                            col += len;
                        }
                    }
                }

                line.clear();
                current_line_number += 1;
                if reader.read_line(&mut line).unwrap() == 0 {
                    break;
                }
                line = line.replace("\r", &"");
            }
            Ok(
                if assertion_failures > 0 {
                    SyntaxTestFileResult::FailedAssertions(assertion_failures, total_assertions)
                } else {
                    SyntaxTestFileResult::Success(total_assertions)
                }
            )
        },
        None => Err(SyntaxTestHeaderError::MalformedHeader)
    }
}

fn main() {
    let args: Vec<String> = std::env::args().collect();
    let tests_path = if args.len() < 2 {
        "."
    } else {
        &args[1]
    };
    let syntaxes_path = if args.len() == 3 {
        &args[2]
    } else {
        ""
    };

    // load the syntaxes from disk if told to
    // (as opposed to from the binary dumps)
    // this helps to ensure that a recompile isn't needed
    // when using this for syntax development
    let mut ss = if syntaxes_path.is_empty() {
        SyntaxSet::load_defaults_newlines() // note we load the version with newlines
    } else {
        SyntaxSet::new()
    };
    if !syntaxes_path.is_empty() {
        println!("loading syntax definitions from {}", syntaxes_path);
        ss.load_syntaxes(&syntaxes_path, true).unwrap(); // note that we load the version with newlines
        ss.link_syntaxes();
    }

    let exit_code = recursive_walk(&ss, &tests_path);
    println!("exiting with code {}", exit_code);
    std::process::exit(exit_code);

}

fn recursive_walk(ss: &SyntaxSet, path: &str) -> i32 {
    let mut exit_code: i32 = 0; // exit with code 0 by default, if all tests pass
    let walker = WalkDir::new(path).into_iter();
    for entry in walker.filter_entry(|e|e.file_type().is_dir() || is_a_syntax_test_file(e)) {
        let entry = entry.unwrap();
        if entry.file_type().is_file() {
            println!("Testing file {}", entry.path().display());
            let result = test_file(&ss, entry.path(), true);
            println!("{:?}", result);
            if exit_code != 2 { // leave exit code 2 if there was an error
                if let Err(_) = result { // set exit code 2 if there was an error
                    exit_code = 2;
                } else if let Ok(ok) = result {
                    if let SyntaxTestFileResult::FailedAssertions(_, _) = ok {
                        exit_code = 1; // otherwise, if there were failures, exit with code 1
                    }
                }
            }
        }
    }
    exit_code
}

fn is_a_syntax_test_file(entry: &DirEntry) -> bool {
    entry.file_name()
         .to_str()
         .map(|s| s.starts_with("syntax_test_"))
         .unwrap_or(false)
}
<commit_msg>support exclude-only scope selectors that don't start with a space<commit_after>//! An example of using syntect for testing syntax definitions.
//! Basically exactly the same as what Sublime Text can do,
//! but without needing ST installed
extern crate syntect;
extern crate walkdir;
#[macro_use]
extern crate lazy_static;
extern crate regex;
//extern crate onig;
use syntect::parsing::{SyntaxSet, ParseState, ScopeStack, Scope};
use syntect::highlighting::ScopeSelectors;
use syntect::easy::{ScopeRegionIterator};

use std::path::Path;
use std::io::{BufRead, BufReader};
use std::fs::File;
use std::cmp::{min, max};
use walkdir::{DirEntry, WalkDir, WalkDirIterator};
use std::str::FromStr;
use regex::Regex;

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum SyntaxTestHeaderError {
    MalformedHeader,
    SyntaxDefinitionNotFound,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum SyntaxTestFileResult {
    FailedAssertions(usize, usize),
    Success(usize),
}

lazy_static! {
    pub static ref SYNTAX_TEST_HEADER_PATTERN: Regex = Regex::new(r#"(?xm)
            ^(?P<testtoken_start>\s*\S+)
            \s+SYNTAX\sTEST\s+
            "(?P<syntax_file>[^"]+)"
            \s*(?P<testtoken_end>\S+)?$
        "#).unwrap();
    pub static ref SYNTAX_TEST_ASSERTION_PATTERN: Regex = Regex::new(r#"(?xm)
        \s*(?:
            (?P<begin_of_token><-)|(?P<range>\^+)
        )(.+)$"#).unwrap();
}

#[derive(Debug)]
struct AssertionRange<'a> {
    begin_char: usize,
    end_char: usize,
    scope_selector_text: &'a str
}

#[derive(Debug)]
struct ScopedText {
    scope: Vec<Scope>,
    char_start: usize,
    text_len: usize,
}

#[derive(Debug)]
struct RangeTestResult {
    column_begin: usize,
    column_end: usize,
    success: bool,
}

fn get_line_assertion_details<'a>(testtoken_start: &str, testtoken_end: Option<&str>, line: &'a str) -> Option<AssertionRange<'a>> {
    // if the test start token specified in the test file's header is on the line
    if let Some(index) = line.find(testtoken_start) {
        let (before_token_start, token_and_rest_of_line) = line.split_at(index);
        if before_token_start.trim_left().is_empty() { // if only whitespace precedes the test token on the line
            if let Some(captures) = SYNTAX_TEST_ASSERTION_PATTERN.captures(&token_and_rest_of_line[testtoken_start.len()..]) {
                let mut sst = captures.get(3).unwrap().as_str().trim_right(); // get the scope selector text
                if let Some(token) = testtoken_end { // if there is an end token defined in the test file header
                    sst = sst.trim_right_matches(&token); // trim it from the scope selector text
                }
                return Some(AssertionRange {
                    begin_char: index + if captures.get(2).is_some() { testtoken_start.len() + captures.get(2).unwrap().start() } else { 0 },
                    end_char: index + if captures.get(2).is_some() { testtoken_start.len() + captures.get(2).unwrap().end() } else { 1 },
                    scope_selector_text: sst
                })
            }
        }
    }
    None
}

fn process_assertions(assertion: &AssertionRange, test_against_line_scopes: &Vec<ScopedText>) -> Vec<RangeTestResult> {
    // format the scope selector to include a space at the beginning, because, currently, ScopeSelector expects excludes to begin with " -"
    // and they are sometimes in the syntax test as ^^^-comment, for example
    let selector = ScopeSelectors::from_str(&format!(" {}", &assertion.scope_selector_text)).unwrap();
    // find the scope at the specified start column, and start matching the selector through the rest of the tokens on the line from there until the end column is reached
    let mut results = Vec::new();
    for scoped_text in test_against_line_scopes.iter().skip_while(|s|s.char_start + s.text_len <= assertion.begin_char).take_while(|s|s.char_start < assertion.end_char) {
        let match_value = selector.does_match(scoped_text.scope.as_slice());
        let result = RangeTestResult {
            column_begin: max(scoped_text.char_start, assertion.begin_char),
            column_end: min(scoped_text.char_start + scoped_text.text_len, assertion.end_char),
            success: match_value.is_some()
        };
        results.push(result);
    }
    // don't ignore assertions after the newline, they should be treated as though they are asserting against the newline
    let last = test_against_line_scopes.last().unwrap();
    if last.char_start + last.text_len < assertion.end_char {
        let match_value = selector.does_match(last.scope.as_slice());
        let result = RangeTestResult {
            column_begin: max(last.char_start + last.text_len, assertion.begin_char),
            column_end: assertion.end_char,
            success: match_value.is_some()
        };
        results.push(result);
    }
    results
}

fn test_file(ss: &SyntaxSet, path: &Path, parse_test_lines: bool) -> Result<SyntaxTestFileResult, SyntaxTestHeaderError> {
    let f = File::open(path).unwrap();
    let mut reader = BufReader::new(f);
    let mut line = String::new();

    // read the first line from the file - if we have reached EOF already, it's an invalid file
    if reader.read_line(&mut line).unwrap() == 0 {
        return Err(SyntaxTestHeaderError::MalformedHeader);
    }

    line = line.replace("\r", &"");

    // parse the syntax test header in the first line of the file
    match SYNTAX_TEST_HEADER_PATTERN.captures(&line.to_string()) {
        Some(captures) => {
            let testtoken_start = captures.name("testtoken_start").unwrap().as_str();
            let testtoken_end = captures.name("testtoken_end").map_or(None, |c|Some(c.as_str()));
            let syntax_file = captures.name("syntax_file").unwrap().as_str();

            // find the relevant syntax definition to parse the file with - case is important!
            println!("The test file references syntax definition file: {}", syntax_file);
            let syntax = match ss.find_syntax_by_path(syntax_file) {
                Some(syntax) => syntax,
                None => return Err(SyntaxTestHeaderError::SyntaxDefinitionNotFound)
            };

            let mut state = ParseState::new(syntax);
            let mut stack = ScopeStack::new();

            let mut current_line_number = 1;
            let mut test_against_line_number = 1;
            let mut scopes_on_line_being_tested = Vec::new();
            let mut previous_non_assertion_line = line.to_string();

            let mut assertion_failures: usize = 0;
            let mut total_assertions: usize = 0;

            loop {
                let mut line_has_assertion = false;
                if let Some(assertion) = get_line_assertion_details(testtoken_start, testtoken_end, &line) {
                    let result = process_assertions(&assertion, &scopes_on_line_being_tested);
                    total_assertions += &assertion.end_char - &assertion.begin_char;
                    for failure in result.iter().filter(|r|!r.success) {
                        let chars: Vec<char> = previous_non_assertion_line.chars().skip(failure.column_begin).take(failure.column_end - failure.column_begin).collect();
                        println!("  Assertion selector {:?} \
                            from line {:?} failed against line {:?}, column range {:?}-{:?} \
                            (with text {:?}) \
                            has scope {:?}",
                            assertion.scope_selector_text.trim(),
                            current_line_number, test_against_line_number, failure.column_begin, failure.column_end,
                            chars,
                            scopes_on_line_being_tested.iter().skip_while(|s|s.char_start + s.text_len <= failure.column_begin).next().unwrap_or(scopes_on_line_being_tested.last().unwrap()).scope
                        );
                        assertion_failures += failure.column_end - failure.column_begin;
                    }
                    line_has_assertion = true;
                }
                if !line_has_assertion || parse_test_lines {
                    if !line_has_assertion {
                        scopes_on_line_being_tested.clear();
                        test_against_line_number = current_line_number;
                        previous_non_assertion_line = line.to_string();
                    }
                    let ops = state.parse_line(&line);
                    let mut col: usize = 0;
                    for (s, op) in ScopeRegionIterator::new(&ops, &line) {
                        stack.apply(op);
                        if s.is_empty() { // in this case we don't care about blank tokens
                            continue;
                        } else if !line_has_assertion {
                            // if the line has no assertions on it, remember the scopes on the line so we can test against them later
                            let len = s.chars().count();
                            scopes_on_line_being_tested.push(
                                ScopedText {
                                    char_start: col,
                                    text_len: len,
                                    scope: stack.as_slice().to_vec()
                                }
                            );
                            // TODO: warn when there are duplicate adjacent (non-meta?) scopes, as it is almost always undesired
                            col += len;
                        }
                    }
                }

                line.clear();
                current_line_number += 1;
                if reader.read_line(&mut line).unwrap() == 0 {
                    break;
                }
                line = line.replace("\r", &"");
            }
            Ok(
                if assertion_failures > 0 {
                    SyntaxTestFileResult::FailedAssertions(assertion_failures, total_assertions)
                } else {
                    SyntaxTestFileResult::Success(total_assertions)
                }
            )
        },
        None => Err(SyntaxTestHeaderError::MalformedHeader)
    }
}

fn main() {
    let args: Vec<String> = std::env::args().collect();
    let tests_path = if args.len() < 2 {
        "."
    } else {
        &args[1]
    };
    let syntaxes_path = if args.len() == 3 {
        &args[2]
    } else {
        ""
    };

    // load the syntaxes from disk if told to
    // (as opposed to from the binary dumps)
    // this helps to ensure that a recompile isn't needed
    // when using this for syntax development
    let mut ss = if syntaxes_path.is_empty() {
        SyntaxSet::load_defaults_newlines() // note we load the version with newlines
    } else {
        SyntaxSet::new()
    };
    if !syntaxes_path.is_empty() {
        println!("loading syntax definitions from {}", syntaxes_path);
        ss.load_syntaxes(&syntaxes_path, true).unwrap(); // note that we load the version with newlines
        ss.link_syntaxes();
    }

    let exit_code = recursive_walk(&ss, &tests_path);
    println!("exiting with code {}", exit_code);
    std::process::exit(exit_code);

}

fn recursive_walk(ss: &SyntaxSet, path: &str) -> i32 {
    let mut exit_code: i32 = 0; // exit with code 0 by default, if all tests pass
    let walker = WalkDir::new(path).into_iter();
    for entry in walker.filter_entry(|e|e.file_type().is_dir() || is_a_syntax_test_file(e)) {
        let entry = entry.unwrap();
        if entry.file_type().is_file() {
            println!("Testing file {}", entry.path().display());
            let result = test_file(&ss, entry.path(), true);
            println!("{:?}", result);
            if exit_code != 2 { // leave exit code 2 if there was an error
                if let Err(_) = result { // set exit code 2 if there was an error
                    exit_code = 2;
                } else if let Ok(ok) = result {
                    if let SyntaxTestFileResult::FailedAssertions(_, _) = ok {
                        exit_code = 1; // otherwise, if there were failures, exit with code 1
                    }
                }
            }
        }
    }
    exit_code
}

fn is_a_syntax_test_file(entry: &DirEntry) -> bool {
    entry.file_name()
         .to_str()
         .map(|s| s.starts_with("syntax_test_"))
         .unwrap_or(false)
}
<|endoftext|>


<commit_before>const _PAGE_SIZE: usize = 0x1000;

pub struct _Page<'a> {
    current_page: *mut _Page<'a>,
    next_object_offset: i32,
    exclusive_pages: i32
}

impl<'a> _Page<'a> {

    pub unsafe fn reset(&mut self) {
        *(self.get_extension_page_location() as *mut *const _Page) = std::ptr::null();
        self.next_object_offset = 0;
        self.exclusive_pages = 0;

        // currentPage = this;
        self.current_page = &mut *self;
    }

    pub unsafe fn clear(&mut self) {
        self.deallocate_extensions();
        self.reset();
    }

    pub fn is_oversized(&self) -> bool {
        self.current_page.is_null()
    }

    unsafe fn get_extension_page_location(&self) -> usize {

        // Convert self to page location
        let self_location = self as *const _Page as usize;

        // Advance one page size so we are past the end of our page
        let location_behind_page = self_location + _PAGE_SIZE;

        // Go back one pointer size
        location_behind_page - std::mem::size_of::<*const _Page>()
    }

    unsafe fn get_next_location(&self) -> usize {
        self as *const _Page as usize + self.next_object_offset as usize
    }

    unsafe fn set_next_location(&mut self, location: usize) {
        let self_location = self as *const _Page as usize;
        self.next_object_offset = (location - self_location as usize) as i32;
    }

    unsafe fn get_next_exclusive_page_location(&self) -> usize  {
        self.get_extension_page_location() - (self.exclusive_pages as usize + 1) * std::mem::size_of::<*const _Page>()
    }

    pub unsafe fn allocate_raw(&mut self, size: usize, align: usize) -> *mut u8 {
        if !std::ptr::eq(self, self.current_page) {

            // We're already known to be full, so we delegate to the current page
            let new_object = (*self.current_page).allocate_raw(size, align);

            // Possibly our current page was also full so we propagate back the new current page
            let allocating_page = _Page::get_page(new_object);
            if !std::ptr::eq(allocating_page, self.current_page) && (!allocating_page.is_oversized()) {
                self.current_page = allocating_page;
            }
            return new_object;
        }

        // Try to allocate from ourselves
        let location = self.get_next_location();
        let next_location = (location + align - 1) & !(align - 1);
        if next_location <= self.get_next_exclusive_page_location() as usize {
            self.set_next_location(next_location);
            return location as *mut u8
        }

        // So the space did not fit.

        // Calculate gross size to decide whether we're oversized
        let gross_size = std::mem::size_of::<_Page>() + size + std::mem::size_of::<*const _Page>();
        if gross_size > _PAGE_SIZE {
            if self.get_next_location() >= self.get_extension_page_location() {

                // Allocate an extension page with default size
                let extension_page = self.allocate_extension_page();

                // Try again with the new extension page
                return extension_page.allocate_raw(size, align);
            }

            // We allocate oversized objects directly.
            let memory = std::alloc::alloc(std::alloc::Layout::from_size_align_unchecked(size + std::mem::size_of::<_Page>(), _PAGE_SIZE) );

            // Initialize a _Page object at the page start
            let page = memory as *mut _Page<'a>;
            (*page).reset();

            // Oversized pages have no current_page
            (*page).current_page = std::ptr::null_mut();

            *(self.get_next_exclusive_page_location() as *mut *const _Page) = page;
            self.exclusive_pages += 1;
            return page.offset(1) as *mut u8;
        }

        // So we're not oversized. Create extension page and let it allocate.
        self.allocate_extension_page().allocate_raw(size, align)
    }

    unsafe fn allocate_extension_page(&mut self) -> &'a mut _Page<'a> {
        let page = _Page::allocate_page();
        *(self.get_extension_page_location() as *mut *const _Page) = page;
        self.current_page = page;
        &mut *page
    }

    unsafe fn allocate_page() -> *mut _Page<'a> {
        let memory = std::alloc::alloc(std::alloc::Layout::from_size_align_unchecked(_PAGE_SIZE, _PAGE_SIZE));
        let page = memory as *mut _Page<'a>;
        (*page).reset();
        page
    }

    pub unsafe fn allocate_exclusive_page(&mut self) -> &'a mut _Page<'a> {

        if !std::ptr::eq(self, self.current_page) {
            // We're already known to be full, so we delegate to the current page
            return (*self.current_page).allocate_exclusive_page();
        }

        // Check first whether we need an ordinary extension
        if self.get_next_location() >= self.get_next_exclusive_page_location() {

            // Allocate an extension page with default size
            return self.allocate_extension_page().allocate_exclusive_page();
        }

        let page = _Page::allocate_page();
        *(self.get_next_exclusive_page_location() as *mut *const _Page) = page;
        self.exclusive_pages += 1;
        &mut *page
    }

    pub fn extend(&mut self, top: usize, size: usize) -> bool {
        unsafe {
            if top != self.get_next_location() {
                return false;
            }

            let new_top = top + size;
            if new_top > self.get_next_exclusive_page_location() {
                return false;
            }
            self.set_next_location(new_top);
        }
        true
    }

    pub unsafe fn get_page(address: *mut u8) -> &'a mut _Page<'a> {
        &mut *((address as usize & !_PAGE_SIZE - 1) as *mut _Page)
    }

    unsafe fn deallocate_extensions(&mut self) {
        unimplemented!()
    }
}

#[test]
fn test_page() {

    unsafe {

        // Allocate a page
        let memory = std::alloc::alloc(std::alloc::Layout::from_size_align_unchecked(_PAGE_SIZE, _PAGE_SIZE) );
        assert_ne!(memory, std::ptr::null_mut());

        // Write some numbers to the page memory
        for i in 0.._PAGE_SIZE {
            let ptr = memory.offset(i as isize);
            *ptr = i as u8
        }

        // Initialize a _Page object at the page start
        let page = &mut *(memory as *mut _Page);

        assert_ne!(page.next_object_offset, 0);
        assert_ne!(page.exclusive_pages, 0);

        page.reset();

        assert_eq!(page.next_object_offset, 0);
        assert_eq!(page.exclusive_pages, 0);
        assert_eq!((*(page.current_page)).next_object_offset, 0);
        assert_eq!((*(page.current_page)).exclusive_pages, 0);
        assert_eq!(page.is_oversized(), false);

    }
}<commit_msg>_Page.deallocate_extensions<commit_after>use std::alloc::alloc;
use std::alloc::dealloc;
use std::alloc::Layout;
use std::mem::size_of;
use std::ptr::eq;
use std::ptr::null;
use std::ptr::null_mut;

const _PAGE_SIZE: usize = 0x1000;

pub struct _Page<'a> {
    current_page: *mut _Page<'a>,
    next_object_offset: i32,
    exclusive_pages: i32
}

impl<'a> _Page<'a> {

    pub unsafe fn reset(&mut self) {
        *(self.get_extension_page_location() as *mut *const _Page) = null();
        self.next_object_offset = 0;
        self.exclusive_pages = 0;

        // currentPage = this;
        self.current_page = &mut *self;
    }

    pub unsafe fn clear(&mut self) {
        self.deallocate_extensions();
        self.reset();
    }

    pub fn is_oversized(&self) -> bool {
        self.current_page.is_null()
    }

    unsafe fn get_extension_page_location(&self) -> usize {

        // Convert self to page location
        let self_location = self as *const _Page as usize;

        // Advance one page size so we are past the end of our page
        let location_behind_page = self_location + _PAGE_SIZE;

        // Go back one pointer size
        location_behind_page - size_of::<*const _Page>()
    }

    unsafe fn get_next_location(&self) -> usize {
        self as *const _Page as usize + self.next_object_offset as usize
    }

    unsafe fn set_next_location(&mut self, location: usize) {
        let self_location = self as *const _Page as usize;
        self.next_object_offset = (location - self_location as usize) as i32;
    }

    unsafe fn get_next_exclusive_page_location(&self) -> usize  {
        self.get_extension_page_location() - (self.exclusive_pages as usize + 1) * size_of::<*const _Page>()
    }

    pub unsafe fn allocate_raw(&mut self, size: usize, align: usize) -> *mut u8 {
        if !eq(self, self.current_page) {

            // We're already known to be full, so we delegate to the current page
            let new_object = (*self.current_page).allocate_raw(size, align);

            // Possibly our current page was also full so we propagate back the new current page
            let allocating_page = _Page::get_page(new_object);
            if !eq(allocating_page, self.current_page) && (!allocating_page.is_oversized()) {
                self.current_page = allocating_page;
            }
            return new_object;
        }

        // Try to allocate from ourselves
        let location = self.get_next_location();
        let next_location = (location + align - 1) & !(align - 1);
        if next_location <= self.get_next_exclusive_page_location() as usize {
            self.set_next_location(next_location);
            return location as *mut u8
        }

        // So the space did not fit.

        // Calculate gross size to decide whether we're oversized
        let gross_size = size_of::<_Page>() + size + size_of::<*const _Page>();
        if gross_size > _PAGE_SIZE {
            if self.get_next_location() >= self.get_extension_page_location() {

                // Allocate an extension page with default size
                let extension_page = self.allocate_extension_page();

                // Try again with the new extension page
                return extension_page.allocate_raw(size, align);
            }

            // We allocate oversized objects directly.
            let memory = alloc(Layout::from_size_align_unchecked(size + size_of::<_Page>(), _PAGE_SIZE) );

            // Initialize a _Page object at the page start
            let page = memory as *mut _Page<'a>;
            (*page).reset();

            // Oversized pages have no current_page
            (*page).current_page = null_mut();

            *(self.get_next_exclusive_page_location() as *mut *const _Page) = page;
            self.exclusive_pages += 1;
            return page.offset(1) as *mut u8;
        }

        // So we're not oversized. Create extension page and let it allocate.
        self.allocate_extension_page().allocate_raw(size, align)
    }

    unsafe fn allocate_extension_page(&mut self) -> &'a mut _Page<'a> {
        let page = _Page::allocate_page();
        *(self.get_extension_page_location() as *mut *const _Page) = page;
        self.current_page = page;
        &mut *page
    }

    unsafe fn allocate_page() -> *mut _Page<'a> {
        let memory = alloc(Layout::from_size_align_unchecked(_PAGE_SIZE, _PAGE_SIZE));
        let page = memory as *mut _Page<'a>;
        (*page).reset();
        page
    }

    pub unsafe fn allocate_exclusive_page(&mut self) -> &'a mut _Page<'a> {

        if !eq(self, self.current_page) {
            // We're already known to be full, so we delegate to the current page
            return (*self.current_page).allocate_exclusive_page();
        }

        // Check first whether we need an ordinary extension
        if self.get_next_location() >= self.get_next_exclusive_page_location() {

            // Allocate an extension page with default size
            return self.allocate_extension_page().allocate_exclusive_page();
        }

        let page = _Page::allocate_page();
        *(self.get_next_exclusive_page_location() as *mut *const _Page) = page;
        self.exclusive_pages += 1;
        &mut *page
    }

    pub fn extend(&mut self, top: usize, size: usize) -> bool {
        unsafe {
            if top != self.get_next_location() {
                return false;
            }

            let new_top = top + size;
            if new_top > self.get_next_exclusive_page_location() {
                return false;
            }
            self.set_next_location(new_top);
        }
        true
    }

    pub fn deallocate_extensions(&mut self) {
        let mut page: *mut _Page<'a> = self;
        unsafe {
            while !page.is_null() {
                let extension_location = (*page).get_extension_page_location();
                for i in 1..(*page).exclusive_pages {
                    let mut exclusive_page = &mut *((extension_location - (i as usize * size_of::<*const _Page> as usize)) as *mut _Page<'a>);
                    if !exclusive_page.is_oversized() {
                        exclusive_page.deallocate_extensions();
                    }
                    exclusive_page.forget();
                }

                if !eq(page, self) {
                    (*page).forget();
                }

                page = extension_location as *mut _Page<'a>;
            }
        }
    }

    pub fn forget(&self) {
        unsafe {
            dealloc(self as *const _Page as *mut u8, Layout::from_size_align_unchecked(_PAGE_SIZE, _PAGE_SIZE))
        }
    }

    pub unsafe fn get_page(address: *mut u8) -> &'a mut _Page<'a> {
        &mut *((address as usize & !_PAGE_SIZE - 1) as *mut _Page)
    }
}

#[test]
fn test_page() {

    unsafe {

        // Allocate a page
        let memory = alloc(Layout::from_size_align_unchecked(_PAGE_SIZE, _PAGE_SIZE) );
        assert_ne!(memory, null_mut());

        // Write some numbers to the page memory
        for i in 0.._PAGE_SIZE {
            let ptr = memory.offset(i as isize);
            *ptr = i as u8
        }

        // Initialize a _Page object at the page start
        let page = &mut *(memory as *mut _Page);

        assert_ne!(page.next_object_offset, 0);
        assert_ne!(page.exclusive_pages, 0);

        page.reset();

        assert_eq!(page.next_object_offset, 0);
        assert_eq!(page.exclusive_pages, 0);
        assert_eq!((*(page.current_page)).next_object_offset, 0);
        assert_eq!((*(page.current_page)).exclusive_pages, 0);
        assert_eq!(page.is_oversized(), false);

    }
}<|endoftext|>


<commit_before>use spin::Mutex;

use crate::memory::Frame;
use crate::paging::{ActivePageTable, Page, PhysicalAddress, VirtualAddress};
use crate::paging::entry::EntryFlags;
use crate::paging::mapper::PageFlushAll;

pub use self::debug::DebugDisplay;
use self::display::Display;
use self::mode_info::VBEModeInfo;
use self::primitive::fast_set64;

pub mod debug;
pub mod display;
pub mod mode_info;
pub mod primitive;

pub static FONT: &'static [u8] = include_bytes!("../../../../res/unifont.font");

pub static DEBUG_DISPLAY: Mutex<Option<DebugDisplay>> = Mutex::new(None);

pub fn init(active_table: &mut ActivePageTable) {
    println!("Starting graphical debug");

    let width;
    let height;
    let physbaseptr;

    {
        let mode_info_addr = 0x5200 + crate::PHYS_OFFSET;
        let mode_info = unsafe { &*(mode_info_addr as *const VBEModeInfo) };
        width = mode_info.xresolution as usize;
        height = mode_info.yresolution as usize;
        physbaseptr = mode_info.physbaseptr as usize;
    }

    println!("Framebuffer {}x{} at {:X}", width, height, physbaseptr);

    {
        let size = width * height;

        let onscreen = physbaseptr + crate::PHYS_OFFSET;
        {
            let flush_all = PageFlushAll::new();
            let start_page = Page::containing_address(VirtualAddress::new(onscreen));
            let end_page = Page::containing_address(VirtualAddress::new(onscreen + size * 4));
            for page in Page::range_inclusive(start_page, end_page) {
                let frame = Frame::containing_address(PhysicalAddress::new(page.start_address().data() - crate::PHYS_OFFSET));
                let flags = EntryFlags::PRESENT | EntryFlags::NO_EXECUTE | EntryFlags::WRITABLE | EntryFlags::HUGE_PAGE;
                let result = active_table.map_to(page, frame, flags);
                flush_all.consume(result);
            }
            flush_all.flush(active_table);
        }

        unsafe { fast_set64(onscreen as *mut u64, 0, size/2) };

        let display = Display::new(width, height, onscreen);
        let debug_display = DebugDisplay::new(display);
        *DEBUG_DISPLAY.lock() = Some(debug_display);
    }
}

pub fn fini(active_table: &mut ActivePageTable) {
    if let Some(debug_display) = DEBUG_DISPLAY.lock().take() {
        let display = debug_display.into_display();
        let onscreen = display.onscreen.as_mut_ptr() as usize;
        let size = display.width * display.height;
        {
            let flush_all = PageFlushAll::new();
            let start_page = Page::containing_address(VirtualAddress::new(onscreen));
            let end_page = Page::containing_address(VirtualAddress::new(onscreen + size * 4));
            for page in Page::range_inclusive(start_page, end_page) {
                let (result, _frame) = active_table.unmap_return(page, false);
                flush_all.consume(result);
            }
            flush_all.flush(active_table);
        }
    }

    println!("Finished graphical debug");
}
<commit_msg>Fix graphical debug feature<commit_after>use spin::Mutex;

use crate::memory::Frame;
use crate::paging::{ActivePageTable, Page, PageFlags, PhysicalAddress, VirtualAddress};
use crate::paging::entry::EntryFlags;
use crate::paging::mapper::PageFlushAll;

pub use self::debug::DebugDisplay;
use self::display::Display;
use self::mode_info::VBEModeInfo;
use self::primitive::fast_set64;

pub mod debug;
pub mod display;
pub mod mode_info;
pub mod primitive;

pub static FONT: &'static [u8] = include_bytes!("../../../../res/unifont.font");

pub static DEBUG_DISPLAY: Mutex<Option<DebugDisplay>> = Mutex::new(None);

pub fn init(active_table: &mut ActivePageTable) {
    println!("Starting graphical debug");

    let width;
    let height;
    let physbaseptr;

    {
        let mode_info_addr = 0x5200 + crate::PHYS_OFFSET;
        let mode_info = unsafe { &*(mode_info_addr as *const VBEModeInfo) };
        width = mode_info.xresolution as usize;
        height = mode_info.yresolution as usize;
        physbaseptr = mode_info.physbaseptr as usize;
    }

    println!("Framebuffer {}x{} at {:X}", width, height, physbaseptr);

    {
        let size = width * height;

        let onscreen = physbaseptr + crate::PHYS_OFFSET;
        {
            let flush_all = PageFlushAll::new();
            let start_page = Page::containing_address(VirtualAddress::new(onscreen));
            let end_page = Page::containing_address(VirtualAddress::new(onscreen + size * 4));
            for page in Page::range_inclusive(start_page, end_page) {
                let frame = Frame::containing_address(PhysicalAddress::new(page.start_address().data() - crate::PHYS_OFFSET));
                let flags = PageFlags::new().write(true).custom_flag(EntryFlags::HUGE_PAGE.bits(), true);
                let result = active_table.map_to(page, frame, flags);
                flush_all.consume(result);
            }
            flush_all.flush();
        }

        unsafe { fast_set64(onscreen as *mut u64, 0, size/2) };

        let display = Display::new(width, height, onscreen);
        let debug_display = DebugDisplay::new(display);
        *DEBUG_DISPLAY.lock() = Some(debug_display);
    }
}

pub fn fini(active_table: &mut ActivePageTable) {
    if let Some(debug_display) = DEBUG_DISPLAY.lock().take() {
        let display = debug_display.into_display();
        let onscreen = display.onscreen.as_mut_ptr() as usize;
        let size = display.width * display.height;
        {
            let flush_all = PageFlushAll::new();
            let start_page = Page::containing_address(VirtualAddress::new(onscreen));
            let end_page = Page::containing_address(VirtualAddress::new(onscreen + size * 4));
            for page in Page::range_inclusive(start_page, end_page) {
                let (result, _frame) = active_table.unmap_return(page, false);
                flush_all.consume(result);
            }
            flush_all.flush();
        }
    }

    println!("Finished graphical debug");
}
<|endoftext|>


<commit_before>use std::io::{self, Error, ErrorKind, Write};
use std::os::unix::io::AsRawFd;
use std::net::SocketAddr;
use mio::net::UdpSocket;
use libc::{sendmmsg, mmsghdr, msghdr, iovec, c_void, c_uint};

pub struct MetricSocket {
  pub addr:   SocketAddr,
  pub socket: UdpSocket,
}

impl Write for MetricSocket {
  fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
    self.socket.send_to(buf, &self.addr)
  }

  fn flush(&mut self) -> io::Result<()> {
    Ok(())
  }
}

pub struct MetricsWriter {
    inner: Option<MetricSocket>,
    buf: Vec<u8>,
    last_newline: usize,
    last_packet_start: usize,
    panicked: bool,
    packet_indexes: Vec<usize>,
}

impl MetricsWriter {
    pub fn new(inner: MetricSocket) -> MetricsWriter {
        MetricsWriter::with_capacity(16384, inner)
    }

    pub fn with_capacity(capacity: usize, inner: MetricSocket) -> MetricsWriter {
        MetricsWriter {
            inner: Some(inner),
            buf: Vec::with_capacity(capacity),
            panicked: false,
            last_newline: 0,
            last_packet_start: 0,
            packet_indexes: vec![],
        }
    }

    pub fn get_ref(&self) -> &MetricSocket {
        self.inner.as_ref().unwrap()
    }

    pub fn get_mut(&mut self) -> &mut MetricSocket {
        self.inner.as_mut().unwrap()
    }

    fn flush_buf(&mut self, flush_entire_buffer: bool) -> io::Result<()> {
        let mut written = 0;
        let len = if flush_entire_buffer {
            self.buf.len()
        } else {
            self.last_newline + 1
        };

        let mut ret = Ok(());
        while written < len {
            self.panicked = true;

            let mut last_index = written;
            let mut iovs = self.packet_indexes.iter().map(|index| {
              let iov = iovec {
                iov_base: (self.buf.as_ptr() as usize + last_index) as *mut c_void,
                iov_len: index - last_index
              };
              /*println!("will add the packet ({} bytes): {}",
                *index -  last_index,
                std::str::from_utf8(&self.buf[last_index..*index]).unwrap());
              */
              //println!("will add the packet ({} bytes) with iov: {:?}", *index -  last_index, iov);

              last_index = *index;
              vec![iov]
            }).collect::<Vec<_>>();

            let mut messages = iovs.iter().map(|iov| {

              mmsghdr {
                msg_hdr: msghdr {
                  msg_name: std::ptr::null_mut(),
                  msg_namelen: 0,
                  msg_iov: iov.as_ptr() as *mut iovec,
                  msg_iovlen: 1,
                  msg_control: std::ptr::null_mut(),
                  msg_controllen: 0,
                  msg_flags: 0,
                },
                msg_len: 0,
              }
            }).collect::<Vec<_>>();
            //println!("created {} packets", messages.len());

            if messages.is_empty() {
              break;
            }

            unsafe {
              let r = libc::sendmmsg(
                self.inner.as_ref().unwrap().socket.as_raw_fd(),
                &mut messages[0] as *mut mmsghdr,
                messages.len() as c_uint,
                0,
              );
              self.panicked = false;

              match r {
                -1 => {
                  //println!("last error: {:?}", io::Error::last_os_error());
                  let e = io::Error::last_os_error();
                  if e.kind() != io::ErrorKind::Interrupted {
                    ret = Err(e);
                    break;
                  }
                },
                sent => {
                  //println!("sent: {:?}", sent);
                  /*for i in 0..messages.len() {
                    println!("message {} wrote {} bytes from {:?}",
                      i, messages[i].msg_len,
                      *messages[i].msg_hdr.msg_iov);
                  }*/

                  let mut currently_written = 0;
                  for i in 0..(sent as usize) {
                    currently_written += messages[i].msg_len as usize;
                  }
                  written += currently_written;
                  //println!("written (packet indexes: {:?}): {}, total {}", self.packet_indexes,
                  //  currently_written, written);
                  //println!("written {}, total {}", currently_written, written);

                  if currently_written == 0 {
                    ret = Err(Error::new(
                        ErrorKind::WriteZero,
                        "failed to write the buffered data",
                    ));
                    break;
                  }

                  if sent as usize == self.packet_indexes.len() {
                    self.packet_indexes.clear();
                    break;
                  } else {
                    for i in 0..(sent as usize) {
                      let _ = self.packet_indexes.remove(0);
                    }
                  }

                }
              }
            };
        }
        //println!("buf len {} last newline {}, written {}", self.buf.len(), self.last_newline, written);
        if written > 0 {
            //println!("FLUSHED: {}", ::std::str::from_utf8(&self.buf[..written]).unwrap());
            self.buf.drain(..written);
        }

        if flush_entire_buffer {
            self.last_newline = 0;
            self.last_packet_start = 0;
        } else if written > self.last_newline {
            self.last_newline = 0;
            //FIXME
            self.last_packet_start = 0;
        } else {
            self.last_newline -= written;
            //FIXME
            self.last_packet_start = 0;
        }

        ret
    }
}

impl Write for MetricsWriter {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        if self.buf.len() + buf.len() > self.buf.capacity() {
            self.flush_buf(false)?;
        }
        if buf.len() >= self.buf.capacity() {
            self.panicked = true;
            let r = self.get_mut().write(buf);
            self.panicked = false;
            r
        } else {
            if let Some(i) = memchr::memrchr(b'\n', buf) {
                let newline_index = self.buf.len() + i;

                // we limit UDP payload size to 502 bytes
                if newline_index - self.last_packet_start > 502 {
                  self.last_packet_start = self.last_newline;
                  self.packet_indexes.push(self.last_newline);
                }

                self.last_newline = newline_index;
            };

            self.buf.write(buf)
        }
    }

    fn flush(&mut self) -> io::Result<()> {
        self.flush_buf(true).and_then(|()| self.get_mut().flush())
    }
}

impl Drop for MetricsWriter {
    fn drop(&mut self) {
        if self.inner.is_some() && !self.panicked {
            // dtors should not panic, so we ignore a failed flush
            let _r = self.flush_buf(true);
        }
    }
}

<commit_msg>sendmmsg is linux specific<commit_after>use std::io::{self, Error, ErrorKind, Write};
use std::os::unix::io::AsRawFd;
use std::net::SocketAddr;
use mio::net::UdpSocket;
use libc::{msghdr, iovec, c_void, c_uint};

pub struct MetricSocket {
  pub addr:   SocketAddr,
  pub socket: UdpSocket,
}

impl Write for MetricSocket {
  fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
    self.socket.send_to(buf, &self.addr)
  }

  fn flush(&mut self) -> io::Result<()> {
    Ok(())
  }
}

#[cfg(not(target_os = "linux"))]
pub type MetricsWriter = sozu_command::writer::MultiLineWriter<MetricSocket>;

#[cfg(target_os = "linux")]
pub struct MetricsWriter {
    inner: Option<MetricSocket>,
    buf: Vec<u8>,
    last_newline: usize,
    last_packet_start: usize,
    panicked: bool,
    packet_indexes: Vec<usize>,
}

#[cfg(target_os = "linux")]
impl MetricsWriter {
    pub fn new(inner: MetricSocket) -> MetricsWriter {
        MetricsWriter::with_capacity(16384, inner)
    }

    pub fn with_capacity(capacity: usize, inner: MetricSocket) -> MetricsWriter {
        MetricsWriter {
            inner: Some(inner),
            buf: Vec::with_capacity(capacity),
            panicked: false,
            last_newline: 0,
            last_packet_start: 0,
            packet_indexes: vec![],
        }
    }

    pub fn get_ref(&self) -> &MetricSocket {
        self.inner.as_ref().unwrap()
    }

    pub fn get_mut(&mut self) -> &mut MetricSocket {
        self.inner.as_mut().unwrap()
    }

    fn flush_buf(&mut self, flush_entire_buffer: bool) -> io::Result<()> {
        let mut written = 0;
        let len = if flush_entire_buffer {
            self.buf.len()
        } else {
            self.last_newline + 1
        };

        let mut ret = Ok(());
        while written < len {
            self.panicked = true;

            let mut last_index = written;
            let mut iovs = self.packet_indexes.iter().map(|index| {
              let iov = iovec {
                iov_base: (self.buf.as_ptr() as usize + last_index) as *mut c_void,
                iov_len: index - last_index
              };
              /*println!("will add the packet ({} bytes): {}",
                *index -  last_index,
                std::str::from_utf8(&self.buf[last_index..*index]).unwrap());
              */
              //println!("will add the packet ({} bytes) with iov: {:?}", *index -  last_index, iov);

              last_index = *index;
              vec![iov]
            }).collect::<Vec<_>>();

            let mut messages = iovs.iter().map(|iov| {

              libc::mmsghdr {
                msg_hdr: msghdr {
                  msg_name: std::ptr::null_mut(),
                  msg_namelen: 0,
                  msg_iov: iov.as_ptr() as *mut iovec,
                  msg_iovlen: 1,
                  msg_control: std::ptr::null_mut(),
                  msg_controllen: 0,
                  msg_flags: 0,
                },
                msg_len: 0,
              }
            }).collect::<Vec<_>>();
            //println!("created {} packets", messages.len());

            if messages.is_empty() {
              break;
            }

            unsafe {
              let r = libc::sendmmsg(
                self.inner.as_ref().unwrap().socket.as_raw_fd(),
                &mut messages[0] as *mut libc::mmsghdr,
                messages.len() as c_uint,
                0,
              );
              self.panicked = false;

              match r {
                -1 => {
                  //println!("last error: {:?}", io::Error::last_os_error());
                  let e = io::Error::last_os_error();
                  if e.kind() != io::ErrorKind::Interrupted {
                    ret = Err(e);
                    break;
                  }
                },
                sent => {
                  //println!("sent: {:?}", sent);
                  /*for i in 0..messages.len() {
                    println!("message {} wrote {} bytes from {:?}",
                      i, messages[i].msg_len,
                      *messages[i].msg_hdr.msg_iov);
                  }*/

                  let mut currently_written = 0;
                  for i in 0..(sent as usize) {
                    currently_written += messages[i].msg_len as usize;
                  }
                  written += currently_written;
                  //println!("written (packet indexes: {:?}): {}, total {}", self.packet_indexes,
                  //  currently_written, written);
                  //println!("written {}, total {}", currently_written, written);

                  if currently_written == 0 {
                    ret = Err(Error::new(
                        ErrorKind::WriteZero,
                        "failed to write the buffered data",
                    ));
                    break;
                  }

                  if sent as usize == self.packet_indexes.len() {
                    self.packet_indexes.clear();
                    break;
                  } else {
                    for i in 0..(sent as usize) {
                      let _ = self.packet_indexes.remove(0);
                    }
                  }

                }
              }
            };
        }
        //println!("buf len {} last newline {}, written {}", self.buf.len(), self.last_newline, written);
        if written > 0 {
            //println!("FLUSHED: {}", ::std::str::from_utf8(&self.buf[..written]).unwrap());
            self.buf.drain(..written);
        }

        if flush_entire_buffer {
            self.last_newline = 0;
            self.last_packet_start = 0;
        } else if written > self.last_newline {
            self.last_newline = 0;
            //FIXME
            self.last_packet_start = 0;
        } else {
            self.last_newline -= written;
            //FIXME
            self.last_packet_start = 0;
        }

        ret
    }
}

#[cfg(target_os = "linux")]
impl Write for MetricsWriter {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        if self.buf.len() + buf.len() > self.buf.capacity() {
            self.flush_buf(false)?;
        }
        if buf.len() >= self.buf.capacity() {
            self.panicked = true;
            let r = self.get_mut().write(buf);
            self.panicked = false;
            r
        } else {
            if let Some(i) = memchr::memrchr(b'\n', buf) {
                let newline_index = self.buf.len() + i;

                // we limit UDP payload size to 502 bytes
                if newline_index - self.last_packet_start > 502 {
                  self.last_packet_start = self.last_newline;
                  self.packet_indexes.push(self.last_newline);
                }

                self.last_newline = newline_index;
            };

            self.buf.write(buf)
        }
    }

    fn flush(&mut self) -> io::Result<()> {
        self.flush_buf(true).and_then(|()| self.get_mut().flush())
    }
}

#[cfg(target_os = "linux")]
impl Drop for MetricsWriter {
    fn drop(&mut self) {
        if self.inner.is_some() && !self.panicked {
            // dtors should not panic, so we ignore a failed flush
            let _r = self.flush_buf(true);
        }
    }
}

<|endoftext|>


<commit_before>use std::str;

use hardware::memory_controller::MemoryController;
use hardware::cpu::Handler;
use std::fmt::Debug;
use std::fmt;

#[allow(dead_code)]
pub struct Cartridge {
    startup_graphic: Vec<u8>,
    game_title: String,
    gb_color_game: bool,
    licence_code: u16,
    super_game_boy: bool,
    memory_controller: MemoryController,
    rom_size: usize,
    ram_size: usize,
    destination_code_jp: bool,
    mask_rom_version_number: u8,
}

impl Debug for Cartridge {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(f, "Cartridge {{\n")?;
        write!(f, "\t game_title: {}\n", self.game_title)?;
        write!(f, "\t gb_color_game: {}\n", self.gb_color_game)?;
        write!(f, "\t licence_code: {}\n", self.licence_code)?;
        write!(f, "\t super_game_boy: {}\n", self.super_game_boy)?;
        write!(f, "\t rom_size: {}\n", self.rom_size)?;
        write!(f, "\t ram_size: {}\n", self.ram_size)?;
        write!(f, "}}\n")
    }
}

fn get_rom_size(byte: u8) -> usize {
    match byte {
        0x00 => 2,
        0x01 => 4,
        0x02 => 8,
        0x03 => 16,
        0x04 => 32,
        0x05 => 64,
        0x06 => 128,
        0x52 => 72,
        0x53 => 80,
        0x54 => 96,
        _ => unreachable!(),
    }
}

fn get_ram_size(byte: u8) -> usize {
    match byte {
        0x00 => 0,
        0x01 => 16,
        0x02 => 64,
        0x03 => 256,
        0x04 => 1024,
        _ => unreachable!(),
    }
}

impl Handler for Cartridge {
    fn read(&self, address: u16) -> u8 {
        self.memory_controller.read(address)
    }

    fn write(&mut self, address: u16, v: u8) {
        self.memory_controller.write(address, v);
    }
}

impl Cartridge {
    pub fn from_data(data: &[u8]) -> Cartridge {
        let mut copy = vec![0; data.len()];
        copy.clone_from_slice(data);

        Cartridge {
            startup_graphic: (&copy[0x104..0x133]).to_vec(),
            game_title: String::from_utf8(copy[0x134..0x142].to_vec()).unwrap(),
            gb_color_game: (copy[0x143] == 0x80),
            licence_code: ((copy[0x144] as u16) << 8) + (copy[0x145] as u16),
            super_game_boy: copy[0x146] == 0x03,
            rom_size: get_rom_size(copy[0x148]),
            ram_size: get_ram_size(copy[0x149]),
            destination_code_jp: copy[0x14A] == 0,
            mask_rom_version_number: copy[0x14C],
            memory_controller: MemoryController::from_bytes(copy),
        }
    }

    pub fn ram(&mut self) -> &mut [u8] {
        self.memory_controller.ram()
    }

    pub fn rtc(&mut self) -> Option<&mut i64> {
        self.memory_controller.rtc()
    }
}
<commit_msg>Remove unused import std::str.<commit_after>use hardware::memory_controller::MemoryController;
use hardware::cpu::Handler;
use std::fmt::Debug;
use std::fmt;

#[allow(dead_code)]
pub struct Cartridge {
    startup_graphic: Vec<u8>,
    game_title: String,
    gb_color_game: bool,
    licence_code: u16,
    super_game_boy: bool,
    memory_controller: MemoryController,
    rom_size: usize,
    ram_size: usize,
    destination_code_jp: bool,
    mask_rom_version_number: u8,
}

impl Debug for Cartridge {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(f, "Cartridge {{\n")?;
        write!(f, "\t game_title: {}\n", self.game_title)?;
        write!(f, "\t gb_color_game: {}\n", self.gb_color_game)?;
        write!(f, "\t licence_code: {}\n", self.licence_code)?;
        write!(f, "\t super_game_boy: {}\n", self.super_game_boy)?;
        write!(f, "\t rom_size: {}\n", self.rom_size)?;
        write!(f, "\t ram_size: {}\n", self.ram_size)?;
        write!(f, "}}\n")
    }
}

fn get_rom_size(byte: u8) -> usize {
    match byte {
        0x00 => 2,
        0x01 => 4,
        0x02 => 8,
        0x03 => 16,
        0x04 => 32,
        0x05 => 64,
        0x06 => 128,
        0x52 => 72,
        0x53 => 80,
        0x54 => 96,
        _ => unreachable!(),
    }
}

fn get_ram_size(byte: u8) -> usize {
    match byte {
        0x00 => 0,
        0x01 => 16,
        0x02 => 64,
        0x03 => 256,
        0x04 => 1024,
        _ => unreachable!(),
    }
}

impl Handler for Cartridge {
    fn read(&self, address: u16) -> u8 {
        self.memory_controller.read(address)
    }

    fn write(&mut self, address: u16, v: u8) {
        self.memory_controller.write(address, v);
    }
}

impl Cartridge {
    pub fn from_data(data: &[u8]) -> Cartridge {
        let mut copy = vec![0; data.len()];
        copy.clone_from_slice(data);

        Cartridge {
            startup_graphic: (&copy[0x104..0x133]).to_vec(),
            game_title: String::from_utf8(copy[0x134..0x142].to_vec()).unwrap(),
            gb_color_game: (copy[0x143] == 0x80),
            licence_code: ((copy[0x144] as u16) << 8) + (copy[0x145] as u16),
            super_game_boy: copy[0x146] == 0x03,
            rom_size: get_rom_size(copy[0x148]),
            ram_size: get_ram_size(copy[0x149]),
            destination_code_jp: copy[0x14A] == 0,
            mask_rom_version_number: copy[0x14C],
            memory_controller: MemoryController::from_bytes(copy),
        }
    }

    pub fn ram(&mut self) -> &mut [u8] {
        self.memory_controller.ram()
    }

    pub fn rtc(&mut self) -> Option<&mut i64> {
        self.memory_controller.rtc()
    }
}
<|endoftext|>


<commit_before>// Copyright 2016 Joe Wilm, The Alacritty Project Contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//! Alacritty - The GPU Enhanced Terminal
#![deny(clippy::all, clippy::if_not_else, clippy::enum_glob_use, clippy::wrong_pub_self_convention)]
#![cfg_attr(feature = "nightly", feature(core_intrinsics))]
#![cfg_attr(all(test, feature = "bench"), feature(test))]
// With the default subsystem, 'console', windows creates an additional console
// window for the program.
// This is silently ignored on non-windows systems.
// See https://msdn.microsoft.com/en-us/library/4cc7ya5b.aspx for more details.
#![windows_subsystem = "windows"]

#[cfg(target_os = "macos")]
use std::env;
use std::error::Error;
use std::fs;
use std::io::{self, Write};
use std::sync::Arc;

#[cfg(target_os = "macos")]
use dirs;
use glutin::event_loop::EventLoop as GlutinEventLoop;
use log::info;
#[cfg(windows)]
use winapi::um::wincon::{AttachConsole, FreeConsole, ATTACH_PARENT_PROCESS};

use alacritty_terminal::clipboard::Clipboard;
use alacritty_terminal::event::Event;
use alacritty_terminal::event_loop::{self, EventLoop, Msg};
#[cfg(target_os = "macos")]
use alacritty_terminal::locale;
use alacritty_terminal::message_bar::MessageBuffer;
use alacritty_terminal::panic;
use alacritty_terminal::sync::FairMutex;
use alacritty_terminal::term::Term;
use alacritty_terminal::tty;

mod cli;
mod config;
mod cursor;
mod display;
mod event;
mod input;
mod logging;
mod renderer;
mod url;
mod window;

mod gl {
    #![allow(clippy::all)]
    include!(concat!(env!("OUT_DIR"), "/gl_bindings.rs"));
}

use crate::cli::Options;
use crate::config::monitor::Monitor;
use crate::config::Config;
use crate::display::Display;
use crate::event::{EventProxy, Processor};

fn main() {
    panic::attach_handler();

    // When linked with the windows subsystem windows won't automatically attach
    // to the console of the parent process, so we do it explicitly. This fails
    // silently if the parent has no console.
    #[cfg(windows)]
    unsafe {
        AttachConsole(ATTACH_PARENT_PROCESS);
    }

    // Load command line options
    let options = Options::new();

    // Setup glutin event loop
    let window_event_loop = GlutinEventLoop::<Event>::with_user_event();

    // Initialize the logger as soon as possible as to capture output from other subsystems
    let log_file = logging::initialize(&options, window_event_loop.create_proxy())
        .expect("Unable to initialize logger");

    // Load configuration file
    let config_path = options.config_path().or_else(config::installed_config);
    let config = config_path.map(config::load_from).unwrap_or_else(Config::default);
    let config = options.into_config(config);

    // Update the log level from config
    log::set_max_level(config.debug.log_level);

    // Switch to home directory
    #[cfg(target_os = "macos")]
    env::set_current_dir(dirs::home_dir().unwrap()).unwrap();
    // Set locale
    #[cfg(target_os = "macos")]
    locale::set_locale_environment();

    // Store if log file should be deleted before moving config
    let persistent_logging = config.persistent_logging();

    // Run alacritty
    if let Err(err) = run(window_event_loop, config) {
        println!("Alacritty encountered an unrecoverable error:\n\n\t{}\n", err);
        std::process::exit(1);
    }

    // Clean up logfile
    if let Some(log_file) = log_file {
        if !persistent_logging && fs::remove_file(&log_file).is_ok() {
            let _ = writeln!(io::stdout(), "Deleted log file at \"{}\"", log_file.display());
        }
    }
}

/// Run Alacritty
///
/// Creates a window, the terminal state, pty, I/O event loop, input processor,
/// config change monitor, and runs the main display loop.
fn run(window_event_loop: GlutinEventLoop<Event>, config: Config) -> Result<(), Box<dyn Error>> {
    info!("Welcome to Alacritty");
    if let Some(config_path) = &config.config_path {
        info!("Configuration loaded from \"{}\"", config_path.display());
    };

    // Set environment variables
    tty::setup_env(&config);

    let event_proxy = EventProxy::new(window_event_loop.create_proxy());

    // Create a display
    //
    // The display manages a window and can draw the terminal.
    let display = Display::new(&config, &window_event_loop)?;

    info!("PTY Dimensions: {:?} x {:?}", display.size_info.lines(), display.size_info.cols());

    // Create new native clipboard
    #[cfg(not(any(target_os = "macos", windows)))]
    let clipboard = Clipboard::new(display.window.wayland_display());
    #[cfg(any(target_os = "macos", windows))]
    let clipboard = Clipboard::new();

    // Create the terminal
    //
    // This object contains all of the state about what's being displayed. It's
    // wrapped in a clonable mutex since both the I/O loop and display need to
    // access it.
    let terminal = Term::new(&config, &display.size_info, clipboard, event_proxy.clone());
    let terminal = Arc::new(FairMutex::new(terminal));

    // Create the pty
    //
    // The pty forks a process to run the shell on the slave side of the
    // pseudoterminal. A file descriptor for the master side is retained for
    // reading/writing to the shell.
    #[cfg(not(any(target_os = "macos", windows)))]
    let pty = tty::new(&config, &display.size_info, display.window.x11_window_id());
    #[cfg(any(target_os = "macos", windows))]
    let pty = tty::new(&config, &display.size_info, None);

    // Create the pseudoterminal I/O loop
    //
    // pty I/O is ran on another thread as to not occupy cycles used by the
    // renderer and input processing. Note that access to the terminal state is
    // synchronized since the I/O loop updates the state, and the display
    // consumes it periodically.
    let event_loop = EventLoop::new(Arc::clone(&terminal), event_proxy.clone(), pty, &config);

    // The event loop channel allows write requests from the event processor
    // to be sent to the pty loop and ultimately written to the pty.
    let loop_tx = event_loop.channel();

    // Create a config monitor when config was loaded from path
    //
    // The monitor watches the config file for changes and reloads it. Pending
    // config changes are processed in the main loop.
    if config.live_config_reload() {
        config.config_path.as_ref().map(|path| Monitor::new(path, event_proxy.clone()));
    }

    // Setup storage for message UI
    let message_buffer = MessageBuffer::new();

    // Event processor
    let mut processor =
        Processor::new(event_loop::Notifier(loop_tx.clone()), message_buffer, config, display);

    // Kick off the I/O thread
    let io_thread = event_loop.spawn();

    info!("Initialisation complete");

    // Start event loop and block until shutdown
    processor.run(terminal, window_event_loop);

    // This explicit drop is needed for Windows, ConPTY backend. Otherwise a deadlock can occur.
    // The cause:
    //   - Drop for Conpty will deadlock if the conout pipe has already been dropped.
    //   - The conout pipe is dropped when the io_thread is joined below (io_thread owns pty).
    //   - Conpty is dropped when the last of processor and io_thread are dropped, because both of
    //     them own an Arc<Conpty>.
    //
    // The fix is to ensure that processor is dropped first. That way, when io_thread (i.e. pty)
    // is dropped, it can ensure Conpty is dropped before the conout pipe in the pty drop order.
    //
    // FIXME: Change PTY API to enforce the correct drop order with the typesystem.
    drop(processor);

    // Shutdown PTY parser event loop
    loop_tx.send(Msg::Shutdown).expect("Error sending shutdown to pty event loop");
    io_thread.join().expect("join io thread");

    // FIXME patch notify library to have a shutdown method
    // config_reloader.join().ok();

    // Without explicitly detaching the console cmd won't redraw it's prompt
    #[cfg(windows)]
    unsafe {
        FreeConsole();
    }

    info!("Goodbye");

    Ok(())
}
<commit_msg>Add debug log for missing config<commit_after>// Copyright 2016 Joe Wilm, The Alacritty Project Contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//! Alacritty - The GPU Enhanced Terminal
#![deny(clippy::all, clippy::if_not_else, clippy::enum_glob_use, clippy::wrong_pub_self_convention)]
#![cfg_attr(feature = "nightly", feature(core_intrinsics))]
#![cfg_attr(all(test, feature = "bench"), feature(test))]
// With the default subsystem, 'console', windows creates an additional console
// window for the program.
// This is silently ignored on non-windows systems.
// See https://msdn.microsoft.com/en-us/library/4cc7ya5b.aspx for more details.
#![windows_subsystem = "windows"]

#[cfg(target_os = "macos")]
use std::env;
use std::error::Error;
use std::fs;
use std::io::{self, Write};
use std::sync::Arc;

#[cfg(target_os = "macos")]
use dirs;
use glutin::event_loop::EventLoop as GlutinEventLoop;
use log::info;
#[cfg(windows)]
use winapi::um::wincon::{AttachConsole, FreeConsole, ATTACH_PARENT_PROCESS};

use alacritty_terminal::clipboard::Clipboard;
use alacritty_terminal::event::Event;
use alacritty_terminal::event_loop::{self, EventLoop, Msg};
#[cfg(target_os = "macos")]
use alacritty_terminal::locale;
use alacritty_terminal::message_bar::MessageBuffer;
use alacritty_terminal::panic;
use alacritty_terminal::sync::FairMutex;
use alacritty_terminal::term::Term;
use alacritty_terminal::tty;

mod cli;
mod config;
mod cursor;
mod display;
mod event;
mod input;
mod logging;
mod renderer;
mod url;
mod window;

mod gl {
    #![allow(clippy::all)]
    include!(concat!(env!("OUT_DIR"), "/gl_bindings.rs"));
}

use crate::cli::Options;
use crate::config::monitor::Monitor;
use crate::config::Config;
use crate::display::Display;
use crate::event::{EventProxy, Processor};

fn main() {
    panic::attach_handler();

    // When linked with the windows subsystem windows won't automatically attach
    // to the console of the parent process, so we do it explicitly. This fails
    // silently if the parent has no console.
    #[cfg(windows)]
    unsafe {
        AttachConsole(ATTACH_PARENT_PROCESS);
    }

    // Load command line options
    let options = Options::new();

    // Setup glutin event loop
    let window_event_loop = GlutinEventLoop::<Event>::with_user_event();

    // Initialize the logger as soon as possible as to capture output from other subsystems
    let log_file = logging::initialize(&options, window_event_loop.create_proxy())
        .expect("Unable to initialize logger");

    // Load configuration file
    let config_path = options.config_path().or_else(config::installed_config);
    let config = config_path.map(config::load_from).unwrap_or_else(Config::default);
    let config = options.into_config(config);

    // Update the log level from config
    log::set_max_level(config.debug.log_level);

    // Switch to home directory
    #[cfg(target_os = "macos")]
    env::set_current_dir(dirs::home_dir().unwrap()).unwrap();
    // Set locale
    #[cfg(target_os = "macos")]
    locale::set_locale_environment();

    // Store if log file should be deleted before moving config
    let persistent_logging = config.persistent_logging();

    // Run alacritty
    if let Err(err) = run(window_event_loop, config) {
        println!("Alacritty encountered an unrecoverable error:\n\n\t{}\n", err);
        std::process::exit(1);
    }

    // Clean up logfile
    if let Some(log_file) = log_file {
        if !persistent_logging && fs::remove_file(&log_file).is_ok() {
            let _ = writeln!(io::stdout(), "Deleted log file at \"{}\"", log_file.display());
        }
    }
}

/// Run Alacritty
///
/// Creates a window, the terminal state, pty, I/O event loop, input processor,
/// config change monitor, and runs the main display loop.
fn run(window_event_loop: GlutinEventLoop<Event>, config: Config) -> Result<(), Box<dyn Error>> {
    info!("Welcome to Alacritty");

    match &config.config_path {
        Some(config_path) => info!("Configuration loaded from \"{}\"", config_path.display()),
        None => info!("No configuration file found"),
    }

    // Set environment variables
    tty::setup_env(&config);

    let event_proxy = EventProxy::new(window_event_loop.create_proxy());

    // Create a display
    //
    // The display manages a window and can draw the terminal.
    let display = Display::new(&config, &window_event_loop)?;

    info!("PTY Dimensions: {:?} x {:?}", display.size_info.lines(), display.size_info.cols());

    // Create new native clipboard
    #[cfg(not(any(target_os = "macos", windows)))]
    let clipboard = Clipboard::new(display.window.wayland_display());
    #[cfg(any(target_os = "macos", windows))]
    let clipboard = Clipboard::new();

    // Create the terminal
    //
    // This object contains all of the state about what's being displayed. It's
    // wrapped in a clonable mutex since both the I/O loop and display need to
    // access it.
    let terminal = Term::new(&config, &display.size_info, clipboard, event_proxy.clone());
    let terminal = Arc::new(FairMutex::new(terminal));

    // Create the pty
    //
    // The pty forks a process to run the shell on the slave side of the
    // pseudoterminal. A file descriptor for the master side is retained for
    // reading/writing to the shell.
    #[cfg(not(any(target_os = "macos", windows)))]
    let pty = tty::new(&config, &display.size_info, display.window.x11_window_id());
    #[cfg(any(target_os = "macos", windows))]
    let pty = tty::new(&config, &display.size_info, None);

    // Create the pseudoterminal I/O loop
    //
    // pty I/O is ran on another thread as to not occupy cycles used by the
    // renderer and input processing. Note that access to the terminal state is
    // synchronized since the I/O loop updates the state, and the display
    // consumes it periodically.
    let event_loop = EventLoop::new(Arc::clone(&terminal), event_proxy.clone(), pty, &config);

    // The event loop channel allows write requests from the event processor
    // to be sent to the pty loop and ultimately written to the pty.
    let loop_tx = event_loop.channel();

    // Create a config monitor when config was loaded from path
    //
    // The monitor watches the config file for changes and reloads it. Pending
    // config changes are processed in the main loop.
    if config.live_config_reload() {
        config.config_path.as_ref().map(|path| Monitor::new(path, event_proxy.clone()));
    }

    // Setup storage for message UI
    let message_buffer = MessageBuffer::new();

    // Event processor
    let mut processor =
        Processor::new(event_loop::Notifier(loop_tx.clone()), message_buffer, config, display);

    // Kick off the I/O thread
    let io_thread = event_loop.spawn();

    info!("Initialisation complete");

    // Start event loop and block until shutdown
    processor.run(terminal, window_event_loop);

    // This explicit drop is needed for Windows, ConPTY backend. Otherwise a deadlock can occur.
    // The cause:
    //   - Drop for Conpty will deadlock if the conout pipe has already been dropped.
    //   - The conout pipe is dropped when the io_thread is joined below (io_thread owns pty).
    //   - Conpty is dropped when the last of processor and io_thread are dropped, because both of
    //     them own an Arc<Conpty>.
    //
    // The fix is to ensure that processor is dropped first. That way, when io_thread (i.e. pty)
    // is dropped, it can ensure Conpty is dropped before the conout pipe in the pty drop order.
    //
    // FIXME: Change PTY API to enforce the correct drop order with the typesystem.
    drop(processor);

    // Shutdown PTY parser event loop
    loop_tx.send(Msg::Shutdown).expect("Error sending shutdown to pty event loop");
    io_thread.join().expect("join io thread");

    // FIXME patch notify library to have a shutdown method
    // config_reloader.join().ok();

    // Without explicitly detaching the console cmd won't redraw it's prompt
    #[cfg(windows)]
    unsafe {
        FreeConsole();
    }

    info!("Goodbye");

    Ok(())
}
<|endoftext|>


<commit_before>use std::io::{Error, ErrorKind};
use std::ops::Add;
use std::result::Result;

fn range_vec<T>(nl: &Vec<T>, i: usize, j: usize) -> Result<Vec<T>, Error>
where
    T: Copy + Clone,
{
    if i >= nl.len() {
        return Err(Error::new(
            ErrorKind::Other,
            "first index larger than length",
        ));
    }

    let mut jj;

    if j > nl.len() {
        jj = nl.len() - 1;
    } else {
        jj = j - 1;
    }

    let mut ind = 0;
    let mut result: Vec<T> = vec![];

    for this in nl {
        if ind >= i && ind <= jj {
            result.push(*this);
        }
        ind += 1;
    }

    Result::Ok(result)
}

fn sum_vec<T>(nl: &Vec<T>) -> T
where
    T: Add<Output = T> + Copy + Clone,
{
    let mut result: T = *nl.first().unwrap();
    if let Some((_, last)) = nl.split_first() {
        for this in last {
            result = result + (*this);
        }
    }
    result
}

/*
fn sum_range<T>(nl: &Vec<T>, i: usize, j: usize) -> Result<T, Error>
where
    T: Add + Copy + Clone,
{

}*/

fn main() {
    let a = vec![1, 2, 3, 4, 5, 6];
    println!("{:?}", range_vec(&a, 0, 1));
    println!("{:?}", range_vec(&a, 2, 6));
    println!("{:?}", range_vec(&a, 6, 6));
    println!("{:?}", range_vec(&a, 5, 6));
    //main thread cannot use `?`
    //println!("{:?}", range_vec(&a, 2, 6)?);

    println!("{:?}", sum_vec(&a));
}
<commit_msg>finish rust version<commit_after>use std::io::{Error, ErrorKind};
use std::ops::Add;
use std::result::Result;

fn range_vec<T>(nl: &Vec<T>, i: usize, j: usize) -> Result<Vec<T>, Error>
where
    T: Copy + Clone,
{
    if i >= nl.len() {
        return Result::Err(Error::new(
            ErrorKind::Other,
            "first index larger than length",
        ));
    }

    let mut jj;

    if j > nl.len() {
        jj = nl.len();
    } else {
        jj = j;
    }

    let mut ind = 0;
    let mut result: Vec<T> = vec![];

    for this in nl {
        if ind >= i && ind <= jj {
            result.push(*this);
        }
        ind += 1;
    }

    Result::Ok(result)
}

fn sum_vec<T>(nl: &Vec<T>) -> T
where
    T: Add<Output = T> + Copy + Clone,
{
    let mut result: T = *nl.first().unwrap();
    if let Some((_, last)) = nl.split_first() {
        for this in last {
            result = result + (*this);
        }
    }
    result
}

fn sum_range<T>(nl: &Vec<T>, i: usize, j: usize) -> Result<T, Error>
where
    T: Add<Output = T> + Copy + Clone,
{
    match range_vec(nl, i, j) {
        Result::Ok(temp_l) => return Result::Ok(sum_vec(&temp_l)),
        Result::Err(e) => return Result::Err(e),
    }
}

fn main() {
    let a = vec![-2, 0, 3, -5, 2, -1];
    println!("{:?}", range_vec(&a, 0, 1));
    println!("{:?}", range_vec(&a, 2, 6));
    println!("{:?}", range_vec(&a, 6, 6));
    println!("{:?}", range_vec(&a, 5, 6));
    //main thread cannot use `?`
    //println!("{:?}", range_vec(&a, 2, 6)?);

    println!("{:?}", sum_vec(&a));

    println!("{:?}", sum_range(&a, 0, 2));
    println!("{:?}", sum_range(&a, 2, 5));
    println!("{:?}", sum_range(&a, 0, 5));
}
<|endoftext|>


<commit_before>extern crate clap;
extern crate clippy_dev;
extern crate regex;

use clap::{App, Arg, SubCommand};
use clippy_dev::*;

mod fmt;
mod stderr_length_check;

#[derive(PartialEq)]
enum UpdateMode {
    Check,
    Change,
}

fn main() {
    let matches = App::new("Clippy developer tooling")
        .subcommand(
            SubCommand::with_name("fmt")
                .about("Run rustfmt on all projects and tests")
                .arg(
                    Arg::with_name("check")
                        .long("check")
                        .help("Use the rustfmt --check option"),
                )
                .arg(
                    Arg::with_name("verbose")
                        .short("v")
                        .long("verbose")
                        .help("Echo commands run"),
                ),
        )
        .subcommand(
            SubCommand::with_name("update_lints")
                .about("Updates lint registration and information from the source code")
                .long_about(
                    "Makes sure that:\n \
                     * the lint count in README.md is correct\n \
                     * the changelog contains markdown link references at the bottom\n \
                     * all lint groups include the correct lints\n \
                     * lint modules in `clippy_lints/*` are visible in `src/lib.rs` via `pub mod`\n \
                     * all lints are registered in the lint store",
                )
                .arg(Arg::with_name("print-only").long("print-only").help(
                    "Print a table of lints to STDOUT. \
                     This does not include deprecated and internal lints. \
                     (Does not modify any files)",
                ))
                .arg(
                    Arg::with_name("check")
                        .long("check")
                        .help("Checks that util/dev update_lints has been run. Used on CI."),
                ),
        )
        .arg(
            Arg::with_name("limit-stderr-length")
                .long("limit-stderr-length")
                .help("Ensures that stderr files do not grow longer than a certain amount of lines."),
        )
        .get_matches();

    if matches.is_present("limit-stderr-length") {
        stderr_length_check::check();
    }

    match matches.subcommand() {
        ("fmt", Some(matches)) => {
            fmt::run(matches.is_present("check"), matches.is_present("verbose"));
        },
        ("update_lints", Some(matches)) => {
            if matches.is_present("print-only") {
                print_lints();
            } else if matches.is_present("check") {
                update_lints(&UpdateMode::Check);
            } else {
                update_lints(&UpdateMode::Change);
            }
        },
        _ => unreachable!(),
    }
}

fn print_lints() {
    let lint_list = gather_all();
    let usable_lints: Vec<Lint> = Lint::usable_lints(lint_list).collect();
    let lint_count = usable_lints.len();
    let grouped_by_lint_group = Lint::by_lint_group(&usable_lints);

    for (lint_group, mut lints) in grouped_by_lint_group {
        if lint_group == "Deprecated" {
            continue;
        }
        println!("\n## {}", lint_group);

        lints.sort_by_key(|l| l.name.clone());

        for lint in lints {
            println!(
                "* [{}]({}#{}) ({})",
                lint.name,
                clippy_dev::DOCS_LINK.clone(),
                lint.name,
                lint.desc
            );
        }
    }

    println!("there are {} lints", lint_count);
}

fn update_lints(update_mode: &UpdateMode) {
    let lint_list: Vec<Lint> = gather_all().collect();

    let usable_lints: Vec<Lint> = Lint::usable_lints(lint_list.clone().into_iter()).collect();
    let lint_count = usable_lints.len();

    let mut sorted_usable_lints = usable_lints.clone();
    sorted_usable_lints.sort_by_key(|lint| lint.name.clone());

    let mut file_change = replace_region_in_file(
        "../src/lintlist/mod.rs",
        "begin lint list",
        "end lint list",
        false,
        update_mode == &UpdateMode::Change,
        || {
            format!(
                "pub const ALL_LINTS: [Lint; {}] = {:#?};",
                sorted_usable_lints.len(),
                sorted_usable_lints
            )
            .lines()
            .map(ToString::to_string)
            .collect::<Vec<_>>()
        },
    )
    .changed;

    file_change |= replace_region_in_file(
        "../README.md",
        r#"\[There are \d+ lints included in this crate!\]\(https://rust-lang.github.io/rust-clippy/master/index.html\)"#,
        "",
        true,
        update_mode == &UpdateMode::Change,
        || {
            vec![
                format!("[There are {} lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)", lint_count)
            ]
        }
    ).changed;

    file_change |= replace_region_in_file(
        "../CHANGELOG.md",
        "<!-- begin autogenerated links to lint list -->",
        "<!-- end autogenerated links to lint list -->",
        false,
        update_mode == &UpdateMode::Change,
        || gen_changelog_lint_list(lint_list.clone()),
    )
    .changed;

    file_change |= replace_region_in_file(
        "../clippy_lints/src/lib.rs",
        "begin deprecated lints",
        "end deprecated lints",
        false,
        update_mode == &UpdateMode::Change,
        || gen_deprecated(&lint_list),
    )
    .changed;

    file_change |= replace_region_in_file(
        "../clippy_lints/src/lib.rs",
        "begin lints modules",
        "end lints modules",
        false,
        update_mode == &UpdateMode::Change,
        || gen_modules_list(lint_list.clone()),
    )
    .changed;

    // Generate lists of lints in the clippy::all lint group
    file_change |= replace_region_in_file(
        "../clippy_lints/src/lib.rs",
        r#"reg.register_lint_group\("clippy::all""#,
        r#"\]\);"#,
        false,
        update_mode == &UpdateMode::Change,
        || {
            // clippy::all should only include the following lint groups:
            let all_group_lints = usable_lints
                .clone()
                .into_iter()
                .filter(|l| {
                    l.group == "correctness" || l.group == "style" || l.group == "complexity" || l.group == "perf"
                })
                .collect();

            gen_lint_group_list(all_group_lints)
        },
    )
    .changed;

    // Generate the list of lints for all other lint groups
    for (lint_group, lints) in Lint::by_lint_group(&usable_lints) {
        file_change |= replace_region_in_file(
            "../clippy_lints/src/lib.rs",
            &format!("reg.register_lint_group\\(\"clippy::{}\"", lint_group),
            r#"\]\);"#,
            false,
            update_mode == &UpdateMode::Change,
            || gen_lint_group_list(lints.clone()),
        )
        .changed;
    }

    if update_mode == &UpdateMode::Check && file_change {
        println!(
            "Not all lints defined properly. \
             Please run `util/dev update_lints` to make sure all lints are defined properly."
        );
        std::process::exit(1);
    }
}
<commit_msg>Fix crash on `dev --limit-stderr-length`<commit_after>extern crate clap;
extern crate clippy_dev;
extern crate regex;

use clap::{App, Arg, SubCommand};
use clippy_dev::*;

mod fmt;
mod stderr_length_check;

#[derive(PartialEq)]
enum UpdateMode {
    Check,
    Change,
}

fn main() {
    let matches = App::new("Clippy developer tooling")
        .subcommand(
            SubCommand::with_name("fmt")
                .about("Run rustfmt on all projects and tests")
                .arg(
                    Arg::with_name("check")
                        .long("check")
                        .help("Use the rustfmt --check option"),
                )
                .arg(
                    Arg::with_name("verbose")
                        .short("v")
                        .long("verbose")
                        .help("Echo commands run"),
                ),
        )
        .subcommand(
            SubCommand::with_name("update_lints")
                .about("Updates lint registration and information from the source code")
                .long_about(
                    "Makes sure that:\n \
                     * the lint count in README.md is correct\n \
                     * the changelog contains markdown link references at the bottom\n \
                     * all lint groups include the correct lints\n \
                     * lint modules in `clippy_lints/*` are visible in `src/lib.rs` via `pub mod`\n \
                     * all lints are registered in the lint store",
                )
                .arg(Arg::with_name("print-only").long("print-only").help(
                    "Print a table of lints to STDOUT. \
                     This does not include deprecated and internal lints. \
                     (Does not modify any files)",
                ))
                .arg(
                    Arg::with_name("check")
                        .long("check")
                        .help("Checks that util/dev update_lints has been run. Used on CI."),
                ),
        )
        .arg(
            Arg::with_name("limit-stderr-length")
                .long("limit-stderr-length")
                .help("Ensures that stderr files do not grow longer than a certain amount of lines."),
        )
        .get_matches();

    if matches.is_present("limit-stderr-length") {
        stderr_length_check::check();
    }

    match matches.subcommand() {
        ("fmt", Some(matches)) => {
            fmt::run(matches.is_present("check"), matches.is_present("verbose"));
        },
        ("update_lints", Some(matches)) => {
            if matches.is_present("print-only") {
                print_lints();
            } else if matches.is_present("check") {
                update_lints(&UpdateMode::Check);
            } else {
                update_lints(&UpdateMode::Change);
            }
        },
        _ => {},
    }
}

fn print_lints() {
    let lint_list = gather_all();
    let usable_lints: Vec<Lint> = Lint::usable_lints(lint_list).collect();
    let lint_count = usable_lints.len();
    let grouped_by_lint_group = Lint::by_lint_group(&usable_lints);

    for (lint_group, mut lints) in grouped_by_lint_group {
        if lint_group == "Deprecated" {
            continue;
        }
        println!("\n## {}", lint_group);

        lints.sort_by_key(|l| l.name.clone());

        for lint in lints {
            println!(
                "* [{}]({}#{}) ({})",
                lint.name,
                clippy_dev::DOCS_LINK.clone(),
                lint.name,
                lint.desc
            );
        }
    }

    println!("there are {} lints", lint_count);
}

fn update_lints(update_mode: &UpdateMode) {
    let lint_list: Vec<Lint> = gather_all().collect();

    let usable_lints: Vec<Lint> = Lint::usable_lints(lint_list.clone().into_iter()).collect();
    let lint_count = usable_lints.len();

    let mut sorted_usable_lints = usable_lints.clone();
    sorted_usable_lints.sort_by_key(|lint| lint.name.clone());

    let mut file_change = replace_region_in_file(
        "../src/lintlist/mod.rs",
        "begin lint list",
        "end lint list",
        false,
        update_mode == &UpdateMode::Change,
        || {
            format!(
                "pub const ALL_LINTS: [Lint; {}] = {:#?};",
                sorted_usable_lints.len(),
                sorted_usable_lints
            )
            .lines()
            .map(ToString::to_string)
            .collect::<Vec<_>>()
        },
    )
    .changed;

    file_change |= replace_region_in_file(
        "../README.md",
        r#"\[There are \d+ lints included in this crate!\]\(https://rust-lang.github.io/rust-clippy/master/index.html\)"#,
        "",
        true,
        update_mode == &UpdateMode::Change,
        || {
            vec![
                format!("[There are {} lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)", lint_count)
            ]
        }
    ).changed;

    file_change |= replace_region_in_file(
        "../CHANGELOG.md",
        "<!-- begin autogenerated links to lint list -->",
        "<!-- end autogenerated links to lint list -->",
        false,
        update_mode == &UpdateMode::Change,
        || gen_changelog_lint_list(lint_list.clone()),
    )
    .changed;

    file_change |= replace_region_in_file(
        "../clippy_lints/src/lib.rs",
        "begin deprecated lints",
        "end deprecated lints",
        false,
        update_mode == &UpdateMode::Change,
        || gen_deprecated(&lint_list),
    )
    .changed;

    file_change |= replace_region_in_file(
        "../clippy_lints/src/lib.rs",
        "begin lints modules",
        "end lints modules",
        false,
        update_mode == &UpdateMode::Change,
        || gen_modules_list(lint_list.clone()),
    )
    .changed;

    // Generate lists of lints in the clippy::all lint group
    file_change |= replace_region_in_file(
        "../clippy_lints/src/lib.rs",
        r#"reg.register_lint_group\("clippy::all""#,
        r#"\]\);"#,
        false,
        update_mode == &UpdateMode::Change,
        || {
            // clippy::all should only include the following lint groups:
            let all_group_lints = usable_lints
                .clone()
                .into_iter()
                .filter(|l| {
                    l.group == "correctness" || l.group == "style" || l.group == "complexity" || l.group == "perf"
                })
                .collect();

            gen_lint_group_list(all_group_lints)
        },
    )
    .changed;

    // Generate the list of lints for all other lint groups
    for (lint_group, lints) in Lint::by_lint_group(&usable_lints) {
        file_change |= replace_region_in_file(
            "../clippy_lints/src/lib.rs",
            &format!("reg.register_lint_group\\(\"clippy::{}\"", lint_group),
            r#"\]\);"#,
            false,
            update_mode == &UpdateMode::Change,
            || gen_lint_group_list(lints.clone()),
        )
        .changed;
    }

    if update_mode == &UpdateMode::Check && file_change {
        println!(
            "Not all lints defined properly. \
             Please run `util/dev update_lints` to make sure all lints are defined properly."
        );
        std::process::exit(1);
    }
}
<|endoftext|>