<commit_before>extern crate gimli;

use gimli::{AttributeValue, DebugAbbrev, DebugAranges, DebugInfo, DebugLine, DebugPubNames,
            DebugPubTypes, DebugRanges, DebugStr, LittleEndian};
use std::env;
use std::fs::File;
use std::io::Read;
use std::path::PathBuf;

fn read_section(section: &str) -> Vec<u8> {
    let mut path = PathBuf::new();
    if let Ok(dir) = env::var("CARGO_MANIFEST_DIR") {
        path.push(dir);
    }
    path.push("fixtures/self");
    path.push(section);

    println!("Reading section \"{}\" at path {:?}", section, path);
    assert!(path.is_file());
    let mut file = File::open(path).unwrap();

    let mut buf = Vec::new();
    file.read_to_end(&mut buf).unwrap();
    buf
}

#[test]
fn test_parse_self_debug_info() {
    let debug_info = read_section("debug_info");
    let debug_info = DebugInfo::<LittleEndian>::new(&debug_info);

    let debug_abbrev = read_section("debug_abbrev");
    let debug_abbrev = DebugAbbrev::<LittleEndian>::new(&debug_abbrev);

    let mut iter = debug_info.units();
    while let Some(unit) = iter.next().expect("Should parse compilation unit") {
        let abbrevs = unit.abbreviations(debug_abbrev)
            .expect("Should parse abbreviations");

        let mut cursor = unit.entries(&abbrevs);

        while cursor.next_dfs().expect("Should parse next dfs").is_some() {
            let entry = cursor.current().expect("Should have a current entry");

            let mut attrs = entry.attrs();
            while let Some(_) = attrs.next().expect("Should parse entry's attribute") {
            }
        }
    }
}

#[test]
fn test_parse_self_debug_line() {
    let debug_info = read_section("debug_info");
    let debug_info = DebugInfo::<LittleEndian>::new(&debug_info);

    let debug_abbrev = read_section("debug_abbrev");
    let debug_abbrev = DebugAbbrev::<LittleEndian>::new(&debug_abbrev);

    let debug_line = read_section("debug_line");
    let debug_line = DebugLine::<LittleEndian>::new(&debug_line);

    let debug_str = read_section("debug_str");
    let debug_str = DebugStr::<LittleEndian>::new(&debug_str);

    let mut iter = debug_info.units();
    while let Some(unit) = iter.next().expect("Should parse compilation unit") {
        let abbrevs = unit.abbreviations(debug_abbrev)
            .expect("Should parse abbreviations");

        let mut cursor = unit.entries(&abbrevs);
        cursor.next_dfs().expect("Should parse next dfs");

        let unit_entry = cursor.current()
            .expect("Should have a root entry");

        let comp_dir = unit_entry.attr(gimli::DW_AT_comp_dir)
            .expect("Should parse comp_dir attribute")
            .and_then(|attr| attr.string_value(&debug_str));
        let comp_name = unit_entry.attr(gimli::DW_AT_name)
            .expect("Should parse name attribute")
            .and_then(|attr| attr.string_value(&debug_str));

        if let Some(AttributeValue::DebugLineRef(offset)) =
            unit_entry.attr_value(gimli::DW_AT_stmt_list).expect("Should parse stmt_list") {
            let program = debug_line.program(offset, unit.address_size(), comp_dir, comp_name)
                .expect("should parse line number program header");

            let mut results = Vec::new();
            let mut rows = program.rows();
            while let Some((_, row)) = rows.next_row()
                .expect("Should parse and execute all rows in the line number program") {
                results.push(*row);
            }
            results.reverse();

            let program = debug_line.program(offset, unit.address_size(), comp_dir, comp_name)
                .expect("should parse line number program header");
            let (program, sequences) = program.sequences()
                .expect("should parse and execute the entire line number program");
            assert!(!sequences.is_empty()); // Should be at least one sequence.
            for sequence in sequences {
                let mut rows = program.resume_from(&sequence);
                while let Some((_, row)) = rows.next_row()
                    .expect("Should parse and execute all rows after resuming") {
                    let other_row = results.pop().unwrap();
                    assert!(row.address() >= sequence.start);
                    assert!(row.address() <= sequence.end);
                    assert_eq!(row.address(), other_row.address());
                    assert_eq!(row.line(), other_row.line());
                }
            }
            assert!(results.is_empty());
        }
    }
}

#[test]
fn test_parse_self_debug_ranges() {
    let debug_info = read_section("debug_info");
    let debug_info = DebugInfo::<LittleEndian>::new(&debug_info);

    let debug_abbrev = read_section("debug_abbrev");
    let debug_abbrev = DebugAbbrev::<LittleEndian>::new(&debug_abbrev);

    let debug_ranges = read_section("debug_ranges");
    let debug_ranges = DebugRanges::<LittleEndian>::new(&debug_ranges);

    let mut iter = debug_info.units();
    while let Some(unit) = iter.next().expect("Should parse compilation unit") {
        let abbrevs = unit.abbreviations(debug_abbrev)
            .expect("Should parse abbreviations");

        let mut cursor = unit.entries(&abbrevs);
        cursor.next_dfs().expect("Should parse next dfs");

        let mut low_pc = 0;

        {
            let unit_entry = cursor.current().expect("Should have a root entry");
            let low_pc_attr = unit_entry
                .attr_value(gimli::DW_AT_low_pc)
                .expect("Should parse low_pc");
            if let Some(gimli::AttributeValue::Addr(address)) = low_pc_attr {
                low_pc = address;
            }
        }

        while cursor.next_dfs().expect("Should parse next dfs").is_some() {
            let entry = cursor.current().expect("Should have a current entry");
            let mut attrs = entry.attrs();
            while let Some(attr) = attrs.next().expect("Should parse entry's attribute") {
                if let AttributeValue::DebugRangesRef(offset) = attr.value() {
                    let mut ranges = debug_ranges
                        .ranges(offset, unit.address_size(), low_pc)
                        .expect("Should parse ranges OK");
                    while let Some(range) = ranges.next().expect("Should parse next range") {
                        assert!(range.begin <= range.end);
                    }
                }
            }
        }
    }
}

#[test]
fn test_parse_self_debug_aranges() {
    let debug_aranges = read_section("debug_aranges");
    let debug_aranges = DebugAranges::<LittleEndian>::new(&debug_aranges);

    let mut aranges = debug_aranges.items();
    while let Some(_) = aranges.next().expect("Should parse arange OK") {
        // Not really anything else we can check right now.
    }
}

#[test]
fn test_parse_self_debug_pubnames() {
    let debug_pubnames = read_section("debug_pubnames");
    let debug_pubnames = DebugPubNames::<LittleEndian>::new(&debug_pubnames);

    let mut pubnames = debug_pubnames.items();
    while let Some(_) = pubnames.next().expect("Should parse pubname OK") {
        // Not really anything else we can check right now.
    }
}

#[test]
fn test_parse_self_debug_pubtypes() {
    let debug_pubtypes = read_section("debug_pubtypes");
    let debug_pubtypes = DebugPubTypes::<LittleEndian>::new(&debug_pubtypes);

    let mut pubtypes = debug_pubtypes.items();
    while let Some(_) = pubtypes.next().expect("Should parse pubtype OK") {
        // Not really anything else we can check right now.
    }
}

// Because `.eh_frame` doesn't contain address sizes, we need to assume the
// native word size, so this test is only valid on 64-bit machines (as the
// `.eh_frame` fixture data was created on a 64-bit machine).
#[cfg(target_pointer_width="64")]
#[test]
fn test_parse_self_eh_frame() {
    use gimli::{BaseAddresses, CieOrFde, EhFrame, UnwindSection};

    let eh_frame = read_section("eh_frame");
    let eh_frame = EhFrame::<LittleEndian>::new(&eh_frame);

    let bases = BaseAddresses::default()
        .set_cfi(0)
        .set_data(0)
        .set_text(0);
    let mut entries = eh_frame.entries(&bases);
    while let Some(entry) = entries.next().expect("Should parse CFI entry OK") {
        match entry {
            CieOrFde::Cie(cie) => {
                let mut instrs = cie.instructions();
                while let Some(_) = instrs.next().expect("Can parse next CFI instruction OK") {
                    // TODO FITZGEN
                }
            }
            CieOrFde::Fde(partial) => {
                let fde = partial.parse(|offset| eh_frame.cie_from_offset(&bases, offset))
                    .expect("Should be able to get CIE for FDE");

                let mut instrs = fde.instructions();
                while let Some(_) = instrs.next().expect("Can parse next CFI instruction OK") {
                    // TODO FITZGEN
                }
            }
        }
    }
}
<commit_msg>Add self-parse test for .debug_loc<commit_after>extern crate gimli;

use gimli::{AttributeValue, DebugAbbrev, DebugAranges, DebugInfo, DebugLine, DebugLoc,
            DebugPubNames, DebugPubTypes, DebugRanges, DebugStr, LittleEndian};
use std::env;
use std::fs::File;
use std::io::Read;
use std::path::PathBuf;

fn read_section(section: &str) -> Vec<u8> {
    let mut path = PathBuf::new();
    if let Ok(dir) = env::var("CARGO_MANIFEST_DIR") {
        path.push(dir);
    }
    path.push("fixtures/self");
    path.push(section);

    println!("Reading section \"{}\" at path {:?}", section, path);
    assert!(path.is_file());
    let mut file = File::open(path).unwrap();

    let mut buf = Vec::new();
    file.read_to_end(&mut buf).unwrap();
    buf
}

#[test]
fn test_parse_self_debug_info() {
    let debug_info = read_section("debug_info");
    let debug_info = DebugInfo::<LittleEndian>::new(&debug_info);

    let debug_abbrev = read_section("debug_abbrev");
    let debug_abbrev = DebugAbbrev::<LittleEndian>::new(&debug_abbrev);

    let mut iter = debug_info.units();
    while let Some(unit) = iter.next().expect("Should parse compilation unit") {
        let abbrevs = unit.abbreviations(debug_abbrev)
            .expect("Should parse abbreviations");

        let mut cursor = unit.entries(&abbrevs);

        while cursor.next_dfs().expect("Should parse next dfs").is_some() {
            let entry = cursor.current().expect("Should have a current entry");

            let mut attrs = entry.attrs();
            while let Some(_) = attrs.next().expect("Should parse entry's attribute") {
            }
        }
    }
}

#[test]
fn test_parse_self_debug_line() {
    let debug_info = read_section("debug_info");
    let debug_info = DebugInfo::<LittleEndian>::new(&debug_info);

    let debug_abbrev = read_section("debug_abbrev");
    let debug_abbrev = DebugAbbrev::<LittleEndian>::new(&debug_abbrev);

    let debug_line = read_section("debug_line");
    let debug_line = DebugLine::<LittleEndian>::new(&debug_line);

    let debug_str = read_section("debug_str");
    let debug_str = DebugStr::<LittleEndian>::new(&debug_str);

    let mut iter = debug_info.units();
    while let Some(unit) = iter.next().expect("Should parse compilation unit") {
        let abbrevs = unit.abbreviations(debug_abbrev)
            .expect("Should parse abbreviations");

        let mut cursor = unit.entries(&abbrevs);
        cursor.next_dfs().expect("Should parse next dfs");

        let unit_entry = cursor.current()
            .expect("Should have a root entry");

        let comp_dir = unit_entry.attr(gimli::DW_AT_comp_dir)
            .expect("Should parse comp_dir attribute")
            .and_then(|attr| attr.string_value(&debug_str));
        let comp_name = unit_entry.attr(gimli::DW_AT_name)
            .expect("Should parse name attribute")
            .and_then(|attr| attr.string_value(&debug_str));

        if let Some(AttributeValue::DebugLineRef(offset)) =
            unit_entry.attr_value(gimli::DW_AT_stmt_list).expect("Should parse stmt_list") {
            let program = debug_line.program(offset, unit.address_size(), comp_dir, comp_name)
                .expect("should parse line number program header");

            let mut results = Vec::new();
            let mut rows = program.rows();
            while let Some((_, row)) = rows.next_row()
                .expect("Should parse and execute all rows in the line number program") {
                results.push(*row);
            }
            results.reverse();

            let program = debug_line.program(offset, unit.address_size(), comp_dir, comp_name)
                .expect("should parse line number program header");
            let (program, sequences) = program.sequences()
                .expect("should parse and execute the entire line number program");
            assert!(!sequences.is_empty()); // Should be at least one sequence.
            for sequence in sequences {
                let mut rows = program.resume_from(&sequence);
                while let Some((_, row)) = rows.next_row()
                    .expect("Should parse and execute all rows after resuming") {
                    let other_row = results.pop().unwrap();
                    assert!(row.address() >= sequence.start);
                    assert!(row.address() <= sequence.end);
                    assert_eq!(row.address(), other_row.address());
                    assert_eq!(row.line(), other_row.line());
                }
            }
            assert!(results.is_empty());
        }
    }
}

#[test]
fn test_parse_self_debug_loc() {
    let debug_info = read_section("debug_info");
    let debug_info = DebugInfo::<LittleEndian>::new(&debug_info);

    let debug_abbrev = read_section("debug_abbrev");
    let debug_abbrev = DebugAbbrev::<LittleEndian>::new(&debug_abbrev);

    let debug_loc = read_section("debug_loc");
    let debug_loc = DebugLoc::<LittleEndian>::new(&debug_loc);

    let mut iter = debug_info.units();
    while let Some(unit) = iter.next().expect("Should parse compilation unit") {
        let abbrevs = unit.abbreviations(debug_abbrev)
            .expect("Should parse abbreviations");

        let mut cursor = unit.entries(&abbrevs);
        cursor.next_dfs().expect("Should parse next dfs");

        let mut low_pc = 0;

        {
            let unit_entry = cursor.current().expect("Should have a root entry");
            let low_pc_attr = unit_entry
                .attr_value(gimli::DW_AT_low_pc)
                .expect("Should parse low_pc");
            if let Some(gimli::AttributeValue::Addr(address)) = low_pc_attr {
                low_pc = address;
            }
        }

        while cursor.next_dfs().expect("Should parse next dfs").is_some() {
            let entry = cursor.current().expect("Should have a current entry");
            let mut attrs = entry.attrs();
            while let Some(attr) = attrs.next().expect("Should parse entry's attribute") {
                if let AttributeValue::DebugLocRef(offset) = attr.value() {
                    let mut locs = debug_loc
                        .locations(offset, unit.address_size(), low_pc)
                        .expect("Should parse locations OK");
                    while let Some(loc) = locs.next().expect("Should parse next location") {
                        assert!(loc.range.begin <= loc.range.end);
                    }
                }
            }
        }
    }
}

#[test]
fn test_parse_self_debug_ranges() {
    let debug_info = read_section("debug_info");
    let debug_info = DebugInfo::<LittleEndian>::new(&debug_info);

    let debug_abbrev = read_section("debug_abbrev");
    let debug_abbrev = DebugAbbrev::<LittleEndian>::new(&debug_abbrev);

    let debug_ranges = read_section("debug_ranges");
    let debug_ranges = DebugRanges::<LittleEndian>::new(&debug_ranges);

    let mut iter = debug_info.units();
    while let Some(unit) = iter.next().expect("Should parse compilation unit") {
        let abbrevs = unit.abbreviations(debug_abbrev)
            .expect("Should parse abbreviations");

        let mut cursor = unit.entries(&abbrevs);
        cursor.next_dfs().expect("Should parse next dfs");

        let mut low_pc = 0;

        {
            let unit_entry = cursor.current().expect("Should have a root entry");
            let low_pc_attr = unit_entry
                .attr_value(gimli::DW_AT_low_pc)
                .expect("Should parse low_pc");
            if let Some(gimli::AttributeValue::Addr(address)) = low_pc_attr {
                low_pc = address;
            }
        }

        while cursor.next_dfs().expect("Should parse next dfs").is_some() {
            let entry = cursor.current().expect("Should have a current entry");
            let mut attrs = entry.attrs();
            while let Some(attr) = attrs.next().expect("Should parse entry's attribute") {
                if let AttributeValue::DebugRangesRef(offset) = attr.value() {
                    let mut ranges = debug_ranges
                        .ranges(offset, unit.address_size(), low_pc)
                        .expect("Should parse ranges OK");
                    while let Some(range) = ranges.next().expect("Should parse next range") {
                        assert!(range.begin <= range.end);
                    }
                }
            }
        }
    }
}

#[test]
fn test_parse_self_debug_aranges() {
    let debug_aranges = read_section("debug_aranges");
    let debug_aranges = DebugAranges::<LittleEndian>::new(&debug_aranges);

    let mut aranges = debug_aranges.items();
    while let Some(_) = aranges.next().expect("Should parse arange OK") {
        // Not really anything else we can check right now.
    }
}

#[test]
fn test_parse_self_debug_pubnames() {
    let debug_pubnames = read_section("debug_pubnames");
    let debug_pubnames = DebugPubNames::<LittleEndian>::new(&debug_pubnames);

    let mut pubnames = debug_pubnames.items();
    while let Some(_) = pubnames.next().expect("Should parse pubname OK") {
        // Not really anything else we can check right now.
    }
}

#[test]
fn test_parse_self_debug_pubtypes() {
    let debug_pubtypes = read_section("debug_pubtypes");
    let debug_pubtypes = DebugPubTypes::<LittleEndian>::new(&debug_pubtypes);

    let mut pubtypes = debug_pubtypes.items();
    while let Some(_) = pubtypes.next().expect("Should parse pubtype OK") {
        // Not really anything else we can check right now.
    }
}

// Because `.eh_frame` doesn't contain address sizes, we need to assume the
// native word size, so this test is only valid on 64-bit machines (as the
// `.eh_frame` fixture data was created on a 64-bit machine).
#[cfg(target_pointer_width="64")]
#[test]
fn test_parse_self_eh_frame() {
    use gimli::{BaseAddresses, CieOrFde, EhFrame, UnwindSection};

    let eh_frame = read_section("eh_frame");
    let eh_frame = EhFrame::<LittleEndian>::new(&eh_frame);

    let bases = BaseAddresses::default()
        .set_cfi(0)
        .set_data(0)
        .set_text(0);
    let mut entries = eh_frame.entries(&bases);
    while let Some(entry) = entries.next().expect("Should parse CFI entry OK") {
        match entry {
            CieOrFde::Cie(cie) => {
                let mut instrs = cie.instructions();
                while let Some(_) = instrs.next().expect("Can parse next CFI instruction OK") {
                    // TODO FITZGEN
                }
            }
            CieOrFde::Fde(partial) => {
                let fde = partial.parse(|offset| eh_frame.cie_from_offset(&bases, offset))
                    .expect("Should be able to get CIE for FDE");

                let mut instrs = fde.instructions();
                while let Some(_) = instrs.next().expect("Can parse next CFI instruction OK") {
                    // TODO FITZGEN
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>extern crate tiny_http;

use tiny_http::{Server, Response};
use std::net::{TcpStream, Shutdown};
use std::io::{copy, Write, Read};
use std::thread::{spawn, sleep};
use std::time::Duration;
use std::sync::mpsc::channel;
use std::sync::Arc;
use std::ops::Deref;

/// Stream that produces bytes very slowly
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
struct SlowByteSrc { val: u8, len: usize }
impl<'b> Read for SlowByteSrc {
    fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
        sleep(Duration::from_millis(100));
        let l = self.len.min(buf.len()).min(1000);
        buf[..l].fill(self.val);
        self.len -= l;
        Ok(l)
    }
}

/// crude impl of http `Transfer-Encoding: chunked`
fn encode_chunked(data: &mut dyn Read, output: &mut dyn Write) {
    let mut buf = [0u8; 4096];
    loop {
        let l = data.read(&mut buf).unwrap();
        write!(output, "{:X}\r\n", l).unwrap();
        output.write_all(&buf[..l]).unwrap();
        write!(output, "\r\n").unwrap();
        if l == 0 { break; }
    }
}

mod prompt_pipelining {
    use super::*;

    /// Check that pipelined requests on the same connection are received promptly.
    fn assert_requests_parsed_promptly(
        req_cnt: usize,
        req_body: &'static [u8],
        timeout: Duration,
        req_writer: impl FnOnce(&mut dyn Write) + Send + 'static
    ) {
        let resp_body = SlowByteSrc { val: 42, len: 1000_000 }; // very slow response body

        let server = Server::http("0.0.0.0:0").unwrap();
        let mut client = TcpStream::connect(server.server_addr()).unwrap();
        let (svr_send, svr_rcv) = channel();

        spawn(move || {
            for _ in 0..req_cnt {
                let mut req = server.recv().unwrap();
                // read the whole body of the request
                let mut body = Vec::new();
                req.as_reader().read_to_end(&mut body).unwrap();
                assert_eq!(req_body, body.as_slice());
                // The next pipelined request should now be available for parsing,
                // while we send the (possibly slow) response in another thread
                spawn(move || req.respond(
                    Response::empty(200).with_data(resp_body, Some(resp_body.len))
                ));
            }
            svr_send.send(()).unwrap();
        });

        spawn(move || req_writer(&mut client));

        // requests must be sent and received quickly (before timeout expires)
        svr_rcv.recv_timeout(timeout)
            .expect("Server did not finish reading pipelined requests quickly enough");
    }

    // short (but not trivial) request body
    const REQ_BODY: &'static [u8] = &[65; 5000];

    #[test]
    fn content_length() {
        assert_requests_parsed_promptly(5, REQ_BODY, Duration::from_millis(200), move |wr| {
            for _ in 0..5 {
                write!(wr, "GET / HTTP/1.1\r\n").unwrap();
                write!(wr, "Connection: keep-alive\r\n").unwrap();
                write!(wr, "Content-Length: {}\r\n\r\n", REQ_BODY.len()).unwrap();
                wr.write_all(REQ_BODY).unwrap();
            }
        });
    }

    #[test]
    fn chunked() {
        assert_requests_parsed_promptly(5, REQ_BODY, Duration::from_millis(200), move |wr| {
            for _ in 0..5 {
                write!(wr, "GET / HTTP/1.1\r\n").unwrap();
                write!(wr, "Connection: keep-alive\r\n").unwrap();
                write!(wr, "Transfer-Encoding: chunked\r\n\r\n").unwrap();
                let mut body = REQ_BODY;
                encode_chunked(&mut body, wr);
            }
        });
    }
}

mod prompt_responses {
    use super::*;

    /// Check that response is sent promptly without waiting for full request body.
    fn assert_responds_promptly(
        timeout: Duration,
        req_writer: impl FnOnce(&mut dyn Write) + Send + 'static
    ) {
        let server = Server::http("0.0.0.0:0").unwrap();
        let client = TcpStream::connect(server.server_addr()).unwrap();

        spawn(move || loop {
            // server attempts to respond immediately
            let req = server.recv().unwrap();
            req.respond(Response::empty(400)).unwrap();
        });

        let client = Arc::new(client);
        let client_write = Arc::clone(&client);
        // request written (possibly very slowly) in another thread
        spawn(move || req_writer(&mut client_write.deref()));

        // response should arrive quickly (before timeout expires)
        client.set_read_timeout(Some(timeout)).unwrap();
        let resp = client.deref().read(&mut [0u8; 4096]);
        client.shutdown(Shutdown::Both).unwrap();
        assert!(resp.is_ok(), "Server response was not sent promptly");
    }

    static SLOW_BODY: SlowByteSrc = SlowByteSrc { val: 65, len: 1000_000 };

    #[test]
    fn content_length_http11() {
        assert_responds_promptly(Duration::from_millis(200), move |wr| {
            write!(wr, "GET / HTTP/1.1\r\n").unwrap();
            write!(wr, "Content-Length: {}\r\n\r\n", SLOW_BODY.len).unwrap();
            copy(&mut SLOW_BODY.clone(), wr).unwrap();
        });
    }

    #[test]
    fn content_length_http10() {
        assert_responds_promptly(Duration::from_millis(200), move |wr| {
            write!(wr, "GET / HTTP/1.0\r\n").unwrap();
            write!(wr, "Content-Length: {}\r\n\r\n", SLOW_BODY.len).unwrap();
            copy(&mut SLOW_BODY.clone(), wr).unwrap();
        });
    }

    #[test]
    fn expect_continue() {
        assert_responds_promptly(Duration::from_millis(200), move |wr| {
            write!(wr, "GET / HTTP/1.1\r\n").unwrap();
            write!(wr, "Expect: 100 continue\r\n").unwrap();
            write!(wr, "Content-Length: {}\r\n\r\n", SLOW_BODY.len).unwrap();
            copy(&mut SLOW_BODY.clone(), wr).unwrap();
        });
    }

    #[test]
    fn chunked() {
        assert_responds_promptly(Duration::from_millis(200), move |wr| {
            write!(wr, "GET / HTTP/1.1\r\n").unwrap();
            write!(wr, "Transfer-Encoding: chunked\r\n\r\n").unwrap();
            encode_chunked(&mut SLOW_BODY.clone(), wr);
        });
    }
}<commit_msg>Additional tests for prompt handling of short requests.<commit_after>extern crate tiny_http;

use tiny_http::{Server, Response};
use std::net::{TcpStream, Shutdown};
use std::io::{copy, Write, Read};
use std::thread::{spawn, sleep};
use std::time::Duration;
use std::sync::mpsc::channel;
use std::sync::Arc;
use std::ops::Deref;

/// Stream that produces bytes very slowly
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
struct SlowByteSrc { val: u8, len: usize }
impl<'b> Read for SlowByteSrc {
    fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
        sleep(Duration::from_millis(100));
        let l = self.len.min(buf.len()).min(1000);
        buf[..l].fill(self.val);
        self.len -= l;
        Ok(l)
    }
}

/// crude impl of http `Transfer-Encoding: chunked`
fn encode_chunked(data: &mut dyn Read, output: &mut dyn Write) {
    let mut buf = [0u8; 4096];
    loop {
        let l = data.read(&mut buf).unwrap();
        write!(output, "{:X}\r\n", l).unwrap();
        output.write_all(&buf[..l]).unwrap();
        write!(output, "\r\n").unwrap();
        if l == 0 { break; }
    }
}

mod prompt_pipelining {
    use super::*;

    /// Check that pipelined requests on the same connection are received promptly.
    fn assert_requests_parsed_promptly(
        req_cnt: usize,
        req_body: &'static [u8],
        timeout: Duration,
        req_writer: impl FnOnce(&mut dyn Write) + Send + 'static
    ) {
        let resp_body = SlowByteSrc { val: 42, len: 1000_000 }; // very slow response body

        let server = Server::http("0.0.0.0:0").unwrap();
        let mut client = TcpStream::connect(server.server_addr()).unwrap();
        let (svr_send, svr_rcv) = channel();

        spawn(move || {
            for _ in 0..req_cnt {
                let mut req = server.recv().unwrap();
                // read the whole body of the request
                let mut body = Vec::new();
                req.as_reader().read_to_end(&mut body).unwrap();
                assert_eq!(req_body, body.as_slice());
                // The next pipelined request should now be available for parsing,
                // while we send the (possibly slow) response in another thread
                spawn(move || req.respond(
                    Response::empty(200).with_data(resp_body, Some(resp_body.len))
                ));
            }
            svr_send.send(()).unwrap();
        });

        spawn(move || req_writer(&mut client));

        // requests must be sent and received quickly (before timeout expires)
        svr_rcv.recv_timeout(timeout)
            .expect("Server did not finish reading pipelined requests quickly enough");
    }

    #[test]
    fn empty() {
        assert_requests_parsed_promptly(5, &[], Duration::from_millis(200), move |wr| {
            for _ in 0..5 {
                write!(wr, "GET / HTTP/1.1\r\n").unwrap();
                write!(wr, "Connection: keep-alive\r\n\r\n").unwrap();
            }
        });
    }

    #[test]
    fn content_length_short() {
        let body = &[65u8; 100]; // short but not trivial
        assert_requests_parsed_promptly(5, body, Duration::from_millis(200), move |wr| {
            for _ in 0..5 {
                write!(wr, "GET / HTTP/1.1\r\n").unwrap();
                write!(wr, "Connection: keep-alive\r\n").unwrap();
                write!(wr, "Content-Length: {}\r\n\r\n", body.len()).unwrap();
                wr.write_all(body).unwrap();
            }
        });
    }

    #[test]
    fn content_length_long() {
        let body = &[65u8; 10000]; // long enough that it won't be buffered
        assert_requests_parsed_promptly(5, body, Duration::from_millis(200), move |wr| {
            for _ in 0..5 {
                write!(wr, "GET / HTTP/1.1\r\n").unwrap();
                write!(wr, "Connection: keep-alive\r\n").unwrap();
                write!(wr, "Content-Length: {}\r\n\r\n", body.len()).unwrap();
                wr.write_all(body).unwrap();
            }
        });
    }

    #[test]
    fn chunked() {
        let body = &[65u8; 10000];
        assert_requests_parsed_promptly(5, body, Duration::from_millis(200), move |wr| {
            for _ in 0..5 {
                write!(wr, "GET / HTTP/1.1\r\n").unwrap();
                write!(wr, "Connection: keep-alive\r\n").unwrap();
                write!(wr, "Transfer-Encoding: chunked\r\n\r\n").unwrap();
                encode_chunked(&mut &body[..], wr);
            }
        });
    }
}

mod prompt_responses {
    use super::*;

    /// Check that response is sent promptly without waiting for full request body.
    fn assert_responds_promptly(
        timeout: Duration,
        req_writer: impl FnOnce(&mut dyn Write) + Send + 'static
    ) {
        let server = Server::http("0.0.0.0:0").unwrap();
        let client = TcpStream::connect(server.server_addr()).unwrap();

        spawn(move || loop {
            // server attempts to respond immediately
            let req = server.recv().unwrap();
            req.respond(Response::empty(400)).unwrap();
        });

        let client = Arc::new(client);
        let client_write = Arc::clone(&client);
        // request written (possibly very slowly) in another thread
        spawn(move || req_writer(&mut client_write.deref()));

        // response should arrive quickly (before timeout expires)
        client.set_read_timeout(Some(timeout)).unwrap();
        let resp = client.deref().read(&mut [0u8; 4096]);
        client.shutdown(Shutdown::Both).unwrap();
        assert!(resp.is_ok(), "Server response was not sent promptly");
    }

    static SLOW_BODY: SlowByteSrc = SlowByteSrc { val: 65, len: 1000_000 };

    #[test]
    fn content_length_http11() {
        assert_responds_promptly(Duration::from_millis(200), move |wr| {
            write!(wr, "GET / HTTP/1.1\r\n").unwrap();
            write!(wr, "Content-Length: {}\r\n\r\n", SLOW_BODY.len).unwrap();
            copy(&mut SLOW_BODY.clone(), wr).unwrap();
        });
    }

    #[test]
    fn content_length_http10() {
        assert_responds_promptly(Duration::from_millis(200), move |wr| {
            write!(wr, "GET / HTTP/1.0\r\n").unwrap();
            write!(wr, "Content-Length: {}\r\n\r\n", SLOW_BODY.len).unwrap();
            copy(&mut SLOW_BODY.clone(), wr).unwrap();
        });
    }

    #[test]
    fn expect_continue() {
        assert_responds_promptly(Duration::from_millis(200), move |wr| {
            write!(wr, "GET / HTTP/1.1\r\n").unwrap();
            write!(wr, "Expect: 100 continue\r\n").unwrap();
            write!(wr, "Content-Length: {}\r\n\r\n", SLOW_BODY.len).unwrap();
            copy(&mut SLOW_BODY.clone(), wr).unwrap();
        });
    }

    #[test]
    fn chunked() {
        assert_responds_promptly(Duration::from_millis(200), move |wr| {
            write!(wr, "GET / HTTP/1.1\r\n").unwrap();
            write!(wr, "Transfer-Encoding: chunked\r\n\r\n").unwrap();
            encode_chunked(&mut SLOW_BODY.clone(), wr);
        });
    }
}<|endoftext|>


<commit_before>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

use crate::chained_bft::{
    common::{Author, Round},
    consensus_types::{sync_info::SyncInfo, vote_msg::VoteMsg},
};
use canonical_serialization::{CanonicalSerialize, CanonicalSerializer, SimpleSerializer};
use crypto::{
    hash::{CryptoHash, CryptoHasher, PacemakerTimeoutHasher, TimeoutMsgHasher},
    HashValue,
};
use failure::prelude::*;
use mirai_annotations::assumed_postcondition;
use network;
use proto_conv::{FromProto, IntoProto};
use protobuf::RepeatedField;
use serde::{Deserialize, Serialize};
use std::{collections::HashSet, convert::TryFrom, fmt, iter::FromIterator};
use types::{
    account_address::AccountAddress,
    crypto_proxies::{Signature, ValidatorSigner, ValidatorVerifier},
};

// Internal use only. Contains all the fields in PaceMakerTimeout that contributes to the
// computation of its hash.
struct PacemakerTimeoutSerializer {
    round: Round,
    author: Author,
}

impl CanonicalSerialize for PacemakerTimeoutSerializer {
    fn serialize(&self, serializer: &mut impl CanonicalSerializer) -> failure::Result<()> {
        serializer.encode_u64(self.round)?;
        serializer.encode_struct(&self.author)?;
        Ok(())
    }
}

impl CryptoHash for PacemakerTimeoutSerializer {
    type Hasher = PacemakerTimeoutHasher;

    fn hash(&self) -> HashValue {
        let mut state = Self::Hasher::default();
        state.write(&SimpleSerializer::<Vec<u8>>::serialize(self).expect("Should serialize."));
        state.finish()
    }
}

/// This message will be broadcast by a pacemaker as part of TimeoutMsg when its local
/// timeout for a round is reached.  Once f+1 PacemakerTimeout structs
/// from unique authors is gathered it forms a TimeoutCertificate.  A TimeoutCertificate is
/// a proof that will cause a replica to advance to the minimum round in the TimeoutCertificate.
#[derive(Deserialize, Serialize, Clone, Debug, PartialEq, Eq)]
pub struct PacemakerTimeout {
    round: Round,
    author: Author,
    signature: Signature,
    vote: Option<VoteMsg>,
}

impl PacemakerTimeout {
    /// Creates new PacemakerTimeout
    pub fn new(round: Round, validator_signer: &ValidatorSigner, vote: Option<VoteMsg>) -> Self {
        let author = validator_signer.author();
        let digest = PacemakerTimeoutSerializer { round, author }.hash();
        let signature = validator_signer
            .sign_message(digest)
            .expect("Failed to sign PacemakerTimeout");
        PacemakerTimeout {
            round,
            author,
            signature: signature.into(),
            vote,
        }
    }

    fn pacemaker_timeout_digest(author: AccountAddress, round: Round) -> HashValue {
        PacemakerTimeoutSerializer { round, author }.hash()
    }

    /// Calculates digest for this struct
    pub fn digest(&self) -> HashValue {
        Self::pacemaker_timeout_digest(self.author, self.round)
    }

    pub fn round(&self) -> Round {
        self.round
    }

    pub fn vote_msg(&self) -> Option<&VoteMsg> {
        self.vote.as_ref()
    }

    /// Verifies that this message has valid signature
    pub fn verify(&self, validator: &ValidatorVerifier) -> failure::Result<()> {
        self.signature
            .verify(validator, self.author, self.digest())
            .map_err(Error::from)
            .and_then(|_| {
                if let Some(vote) = self.vote.as_ref() {
                    vote.verify(validator)?;
                }
                Ok(())
            })
            .with_context(|e| format!("Fail to verify TimeoutMsg: {:?}", e))?;
        Ok(())
    }

    /// Returns the author of the timeout
    pub fn author(&self) -> Author {
        self.author
    }

    /// Returns the signature of the author for this timeout
    pub fn signature(&self) -> &Signature {
        &self.signature
    }
}

impl IntoProto for PacemakerTimeout {
    type ProtoType = network::proto::PacemakerTimeout;

    fn into_proto(self) -> Self::ProtoType {
        let mut proto = Self::ProtoType::new();
        proto.set_round(self.round);
        proto.set_author(self.author.into());
        proto.set_signature(bytes::Bytes::from(self.signature.to_bytes()));
        if let Some(vote) = self.vote {
            proto.set_vote(vote.into_proto());
        }
        proto
    }
}

impl FromProto for PacemakerTimeout {
    type ProtoType = network::proto::PacemakerTimeout;

    fn from_proto(mut object: Self::ProtoType) -> failure::Result<Self> {
        let round = object.get_round();
        let author = Author::try_from(object.take_author())?;
        let signature = Signature::try_from(object.get_signature())?;
        let vote = if let Some(vote_msg) = object.vote.into_option() {
            Some(VoteMsg::from_proto(vote_msg)?)
        } else {
            None
        };
        Ok(PacemakerTimeout {
            round,
            author,
            signature,
            vote,
        })
    }
}

// Internal use only. Contains all the fields in TimeoutMsg that contributes to the computation of
// its hash.
struct TimeoutMsgSerializer {
    pacemaker_timeout_digest: HashValue,
}

impl CanonicalSerialize for TimeoutMsgSerializer {
    fn serialize(&self, serializer: &mut impl CanonicalSerializer) -> failure::Result<()> {
        serializer.encode_bytes(self.pacemaker_timeout_digest.as_ref())?;
        Ok(())
    }
}

impl CryptoHash for TimeoutMsgSerializer {
    type Hasher = TimeoutMsgHasher;

    fn hash(&self) -> HashValue {
        let mut state = Self::Hasher::default();
        state.write(&SimpleSerializer::<Vec<u8>>::serialize(self).expect("Should serialize."));
        state.finish()
    }
}

/// This message will be broadcast by a pacemaker when its local timeout for a round is reached.
/// Once the broadcasts start, retries will continue for every timeout until the round changes.
/// Retries are required since, say if a proposer for a round r was unresponsive, it might not
/// propose if it misses even only one PacemakerTimeoutMsg.
///
/// The expected proposer will wait until n-f such messages are received before proposing to
/// ensure liveness (a next proposal has the highest quorum certificate across all replicas
/// as justification). If the expected proposer has a quorum certificate on round r-1, it need
/// not wait until n-f such messages are received and can make a proposal justified
/// by this quorum certificate.
#[derive(Deserialize, Serialize, Clone, Debug, PartialEq, Eq)]
pub struct TimeoutMsg {
    sync_info: SyncInfo,
    pacemaker_timeout: PacemakerTimeout,
    signature: Signature,
}

impl TimeoutMsg {
    /// Creates new TimeoutMsg
    pub fn new(
        sync_info: SyncInfo,
        pacemaker_timeout: PacemakerTimeout,
        validator_signer: &ValidatorSigner,
    ) -> TimeoutMsg {
        let digest = Self::new_round_digest(pacemaker_timeout.digest());
        let signature = validator_signer
            .sign_message(digest)
            .expect("Failed to sign PacemakerTimeoutMsg");
        TimeoutMsg {
            sync_info,
            pacemaker_timeout,
            signature: signature.into(),
        }
    }

    fn new_round_digest(pacemaker_timeout_digest: HashValue) -> HashValue {
        TimeoutMsgSerializer {
            pacemaker_timeout_digest,
        }
        .hash()
    }

    /// SyncInfo of the given timeout message
    pub fn sync_info(&self) -> &SyncInfo {
        &self.sync_info
    }

    /// Returns a reference to the included PacemakerTimeout
    pub fn pacemaker_timeout(&self) -> &PacemakerTimeout {
        &self.pacemaker_timeout
    }

    /// Verifies that this message has valid signature
    pub fn verify(&self, validator: &ValidatorVerifier) -> failure::Result<()> {
        self.pacemaker_timeout.verify(validator)
    }

    /// Returns the author of the TimeoutMsg
    pub fn author(&self) -> Author {
        self.pacemaker_timeout.author()
    }

    /// Returns a reference to the signature of the author
    #[allow(dead_code)]
    pub fn signature(&self) -> &Signature {
        &self.signature
    }
}

impl FromProto for TimeoutMsg {
    type ProtoType = network::proto::TimeoutMsg;

    fn from_proto(mut object: network::proto::TimeoutMsg) -> failure::Result<Self> {
        let sync_info = SyncInfo::from_proto(object.take_sync_info())?;
        let pacemaker_timeout = PacemakerTimeout::from_proto(object.take_pacemaker_timeout())?;
        let signature = Signature::try_from(object.get_signature())?;
        Ok(TimeoutMsg {
            sync_info,
            pacemaker_timeout,
            signature,
        })
    }
}

impl IntoProto for TimeoutMsg {
    type ProtoType = network::proto::TimeoutMsg;

    fn into_proto(self) -> Self::ProtoType {
        let mut proto = Self::ProtoType::new();
        proto.set_sync_info(self.sync_info.into_proto());
        proto.set_pacemaker_timeout(self.pacemaker_timeout.into_proto());
        proto.set_signature(bytes::Bytes::from(self.signature.to_bytes()));
        proto
    }
}

#[derive(Deserialize, Serialize, Clone, Debug, PartialEq, Eq)]
/// Proposal can include this timeout certificate as justification for switching to next round
pub struct PacemakerTimeoutCertificate {
    round: Round,
    timeouts: Vec<PacemakerTimeout>,
}

impl fmt::Display for PacemakerTimeoutCertificate {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "TimeoutCertificate[round: {}, timeouts:[", self.round)?;
        for (idx, timeout) in self.timeouts.iter().enumerate() {
            write!(f, "<{}>", timeout.round())?;
            if idx != self.timeouts.len() - 1 {
                write!(f, ", ")?;
            }
        }
        write!(f, "]")
    }
}

impl PacemakerTimeoutCertificate {
    /// Creates new PacemakerTimeoutCertificate
    pub fn new(round: Round, timeouts: Vec<PacemakerTimeout>) -> PacemakerTimeoutCertificate {
        PacemakerTimeoutCertificate { round, timeouts }
    }

    /// Verifies that timeouts in message actually certify the round
    pub fn verify(&self, validator: &ValidatorVerifier) -> failure::Result<()> {
        let mut min_round: Option<Round> = None;
        let mut unique_authors = HashSet::new();
        for timeout in &self.timeouts {
            timeout
                .signature()
                .verify(validator, timeout.author(), timeout.digest())
                .with_context(|e| format!("Fail to verify TimeoutCert: {:?}", e))?;
            unique_authors.insert(timeout.author());
            let timeout_round = timeout.round();
            min_round = Some(min_round.map_or(timeout_round, move |x| x.min(timeout_round)))
        }
        validator.check_voting_power(unique_authors.iter())?;
        ensure!(
            min_round == Some(self.round),
            "TimeoutCert has inconsistent round {}, expected: {:?}",
            self.round,
            min_round
        );
        Ok(())
    }

    /// Returns the round of the timeout
    pub fn round(&self) -> Round {
        // Round numbers:
        // - are reset to 0 periodically.
        // - do not exceed std::u64::MAX - 2 per the 3 chain safety rule
        // (ConsensusState::commit_rule_for_certified_block)
        assumed_postcondition!(self.round < std::u64::MAX - 1);
        self.round
    }

    /// Returns the timeouts that certify the PacemakerTimeoutCertificate
    pub fn timeouts(&self) -> &Vec<PacemakerTimeout> {
        &self.timeouts
    }
}

impl IntoProto for PacemakerTimeoutCertificate {
    type ProtoType = network::proto::PacemakerTimeoutCertificate;

    fn into_proto(self) -> Self::ProtoType {
        let mut proto = Self::ProtoType::new();
        proto.set_timeouts(RepeatedField::from_iter(
            self.timeouts.into_iter().map(PacemakerTimeout::into_proto),
        ));
        proto.set_round(self.round);
        proto
    }
}

impl FromProto for PacemakerTimeoutCertificate {
    type ProtoType = network::proto::PacemakerTimeoutCertificate;

    fn from_proto(mut object: Self::ProtoType) -> failure::Result<Self> {
        let timeouts = object
            .take_timeouts()
            .into_iter()
            .map(PacemakerTimeout::from_proto)
            .collect::<failure::Result<Vec<_>>>()?;
        Ok(PacemakerTimeoutCertificate::new(
            object.get_round(),
            timeouts,
        ))
    }
}
<commit_msg>[consensus] impl conversions from/to prost proto and PacemakerTimeout<commit_after>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

use crate::chained_bft::{
    common::{Author, Round},
    consensus_types::{sync_info::SyncInfo, vote_msg::VoteMsg},
};
use canonical_serialization::{CanonicalSerialize, CanonicalSerializer, SimpleSerializer};
use crypto::{
    hash::{CryptoHash, CryptoHasher, PacemakerTimeoutHasher, TimeoutMsgHasher},
    HashValue,
};
use failure::prelude::*;
use mirai_annotations::assumed_postcondition;
use network;
use proto_conv::{FromProto, IntoProto};
use protobuf::RepeatedField;
use serde::{Deserialize, Serialize};
use std::{collections::HashSet, convert::TryFrom, fmt, iter::FromIterator};
use types::{
    account_address::AccountAddress,
    crypto_proxies::{Signature, ValidatorSigner, ValidatorVerifier},
};

// Internal use only. Contains all the fields in PaceMakerTimeout that contributes to the
// computation of its hash.
struct PacemakerTimeoutSerializer {
    round: Round,
    author: Author,
}

impl CanonicalSerialize for PacemakerTimeoutSerializer {
    fn serialize(&self, serializer: &mut impl CanonicalSerializer) -> failure::Result<()> {
        serializer.encode_u64(self.round)?;
        serializer.encode_struct(&self.author)?;
        Ok(())
    }
}

impl CryptoHash for PacemakerTimeoutSerializer {
    type Hasher = PacemakerTimeoutHasher;

    fn hash(&self) -> HashValue {
        let mut state = Self::Hasher::default();
        state.write(&SimpleSerializer::<Vec<u8>>::serialize(self).expect("Should serialize."));
        state.finish()
    }
}

/// This message will be broadcast by a pacemaker as part of TimeoutMsg when its local
/// timeout for a round is reached.  Once f+1 PacemakerTimeout structs
/// from unique authors is gathered it forms a TimeoutCertificate.  A TimeoutCertificate is
/// a proof that will cause a replica to advance to the minimum round in the TimeoutCertificate.
#[derive(Deserialize, Serialize, Clone, Debug, PartialEq, Eq)]
pub struct PacemakerTimeout {
    round: Round,
    author: Author,
    signature: Signature,
    vote: Option<VoteMsg>,
}

impl PacemakerTimeout {
    /// Creates new PacemakerTimeout
    pub fn new(round: Round, validator_signer: &ValidatorSigner, vote: Option<VoteMsg>) -> Self {
        let author = validator_signer.author();
        let digest = PacemakerTimeoutSerializer { round, author }.hash();
        let signature = validator_signer
            .sign_message(digest)
            .expect("Failed to sign PacemakerTimeout");
        PacemakerTimeout {
            round,
            author,
            signature: signature.into(),
            vote,
        }
    }

    fn pacemaker_timeout_digest(author: AccountAddress, round: Round) -> HashValue {
        PacemakerTimeoutSerializer { round, author }.hash()
    }

    /// Calculates digest for this struct
    pub fn digest(&self) -> HashValue {
        Self::pacemaker_timeout_digest(self.author, self.round)
    }

    pub fn round(&self) -> Round {
        self.round
    }

    pub fn vote_msg(&self) -> Option<&VoteMsg> {
        self.vote.as_ref()
    }

    /// Verifies that this message has valid signature
    pub fn verify(&self, validator: &ValidatorVerifier) -> failure::Result<()> {
        self.signature
            .verify(validator, self.author, self.digest())
            .map_err(Error::from)
            .and_then(|_| {
                if let Some(vote) = self.vote.as_ref() {
                    vote.verify(validator)?;
                }
                Ok(())
            })
            .with_context(|e| format!("Fail to verify TimeoutMsg: {:?}", e))?;
        Ok(())
    }

    /// Returns the author of the timeout
    pub fn author(&self) -> Author {
        self.author
    }

    /// Returns the signature of the author for this timeout
    pub fn signature(&self) -> &Signature {
        &self.signature
    }
}

impl IntoProto for PacemakerTimeout {
    type ProtoType = network::proto::PacemakerTimeout;

    fn into_proto(self) -> Self::ProtoType {
        let mut proto = Self::ProtoType::new();
        proto.set_round(self.round);
        proto.set_author(self.author.into());
        proto.set_signature(bytes::Bytes::from(self.signature.to_bytes()));
        if let Some(vote) = self.vote {
            proto.set_vote(vote.into_proto());
        }
        proto
    }
}

impl FromProto for PacemakerTimeout {
    type ProtoType = network::proto::PacemakerTimeout;

    fn from_proto(mut object: Self::ProtoType) -> failure::Result<Self> {
        let round = object.get_round();
        let author = Author::try_from(object.take_author())?;
        let signature = Signature::try_from(object.get_signature())?;
        let vote = if let Some(vote_msg) = object.vote.into_option() {
            Some(VoteMsg::from_proto(vote_msg)?)
        } else {
            None
        };
        Ok(PacemakerTimeout {
            round,
            author,
            signature,
            vote,
        })
    }
}

impl TryFrom<network::proto::consensus_prost::PacemakerTimeout> for PacemakerTimeout {
    type Error = failure::Error;

    fn try_from(proto: network::proto::consensus_prost::PacemakerTimeout) -> failure::Result<Self> {
        let round = proto.round;
        let author = Author::try_from(&proto.author[..])?;
        let signature = Signature::try_from(&proto.signature)?;
        let vote = if let Some(vote_msg) = proto.vote {
            Some(VoteMsg::try_from(vote_msg)?)
        } else {
            None
        };
        Ok(PacemakerTimeout {
            round,
            author,
            signature,
            vote,
        })
    }
}

impl From<PacemakerTimeout> for network::proto::consensus_prost::PacemakerTimeout {
    fn from(timeout: PacemakerTimeout) -> Self {
        Self {
            round: timeout.round,
            author: timeout.author.to_vec(),
            signature: timeout.signature.to_bytes(),
            vote: timeout.vote.map(Into::into),
        }
    }
}

// Internal use only. Contains all the fields in TimeoutMsg that contributes to the computation of
// its hash.
struct TimeoutMsgSerializer {
    pacemaker_timeout_digest: HashValue,
}

impl CanonicalSerialize for TimeoutMsgSerializer {
    fn serialize(&self, serializer: &mut impl CanonicalSerializer) -> failure::Result<()> {
        serializer.encode_bytes(self.pacemaker_timeout_digest.as_ref())?;
        Ok(())
    }
}

impl CryptoHash for TimeoutMsgSerializer {
    type Hasher = TimeoutMsgHasher;

    fn hash(&self) -> HashValue {
        let mut state = Self::Hasher::default();
        state.write(&SimpleSerializer::<Vec<u8>>::serialize(self).expect("Should serialize."));
        state.finish()
    }
}

/// This message will be broadcast by a pacemaker when its local timeout for a round is reached.
/// Once the broadcasts start, retries will continue for every timeout until the round changes.
/// Retries are required since, say if a proposer for a round r was unresponsive, it might not
/// propose if it misses even only one PacemakerTimeoutMsg.
///
/// The expected proposer will wait until n-f such messages are received before proposing to
/// ensure liveness (a next proposal has the highest quorum certificate across all replicas
/// as justification). If the expected proposer has a quorum certificate on round r-1, it need
/// not wait until n-f such messages are received and can make a proposal justified
/// by this quorum certificate.
#[derive(Deserialize, Serialize, Clone, Debug, PartialEq, Eq)]
pub struct TimeoutMsg {
    sync_info: SyncInfo,
    pacemaker_timeout: PacemakerTimeout,
    signature: Signature,
}

impl TimeoutMsg {
    /// Creates new TimeoutMsg
    pub fn new(
        sync_info: SyncInfo,
        pacemaker_timeout: PacemakerTimeout,
        validator_signer: &ValidatorSigner,
    ) -> TimeoutMsg {
        let digest = Self::new_round_digest(pacemaker_timeout.digest());
        let signature = validator_signer
            .sign_message(digest)
            .expect("Failed to sign PacemakerTimeoutMsg");
        TimeoutMsg {
            sync_info,
            pacemaker_timeout,
            signature: signature.into(),
        }
    }

    fn new_round_digest(pacemaker_timeout_digest: HashValue) -> HashValue {
        TimeoutMsgSerializer {
            pacemaker_timeout_digest,
        }
        .hash()
    }

    /// SyncInfo of the given timeout message
    pub fn sync_info(&self) -> &SyncInfo {
        &self.sync_info
    }

    /// Returns a reference to the included PacemakerTimeout
    pub fn pacemaker_timeout(&self) -> &PacemakerTimeout {
        &self.pacemaker_timeout
    }

    /// Verifies that this message has valid signature
    pub fn verify(&self, validator: &ValidatorVerifier) -> failure::Result<()> {
        self.pacemaker_timeout.verify(validator)
    }

    /// Returns the author of the TimeoutMsg
    pub fn author(&self) -> Author {
        self.pacemaker_timeout.author()
    }

    /// Returns a reference to the signature of the author
    #[allow(dead_code)]
    pub fn signature(&self) -> &Signature {
        &self.signature
    }
}

impl FromProto for TimeoutMsg {
    type ProtoType = network::proto::TimeoutMsg;

    fn from_proto(mut object: network::proto::TimeoutMsg) -> failure::Result<Self> {
        let sync_info = SyncInfo::from_proto(object.take_sync_info())?;
        let pacemaker_timeout = PacemakerTimeout::from_proto(object.take_pacemaker_timeout())?;
        let signature = Signature::try_from(object.get_signature())?;
        Ok(TimeoutMsg {
            sync_info,
            pacemaker_timeout,
            signature,
        })
    }
}

impl IntoProto for TimeoutMsg {
    type ProtoType = network::proto::TimeoutMsg;

    fn into_proto(self) -> Self::ProtoType {
        let mut proto = Self::ProtoType::new();
        proto.set_sync_info(self.sync_info.into_proto());
        proto.set_pacemaker_timeout(self.pacemaker_timeout.into_proto());
        proto.set_signature(bytes::Bytes::from(self.signature.to_bytes()));
        proto
    }
}

#[derive(Deserialize, Serialize, Clone, Debug, PartialEq, Eq)]
/// Proposal can include this timeout certificate as justification for switching to next round
pub struct PacemakerTimeoutCertificate {
    round: Round,
    timeouts: Vec<PacemakerTimeout>,
}

impl fmt::Display for PacemakerTimeoutCertificate {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "TimeoutCertificate[round: {}, timeouts:[", self.round)?;
        for (idx, timeout) in self.timeouts.iter().enumerate() {
            write!(f, "<{}>", timeout.round())?;
            if idx != self.timeouts.len() - 1 {
                write!(f, ", ")?;
            }
        }
        write!(f, "]")
    }
}

impl PacemakerTimeoutCertificate {
    /// Creates new PacemakerTimeoutCertificate
    pub fn new(round: Round, timeouts: Vec<PacemakerTimeout>) -> PacemakerTimeoutCertificate {
        PacemakerTimeoutCertificate { round, timeouts }
    }

    /// Verifies that timeouts in message actually certify the round
    pub fn verify(&self, validator: &ValidatorVerifier) -> failure::Result<()> {
        let mut min_round: Option<Round> = None;
        let mut unique_authors = HashSet::new();
        for timeout in &self.timeouts {
            timeout
                .signature()
                .verify(validator, timeout.author(), timeout.digest())
                .with_context(|e| format!("Fail to verify TimeoutCert: {:?}", e))?;
            unique_authors.insert(timeout.author());
            let timeout_round = timeout.round();
            min_round = Some(min_round.map_or(timeout_round, move |x| x.min(timeout_round)))
        }
        validator.check_voting_power(unique_authors.iter())?;
        ensure!(
            min_round == Some(self.round),
            "TimeoutCert has inconsistent round {}, expected: {:?}",
            self.round,
            min_round
        );
        Ok(())
    }

    /// Returns the round of the timeout
    pub fn round(&self) -> Round {
        // Round numbers:
        // - are reset to 0 periodically.
        // - do not exceed std::u64::MAX - 2 per the 3 chain safety rule
        // (ConsensusState::commit_rule_for_certified_block)
        assumed_postcondition!(self.round < std::u64::MAX - 1);
        self.round
    }

    /// Returns the timeouts that certify the PacemakerTimeoutCertificate
    pub fn timeouts(&self) -> &Vec<PacemakerTimeout> {
        &self.timeouts
    }
}

impl IntoProto for PacemakerTimeoutCertificate {
    type ProtoType = network::proto::PacemakerTimeoutCertificate;

    fn into_proto(self) -> Self::ProtoType {
        let mut proto = Self::ProtoType::new();
        proto.set_timeouts(RepeatedField::from_iter(
            self.timeouts.into_iter().map(PacemakerTimeout::into_proto),
        ));
        proto.set_round(self.round);
        proto
    }
}

impl FromProto for PacemakerTimeoutCertificate {
    type ProtoType = network::proto::PacemakerTimeoutCertificate;

    fn from_proto(mut object: Self::ProtoType) -> failure::Result<Self> {
        let timeouts = object
            .take_timeouts()
            .into_iter()
            .map(PacemakerTimeout::from_proto)
            .collect::<failure::Result<Vec<_>>>()?;
        Ok(PacemakerTimeoutCertificate::new(
            object.get_round(),
            timeouts,
        ))
    }
}
<|endoftext|>


<commit_before>extern crate limonite;

use limonite::syntax::core::keywords::Keywords::{Def, Fn, If, Is, Print, Return, Var};
use limonite::syntax::core::symbols::Symbols::{Comma, Equals, ParenClose, ParenOpen, PlusEquals, RightThinArrow};
use limonite::syntax::core::tokens::Token;
use limonite::syntax::core::tokens::Token::{BoolLiteral, CharLiteral, Comment, EOF, Error, Identifier, Indent, Keyword, Numeric, Symbol, StrLiteral, Type};
use limonite::syntax::core::types::Types::{Float32Bit, Float64Bit, Int32Bit, Int64Bit, Str, UInt32Bit, UInt64Bit};
use limonite::syntax::lexer::{Lexer, Tokenizer};

fn cmp_tokens(mut lexer: Lexer, vec: Vec<Token>) {
    let mut tok: Token;

    for desired_tok in vec.iter() {
        tok = lexer.get_tok();

        if tok == *desired_tok { continue; }

        panic!(format!("Unexpected token `{0:?}` found. Expected `{1:?}`.", tok, desired_tok));
    }

    tok = lexer.get_tok();

    if tok != EOF {
        panic!(format!("Unexpected token `{:?}` found. Expected `EOF`", tok));
    }
}

#[test]
fn test_hello_world() {
    let input_string = "\
>> Hello World!

print(\"Hello World!\")";

    let lexer = Lexer::new(&input_string);
    let desired_output = vec![Comment(" Hello World!".to_string()), Indent(0),
                              Indent(0),
                              Keyword(Print), Symbol(ParenOpen), StrLiteral("Hello World!".to_string()), Symbol(ParenClose), EOF];

    cmp_tokens(lexer, desired_output);
}

#[test]
fn test_indentation() {
    let input_string = "\
>>>
    Test of indentation and a few keywords.
<<<

if True,
	func()

	if False,
		func2()";

    let lexer = Lexer::new(&input_string);
    let desired_output = vec![Comment("\n    Test of indentation and a few keywords.\n".to_string()), Indent(0),
                              Indent(0),
                              Keyword(If), BoolLiteral(true), Symbol(Comma), Indent(1),
                              Identifier("func".to_string()), Symbol(ParenOpen), Symbol(ParenClose), Indent(0),
                              Indent(1),
                              Keyword(If), BoolLiteral(false), Symbol(Comma), Indent(2),
                              Identifier("func2".to_string()), Symbol(ParenOpen), Symbol(ParenClose), EOF];

    cmp_tokens(lexer, desired_output);
}

#[test]
fn test_valid_numerics() {
    let input_string = "0xF3a
0xfffi32
0xfffi64
0xfffu32
0xfffu64
0b111
0b101i32
0b101i64
0b101u32
0b101u64
42
42i32
42i64
42u32
42u64
42.0
42.0f32
42.0f64
0xFFFF_FFFF
0b0101_0101
400_000
400_000.000_000";

    let lexer = Lexer::new(&input_string);
    let desired_output = vec![Numeric("0xF3a".to_string(), None), Indent(0),
                              Numeric("0xfff".to_string(), Some(Int32Bit)), Indent(0),
                              Numeric("0xfff".to_string(), Some(Int64Bit)), Indent(0),
                              Numeric("0xfff".to_string(), Some(UInt32Bit)), Indent(0),
                              Numeric("0xfff".to_string(), Some(UInt64Bit)), Indent(0),
                              Numeric("0b111".to_string(), None), Indent(0),
                              Numeric("0b101".to_string(), Some(Int32Bit)), Indent(0),
                              Numeric("0b101".to_string(), Some(Int64Bit)), Indent(0),
                              Numeric("0b101".to_string(), Some(UInt32Bit)), Indent(0),
                              Numeric("0b101".to_string(), Some(UInt64Bit)), Indent(0),
                              Numeric("42".to_string(), None), Indent(0),
                              Numeric("42".to_string(), Some(Int32Bit)), Indent(0),
                              Numeric("42".to_string(), Some(Int64Bit)), Indent(0),
                              Numeric("42".to_string(), Some(UInt32Bit)), Indent(0),
                              Numeric("42".to_string(), Some(UInt64Bit)), Indent(0),
                              Numeric("42.0".to_string(), None), Indent(0),
                              Numeric("42.0".to_string(), Some(Float32Bit)), Indent(0),
                              Numeric("42.0".to_string(), Some(Float64Bit)), Indent(0),
                              Numeric("0xFFFF_FFFF".to_string(), None), Indent(0),
                              Numeric("0b0101_0101".to_string(), None), Indent(0),
                              Numeric("400_000".to_string(), None), Indent(0),
                              Numeric("400_000.000_000".to_string(), None), EOF];

    cmp_tokens(lexer, desired_output);
}

#[test]
fn test_invalid_numerics() {
    let input_string = "0x
0xz
0xfz
0xfi3
0xfi31
0xfi6
0xfi63
0xfu8
0b
0ba
0b1a
0b1f
42f32
42i3
42i31
42.0f
42.0f3
42.0f31";

    let lexer = Lexer::new(&input_string);
    let desired_output = vec![Error("No hexadecimal value was found.".to_string()), Indent(0),
                              Error("No hexadecimal value was found.".to_string()), Identifier("z".to_string()), Indent(0),
                              Error("Invalid suffix z. Did you mean u32, u64, i32, or i64?".to_string()), Indent(0),
                              Error("Invalid suffix i3. Did you mean i32?".to_string()), Indent(0),
                              Error("Invalid suffix i31. Did you mean i32?".to_string()), Indent(0),
                              Error("Invalid suffix i6. Did you mean i64?".to_string()), Indent(0),
                              Error("Invalid suffix i63. Did you mean i64?".to_string()), Indent(0),
                              Error("Invalid suffix u8. Did you mean u32 or u64?".to_string()), Indent(0),
                              Error("No binary value was found.".to_string()), Indent(0),
                              Error("No binary value was found.".to_string()), Identifier("a".to_string()), Indent(0),
                              Error("Invalid suffix a. Did you mean u32, u64, i32, or i64?".to_string()), Indent(0),
                              Error("Invalid suffix f. Did you mean u32, u64, i32, or i64?".to_string()), Indent(0),
                              Error("Invalid suffix f. Did you mean u32, u64, i32, or i64?".to_string()), Numeric("32".to_string(), None), Indent(0),
                              Error("Invalid suffix i3. Did you mean i32?".to_string()), Indent(0),
                              Error("Invalid suffix i31. Did you mean i32?".to_string()), Indent(0),
                              Error("Invalid suffix f. Did you mean f32 or f64?".to_string()), Indent(0),
                              Error("Invalid suffix f3. Did you mean f32?".to_string()), Indent(0),
                              Error("Invalid suffix f31. Did you mean f32?".to_string()), EOF];

    cmp_tokens(lexer, desired_output);
}

#[test]
fn test_functions() {
    let input_string = "\
fn basic_func() -> str
	def ch = 'g'
	var string = \"strin\"

	if ch is 'g',
		string += ch

	return string";

    let lexer = Lexer::new(&input_string);
    let desired_output = vec![Keyword(Fn), Identifier("basic_func".to_string()), Symbol(ParenOpen), Symbol(ParenClose),
                              Symbol(RightThinArrow), Type(Str), Indent(1),
                              Keyword(Def), Identifier("ch".to_string()), Symbol(Equals), CharLiteral('g'), Indent(1),
                              Keyword(Var), Identifier("string".to_string()), Symbol(Equals), StrLiteral("strin".to_string()), Indent(0),
                              Indent(1),
                              Keyword(If), Identifier("ch".to_string()), Keyword(Is), CharLiteral('g'), Symbol(Comma), Indent(2),
                              Identifier("string".to_string()), Symbol(PlusEquals), Identifier("ch".to_string()), Indent(0),
                              Indent(1),
                              Keyword(Return), Identifier("string".to_string()), EOF];

    cmp_tokens(lexer, desired_output);
}
<commit_msg>Tiny string EOL fix.<commit_after>extern crate limonite;

use limonite::syntax::core::keywords::Keywords::{Def, Fn, If, Is, Print, Return, Var};
use limonite::syntax::core::symbols::Symbols::{Comma, Equals, ParenClose, ParenOpen, PlusEquals, RightThinArrow};
use limonite::syntax::core::tokens::Token;
use limonite::syntax::core::tokens::Token::{BoolLiteral, CharLiteral, Comment, EOF, Error, Identifier, Indent, Keyword, Numeric, Symbol, StrLiteral, Type};
use limonite::syntax::core::types::Types::{Float32Bit, Float64Bit, Int32Bit, Int64Bit, Str, UInt32Bit, UInt64Bit};
use limonite::syntax::lexer::{Lexer, Tokenizer};

fn cmp_tokens(mut lexer: Lexer, vec: Vec<Token>) {
    let mut tok: Token;

    for desired_tok in vec.iter() {
        tok = lexer.get_tok();

        if tok == *desired_tok { continue; }

        panic!(format!("Unexpected token `{0:?}` found. Expected `{1:?}`.", tok, desired_tok));
    }

    tok = lexer.get_tok();

    if tok != EOF {
        panic!(format!("Unexpected token `{:?}` found. Expected `EOF`", tok));
    }
}

#[test]
fn test_hello_world() {
    let input_string = "\
>> Hello World!

print(\"Hello World!\")";

    let lexer = Lexer::new(&input_string);
    let desired_output = vec![Comment(" Hello World!".to_string()), Indent(0),
                              Indent(0),
                              Keyword(Print), Symbol(ParenOpen), StrLiteral("Hello World!".to_string()), Symbol(ParenClose), EOF];

    cmp_tokens(lexer, desired_output);
}

#[test]
fn test_indentation() {
    let input_string = "\
>>>
    Test of indentation and a few keywords.
<<<

if True,
	func()

	if False,
		func2()";

    let lexer = Lexer::new(&input_string);
    let desired_output = vec![Comment("\n    Test of indentation and a few keywords.\n".to_string()), Indent(0),
                              Indent(0),
                              Keyword(If), BoolLiteral(true), Symbol(Comma), Indent(1),
                              Identifier("func".to_string()), Symbol(ParenOpen), Symbol(ParenClose), Indent(0),
                              Indent(1),
                              Keyword(If), BoolLiteral(false), Symbol(Comma), Indent(2),
                              Identifier("func2".to_string()), Symbol(ParenOpen), Symbol(ParenClose), EOF];

    cmp_tokens(lexer, desired_output);
}

#[test]
fn test_valid_numerics() {
    let input_string = "\
0xF3a
0xfffi32
0xfffi64
0xfffu32
0xfffu64
0b111
0b101i32
0b101i64
0b101u32
0b101u64
42
42i32
42i64
42u32
42u64
42.0
42.0f32
42.0f64
0xFFFF_FFFF
0b0101_0101
400_000
400_000.000_000";

    let lexer = Lexer::new(&input_string);
    let desired_output = vec![Numeric("0xF3a".to_string(), None), Indent(0),
                              Numeric("0xfff".to_string(), Some(Int32Bit)), Indent(0),
                              Numeric("0xfff".to_string(), Some(Int64Bit)), Indent(0),
                              Numeric("0xfff".to_string(), Some(UInt32Bit)), Indent(0),
                              Numeric("0xfff".to_string(), Some(UInt64Bit)), Indent(0),
                              Numeric("0b111".to_string(), None), Indent(0),
                              Numeric("0b101".to_string(), Some(Int32Bit)), Indent(0),
                              Numeric("0b101".to_string(), Some(Int64Bit)), Indent(0),
                              Numeric("0b101".to_string(), Some(UInt32Bit)), Indent(0),
                              Numeric("0b101".to_string(), Some(UInt64Bit)), Indent(0),
                              Numeric("42".to_string(), None), Indent(0),
                              Numeric("42".to_string(), Some(Int32Bit)), Indent(0),
                              Numeric("42".to_string(), Some(Int64Bit)), Indent(0),
                              Numeric("42".to_string(), Some(UInt32Bit)), Indent(0),
                              Numeric("42".to_string(), Some(UInt64Bit)), Indent(0),
                              Numeric("42.0".to_string(), None), Indent(0),
                              Numeric("42.0".to_string(), Some(Float32Bit)), Indent(0),
                              Numeric("42.0".to_string(), Some(Float64Bit)), Indent(0),
                              Numeric("0xFFFF_FFFF".to_string(), None), Indent(0),
                              Numeric("0b0101_0101".to_string(), None), Indent(0),
                              Numeric("400_000".to_string(), None), Indent(0),
                              Numeric("400_000.000_000".to_string(), None), EOF];

    cmp_tokens(lexer, desired_output);
}

#[test]
fn test_invalid_numerics() {
    let input_string = "\
0x
0xz
0xfz
0xfi3
0xfi31
0xfi6
0xfi63
0xfu8
0b
0ba
0b1a
0b1f
42f32
42i3
42i31
42.0f
42.0f3
42.0f31";

    let lexer = Lexer::new(&input_string);
    let desired_output = vec![Error("No hexadecimal value was found.".to_string()), Indent(0),
                              Error("No hexadecimal value was found.".to_string()), Identifier("z".to_string()), Indent(0),
                              Error("Invalid suffix z. Did you mean u32, u64, i32, or i64?".to_string()), Indent(0),
                              Error("Invalid suffix i3. Did you mean i32?".to_string()), Indent(0),
                              Error("Invalid suffix i31. Did you mean i32?".to_string()), Indent(0),
                              Error("Invalid suffix i6. Did you mean i64?".to_string()), Indent(0),
                              Error("Invalid suffix i63. Did you mean i64?".to_string()), Indent(0),
                              Error("Invalid suffix u8. Did you mean u32 or u64?".to_string()), Indent(0),
                              Error("No binary value was found.".to_string()), Indent(0),
                              Error("No binary value was found.".to_string()), Identifier("a".to_string()), Indent(0),
                              Error("Invalid suffix a. Did you mean u32, u64, i32, or i64?".to_string()), Indent(0),
                              Error("Invalid suffix f. Did you mean u32, u64, i32, or i64?".to_string()), Indent(0),
                              Error("Invalid suffix f. Did you mean u32, u64, i32, or i64?".to_string()), Numeric("32".to_string(), None), Indent(0),
                              Error("Invalid suffix i3. Did you mean i32?".to_string()), Indent(0),
                              Error("Invalid suffix i31. Did you mean i32?".to_string()), Indent(0),
                              Error("Invalid suffix f. Did you mean f32 or f64?".to_string()), Indent(0),
                              Error("Invalid suffix f3. Did you mean f32?".to_string()), Indent(0),
                              Error("Invalid suffix f31. Did you mean f32?".to_string()), EOF];

    cmp_tokens(lexer, desired_output);
}

#[test]
fn test_functions() {
    let input_string = "\
fn basic_func() -> str
	def ch = 'g'
	var string = \"strin\"

	if ch is 'g',
		string += ch

	return string";

    let lexer = Lexer::new(&input_string);
    let desired_output = vec![Keyword(Fn), Identifier("basic_func".to_string()), Symbol(ParenOpen), Symbol(ParenClose),
                              Symbol(RightThinArrow), Type(Str), Indent(1),
                              Keyword(Def), Identifier("ch".to_string()), Symbol(Equals), CharLiteral('g'), Indent(1),
                              Keyword(Var), Identifier("string".to_string()), Symbol(Equals), StrLiteral("strin".to_string()), Indent(0),
                              Indent(1),
                              Keyword(If), Identifier("ch".to_string()), Keyword(Is), CharLiteral('g'), Symbol(Comma), Indent(2),
                              Identifier("string".to_string()), Symbol(PlusEquals), Identifier("ch".to_string()), Indent(0),
                              Indent(1),
                              Keyword(Return), Identifier("string".to_string()), EOF];

    cmp_tokens(lexer, desired_output);
}
<|endoftext|>


<commit_before>use syntax::{ast, ast::Radix, AstToken};

use crate::{AssistContext, AssistId, AssistKind, Assists, GroupLabel};

const MIN_NUMBER_OF_DIGITS_TO_FORMAT: usize = 5;

// Assist: reformat_number_literal
//
// Adds or removes seprators from integer literal.
//
// ```
// const _: i32 = 1012345$0;
// ```
// ->
// ```
// const _: i32 = 1_012_345;
// ```
pub(crate) fn reformat_number_literal(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
    let literal = ctx.find_node_at_offset::<ast::Literal>()?;
    let literal = match literal.kind() {
        ast::LiteralKind::IntNumber(it) => it,
        _ => return None,
    };

    let text = literal.text();
    if text.contains('_') {
        return remove_separators(acc, literal);
    }

    let (prefix, value, suffix) = literal.split_into_parts();
    if value.len() < MIN_NUMBER_OF_DIGITS_TO_FORMAT {
        return None;
    }

    let radix = literal.radix();
    let mut converted = prefix.to_string();
    converted.push_str(&add_group_separators(value, group_size(radix)));
    converted.push_str(suffix);

    let group_id = GroupLabel("Reformat number literal".into());
    let label = format!("Convert {} to {}", literal, converted);
    let range = literal.syntax().text_range();
    acc.add_group(
        &group_id,
        AssistId("reformat_number_literal", AssistKind::RefactorInline),
        label,
        range,
        |builder| builder.replace(range, converted),
    )
}

fn remove_separators(acc: &mut Assists, literal: ast::IntNumber) -> Option<()> {
    let group_id = GroupLabel("Reformat number literal".into());
    let range = literal.syntax().text_range();
    acc.add_group(
        &group_id,
        AssistId("reformat_number_literal", AssistKind::RefactorInline),
        "Remove digit seprators",
        range,
        |builder| builder.replace(range, literal.text().replace("_", "")),
    )
}

const fn group_size(r: Radix) -> usize {
    match r {
        Radix::Binary => 4,
        Radix::Octal => 3,
        Radix::Decimal => 3,
        Radix::Hexadecimal => 4,
    }
}

fn add_group_separators(s: &str, group_size: usize) -> String {
    let mut chars = Vec::new();
    for (i, ch) in s.chars().filter(|&ch| ch != '_').rev().enumerate() {
        if i > 0 && i % group_size == 0 {
            chars.push('_');
        }
        chars.push(ch);
    }

    chars.into_iter().rev().collect()
}

#[cfg(test)]
mod tests {
    use crate::tests::{check_assist_by_label, check_assist_not_applicable, check_assist_target};

    use super::*;

    #[test]
    fn group_separators() {
        let cases = vec![
            ("", 4, ""),
            ("1", 4, "1"),
            ("12", 4, "12"),
            ("123", 4, "123"),
            ("1234", 4, "1234"),
            ("12345", 4, "1_2345"),
            ("123456", 4, "12_3456"),
            ("1234567", 4, "123_4567"),
            ("12345678", 4, "1234_5678"),
            ("123456789", 4, "1_2345_6789"),
            ("1234567890", 4, "12_3456_7890"),
            ("1_2_3_4_5_6_7_8_9_0_", 4, "12_3456_7890"),
            ("1234567890", 3, "1_234_567_890"),
            ("1234567890", 2, "12_34_56_78_90"),
            ("1234567890", 1, "1_2_3_4_5_6_7_8_9_0"),
        ];

        for case in cases {
            let (input, group_size, expected) = case;
            assert_eq!(add_group_separators(input, group_size), expected)
        }
    }

    #[test]
    fn good_targets() {
        let cases = vec![
            ("const _: i32 = 0b11111$0", "0b11111"),
            ("const _: i32 = 0o77777$0;", "0o77777"),
            ("const _: i32 = 10000$0;", "10000"),
            ("const _: i32 = 0xFFFFF$0;", "0xFFFFF"),
            ("const _: i32 = 10000i32$0;", "10000i32"),
            ("const _: i32 = 0b_10_0i32$0;", "0b_10_0i32"),
        ];

        for case in cases {
            check_assist_target(reformat_number_literal, case.0, case.1);
        }
    }

    #[test]
    fn bad_targets() {
        let cases = vec![
            "const _: i32 = 0b111$0",
            "const _: i32 = 0b1111$0",
            "const _: i32 = 0o77$0;",
            "const _: i32 = 0o777$0;",
            "const _: i32 = 10$0;",
            "const _: i32 = 999$0;",
            "const _: i32 = 0xFF$0;",
            "const _: i32 = 0xFFFF$0;",
        ];

        for case in cases {
            check_assist_not_applicable(reformat_number_literal, case);
        }
    }

    #[test]
    fn labels() {
        let cases = vec![
            ("const _: i32 = 10000$0", "const _: i32 = 10_000", "Convert 10000 to 10_000"),
            (
                "const _: i32 = 0xFF0000$0;",
                "const _: i32 = 0xFF_0000;",
                "Convert 0xFF0000 to 0xFF_0000",
            ),
            (
                "const _: i32 = 0b11111111$0;",
                "const _: i32 = 0b1111_1111;",
                "Convert 0b11111111 to 0b1111_1111",
            ),
            (
                "const _: i32 = 0o377211$0;",
                "const _: i32 = 0o377_211;",
                "Convert 0o377211 to 0o377_211",
            ),
            (
                "const _: i32 = 10000i32$0;",
                "const _: i32 = 10_000i32;",
                "Convert 10000i32 to 10_000i32",
            ),
            ("const _: i32 = 1_0_0_0_i32$0;", "const _: i32 = 1000i32;", "Remove digit seprators"),
        ];

        for case in cases {
            let (before, after, label) = case;
            check_assist_by_label(reformat_number_literal, before, after, label);
        }
    }
}
<commit_msg>Fix typo in reformat number assist label<commit_after>use syntax::{ast, ast::Radix, AstToken};

use crate::{AssistContext, AssistId, AssistKind, Assists, GroupLabel};

const MIN_NUMBER_OF_DIGITS_TO_FORMAT: usize = 5;

// Assist: reformat_number_literal
//
// Adds or removes separators from integer literal.
//
// ```
// const _: i32 = 1012345$0;
// ```
// ->
// ```
// const _: i32 = 1_012_345;
// ```
pub(crate) fn reformat_number_literal(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
    let literal = ctx.find_node_at_offset::<ast::Literal>()?;
    let literal = match literal.kind() {
        ast::LiteralKind::IntNumber(it) => it,
        _ => return None,
    };

    let text = literal.text();
    if text.contains('_') {
        return remove_separators(acc, literal);
    }

    let (prefix, value, suffix) = literal.split_into_parts();
    if value.len() < MIN_NUMBER_OF_DIGITS_TO_FORMAT {
        return None;
    }

    let radix = literal.radix();
    let mut converted = prefix.to_string();
    converted.push_str(&add_group_separators(value, group_size(radix)));
    converted.push_str(suffix);

    let group_id = GroupLabel("Reformat number literal".into());
    let label = format!("Convert {} to {}", literal, converted);
    let range = literal.syntax().text_range();
    acc.add_group(
        &group_id,
        AssistId("reformat_number_literal", AssistKind::RefactorInline),
        label,
        range,
        |builder| builder.replace(range, converted),
    )
}

fn remove_separators(acc: &mut Assists, literal: ast::IntNumber) -> Option<()> {
    let group_id = GroupLabel("Reformat number literal".into());
    let range = literal.syntax().text_range();
    acc.add_group(
        &group_id,
        AssistId("reformat_number_literal", AssistKind::RefactorInline),
        "Remove digit separators",
        range,
        |builder| builder.replace(range, literal.text().replace("_", "")),
    )
}

const fn group_size(r: Radix) -> usize {
    match r {
        Radix::Binary => 4,
        Radix::Octal => 3,
        Radix::Decimal => 3,
        Radix::Hexadecimal => 4,
    }
}

fn add_group_separators(s: &str, group_size: usize) -> String {
    let mut chars = Vec::new();
    for (i, ch) in s.chars().filter(|&ch| ch != '_').rev().enumerate() {
        if i > 0 && i % group_size == 0 {
            chars.push('_');
        }
        chars.push(ch);
    }

    chars.into_iter().rev().collect()
}

#[cfg(test)]
mod tests {
    use crate::tests::{check_assist_by_label, check_assist_not_applicable, check_assist_target};

    use super::*;

    #[test]
    fn group_separators() {
        let cases = vec![
            ("", 4, ""),
            ("1", 4, "1"),
            ("12", 4, "12"),
            ("123", 4, "123"),
            ("1234", 4, "1234"),
            ("12345", 4, "1_2345"),
            ("123456", 4, "12_3456"),
            ("1234567", 4, "123_4567"),
            ("12345678", 4, "1234_5678"),
            ("123456789", 4, "1_2345_6789"),
            ("1234567890", 4, "12_3456_7890"),
            ("1_2_3_4_5_6_7_8_9_0_", 4, "12_3456_7890"),
            ("1234567890", 3, "1_234_567_890"),
            ("1234567890", 2, "12_34_56_78_90"),
            ("1234567890", 1, "1_2_3_4_5_6_7_8_9_0"),
        ];

        for case in cases {
            let (input, group_size, expected) = case;
            assert_eq!(add_group_separators(input, group_size), expected)
        }
    }

    #[test]
    fn good_targets() {
        let cases = vec![
            ("const _: i32 = 0b11111$0", "0b11111"),
            ("const _: i32 = 0o77777$0;", "0o77777"),
            ("const _: i32 = 10000$0;", "10000"),
            ("const _: i32 = 0xFFFFF$0;", "0xFFFFF"),
            ("const _: i32 = 10000i32$0;", "10000i32"),
            ("const _: i32 = 0b_10_0i32$0;", "0b_10_0i32"),
        ];

        for case in cases {
            check_assist_target(reformat_number_literal, case.0, case.1);
        }
    }

    #[test]
    fn bad_targets() {
        let cases = vec![
            "const _: i32 = 0b111$0",
            "const _: i32 = 0b1111$0",
            "const _: i32 = 0o77$0;",
            "const _: i32 = 0o777$0;",
            "const _: i32 = 10$0;",
            "const _: i32 = 999$0;",
            "const _: i32 = 0xFF$0;",
            "const _: i32 = 0xFFFF$0;",
        ];

        for case in cases {
            check_assist_not_applicable(reformat_number_literal, case);
        }
    }

    #[test]
    fn labels() {
        let cases = vec![
            ("const _: i32 = 10000$0", "const _: i32 = 10_000", "Convert 10000 to 10_000"),
            (
                "const _: i32 = 0xFF0000$0;",
                "const _: i32 = 0xFF_0000;",
                "Convert 0xFF0000 to 0xFF_0000",
            ),
            (
                "const _: i32 = 0b11111111$0;",
                "const _: i32 = 0b1111_1111;",
                "Convert 0b11111111 to 0b1111_1111",
            ),
            (
                "const _: i32 = 0o377211$0;",
                "const _: i32 = 0o377_211;",
                "Convert 0o377211 to 0o377_211",
            ),
            (
                "const _: i32 = 10000i32$0;",
                "const _: i32 = 10_000i32;",
                "Convert 10000i32 to 10_000i32",
            ),
            ("const _: i32 = 1_0_0_0_i32$0;", "const _: i32 = 1000i32;", "Remove digit separators"),
        ];

        for case in cases {
            let (before, after, label) = case;
            check_assist_by_label(reformat_number_literal, before, after, label);
        }
    }
}
<|endoftext|>


<commit_before>use std::fmt;

pub use serde_json::Value;

/// Data type of the `extras` attribute on all glTF objects.
#[cfg(feature = "extras")]
pub type Extras = Option<Value>;

/// Data type of the `extras` attribute on all glTF objects.
#[cfg(not(feature = "extras"))]
pub type Extras = Void;

/// Type representing no user-defined data.
#[derive(Clone, Default, Deserialize, Serialize, Validate)]
pub struct Void {
    #[serde(default)]
    _allow_unknown_fields: (),
}

impl fmt::Debug for Void {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{{}}")
    }
}

impl fmt::Display for Void {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{{}}")
    }
}
<commit_msg>Skip serializing extras::Void's field<commit_after>use std::fmt;

pub use serde_json::Value;

/// Data type of the `extras` attribute on all glTF objects.
#[cfg(feature = "extras")]
pub type Extras = Option<Value>;

/// Data type of the `extras` attribute on all glTF objects.
#[cfg(not(feature = "extras"))]
pub type Extras = Void;

/// Type representing no user-defined data.
#[derive(Clone, Default, Serialize, Deserialize, Validate)]
pub struct Void {
    #[serde(default, skip_serializing)]
    _allow_unknown_fields: (),
}

impl fmt::Debug for Void {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{{}}")
    }
}

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


<commit_before>//! Like `std::time::Instant`, but also measures memory & CPU cycles.
use std::{
    fmt,
    time::{Duration, Instant},
};

use crate::MemoryUsage;

pub struct StopWatch {
    time: Instant,
    #[cfg(target_os = "linux")]
    counter: Option<perf_event::Counter>,
    memory: Option<MemoryUsage>,
}

pub struct StopWatchSpan {
    pub time: Duration,
    pub instructions: Option<u64>,
    pub memory: Option<MemoryUsage>,
}

impl StopWatch {
    pub fn start() -> StopWatch {
        #[cfg(target_os = "linux")]
        let counter = {
            let mut counter = perf_event::Builder::new()
                .build()
                .map_err(|err| eprintln!("Failed to create perf counter: {}", err))
                .ok();
            if let Some(counter) = &mut counter {
                if let Err(err) = counter.enable() {
                    eprintln!("Failed to start perf counter: {}", err)
                }
            }
            counter
        };
        let time = Instant::now();
        StopWatch {
            time,
            #[cfg(target_os = "linux")]
            counter,
            memory: None,
        }
    }
    pub fn memory(mut self, yes: bool) -> StopWatch {
        if yes {
            self.memory = Some(MemoryUsage::current());
        }
        self
    }
    pub fn elapsed(&mut self) -> StopWatchSpan {
        let time = self.time.elapsed();

        #[cfg(target_os = "linux")]
        let instructions = self.counter.as_mut().and_then(|it| {
            it.read().map_err(|err| eprintln!("Failed to read perf counter: {}", err)).ok()
        });
        #[cfg(not(target_os = "linux"))]
        let instructions = None;

        let memory = self.memory.map(|it| MemoryUsage::current() - it);
        StopWatchSpan { time, instructions, memory }
    }
}

impl fmt::Display for StopWatchSpan {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{:.2?}", self.time)?;
        if let Some(mut instructions) = self.instructions {
            let mut prefix = "";
            if instructions > 10000 {
                instructions /= 1000;
                prefix = "k"
            }
            if instructions > 10000 {
                instructions /= 1000;
                prefix = "m"
            }
            write!(f, ", {}{}i", instructions, prefix)?;
        }
        if let Some(memory) = self.memory {
            write!(f, ", {}", memory)?;
        }
        Ok(())
    }
}
<commit_msg>Less confusing instr stat<commit_after>//! Like `std::time::Instant`, but also measures memory & CPU cycles.
use std::{
    fmt,
    time::{Duration, Instant},
};

use crate::MemoryUsage;

pub struct StopWatch {
    time: Instant,
    #[cfg(target_os = "linux")]
    counter: Option<perf_event::Counter>,
    memory: Option<MemoryUsage>,
}

pub struct StopWatchSpan {
    pub time: Duration,
    pub instructions: Option<u64>,
    pub memory: Option<MemoryUsage>,
}

impl StopWatch {
    pub fn start() -> StopWatch {
        #[cfg(target_os = "linux")]
        let counter = {
            let mut counter = perf_event::Builder::new()
                .build()
                .map_err(|err| eprintln!("Failed to create perf counter: {}", err))
                .ok();
            if let Some(counter) = &mut counter {
                if let Err(err) = counter.enable() {
                    eprintln!("Failed to start perf counter: {}", err)
                }
            }
            counter
        };
        let time = Instant::now();
        StopWatch {
            time,
            #[cfg(target_os = "linux")]
            counter,
            memory: None,
        }
    }
    pub fn memory(mut self, yes: bool) -> StopWatch {
        if yes {
            self.memory = Some(MemoryUsage::current());
        }
        self
    }
    pub fn elapsed(&mut self) -> StopWatchSpan {
        let time = self.time.elapsed();

        #[cfg(target_os = "linux")]
        let instructions = self.counter.as_mut().and_then(|it| {
            it.read().map_err(|err| eprintln!("Failed to read perf counter: {}", err)).ok()
        });
        #[cfg(not(target_os = "linux"))]
        let instructions = None;

        let memory = self.memory.map(|it| MemoryUsage::current() - it);
        StopWatchSpan { time, instructions, memory }
    }
}

impl fmt::Display for StopWatchSpan {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{:.2?}", self.time)?;
        if let Some(mut instructions) = self.instructions {
            let mut prefix = "";
            if instructions > 10000 {
                instructions /= 1000;
                prefix = "k"
            }
            if instructions > 10000 {
                instructions /= 1000;
                prefix = "m"
            }
            if instructions > 10000 {
                instructions /= 1000;
                prefix = "g"
            }
            write!(f, ", {}{}instr", instructions, prefix)?;
        }
        if let Some(memory) = self.memory {
            write!(f, ", {}", memory)?;
        }
        Ok(())
    }
}
<|endoftext|>