<commit_before>extern crate pinyin;

struct TestCase {
    hans: String,
    args: pinyin::Args,
    result: Vec<Vec<String>>,
    lazy_result: Vec<String>,
}

impl TestCase {
    pub fn new(
        hans: String,
        args: pinyin::Args,
        result: Vec<Vec<String>>,
        lazy_result: Vec<String>,
    ) -> TestCase {
        TestCase {
            hans,
            args,
            result,
            lazy_result,
        }
    }
}

#[test]
fn test_pinyin() {
    let test_data = vec![
        TestCase::new(
            "中国人".to_string(),
            pinyin::Args::new(),
            vec![
                vec!["zhong".to_string()],
                vec!["guo".to_string()],
                vec!["ren".to_string()],
            ],
            vec!["zhong".to_string(), "guo".to_string(), "ren".to_string()],
        ),
        TestCase::new(
            "中国人".to_string(),
            pinyin::Args {
                style: pinyin::Style::Normal,
                heteronym: false,
            },
            vec![
                vec!["zhong".to_string()],
                vec!["guo".to_string()],
                vec!["ren".to_string()],
            ],
            vec!["zhong".to_string(), "guo".to_string(), "ren".to_string()],
        ),
        TestCase::new(
            "中国人".to_string(),
            pinyin::Args {
                style: pinyin::Style::Tone,
                heteronym: false,
            },
            vec![
                vec!["zhōng".to_string()],
                vec!["guó".to_string()],
                vec!["rén".to_string()],
            ],
            vec!["zhōng".to_string(), "guó".to_string(), "rén".to_string()],
        ),
        TestCase::new(
            "中国人".to_string(),
            pinyin::Args {
                style: pinyin::Style::Tone2,
                heteronym: false,
            },
            vec![
                vec!["zho1ng".to_string()],
                vec!["guo2".to_string()],
                vec!["re2n".to_string()],
            ],
            vec!["zho1ng".to_string(), "guo2".to_string(), "re2n".to_string()],
        ),
        TestCase::new(
            "中国人".to_string(),
            pinyin::Args {
                style: pinyin::Style::Initials,
                heteronym: false,
            },
            vec![
                vec!["zh".to_string()],
                vec!["g".to_string()],
                vec!["r".to_string()],
            ],
            vec!["zh".to_string(), "g".to_string(), "r".to_string()],
        ),
        TestCase::new(
            "中国人".to_string(),
            pinyin::Args {
                style: pinyin::Style::FirstLetter,
                heteronym: false,
            },
            vec![
                vec!["z".to_string()],
                vec!["g".to_string()],
                vec!["r".to_string()],
            ],
            vec!["z".to_string(), "g".to_string(), "r".to_string()],
        ),
        TestCase::new(
            "中国人".to_string(),
            pinyin::Args {
                style: pinyin::Style::Finals,
                heteronym: false,
            },
            vec![
                vec!["ong".to_string()],
                vec!["uo".to_string()],
                vec!["en".to_string()],
            ],
            vec!["ong".to_string(), "uo".to_string(), "en".to_string()],
        ),
        TestCase::new(
            "中国人".to_string(),
            pinyin::Args {
                style: pinyin::Style::FinalsTone,
                heteronym: false,
            },
            vec![
                vec!["ōng".to_string()],
                vec!["uó".to_string()],
                vec!["én".to_string()],
            ],
            vec!["ōng".to_string(), "uó".to_string(), "én".to_string()],
        ),
        TestCase::new(
            "中国人".to_string(),
            pinyin::Args {
                style: pinyin::Style::FinalsTone2,
                heteronym: false,
            },
            vec![
                vec!["o1ng".to_string()],
                vec!["uo2".to_string()],
                vec!["e2n".to_string()],
            ],
            vec!["o1ng".to_string(), "uo2".to_string(), "e2n".to_string()],
        ),
        TestCase::new(
            "中国人".to_string(),
            pinyin::Args {
                style: pinyin::Style::Normal,
                heteronym: true,
            },
            vec![
                vec!["zhong".to_string(), "zhong".to_string()],
                vec!["guo".to_string()],
                vec!["ren".to_string()],
            ],
            vec!["zhong".to_string(), "guo".to_string(), "ren".to_string()],
        ),
    ];
    for data in &test_data {
        assert_eq!(data.result, pinyin::pinyin(&data.hans, &data.args));
        assert_eq!(
            data.lazy_result,
            pinyin::lazy_pinyin(&data.hans, &data.args)
        );
    }
}

#[test]
fn test_non_chinese_pinyin() {
    let hans = "中国人abc你好";
    let expect = vec![
        vec!["zhong".to_string()],
        vec!["guo".to_string()],
        vec!["ren".to_string()],
        vec![],
        vec![],
        vec![],
        vec!["ni".to_string()],
        vec!["hao".to_string()],
    ];
    let result = pinyin::pinyin(hans, &pinyin::Args::new());
    assert_eq!(expect, result);
}

#[test]
fn test_non_chinese_lazy_pinyin() {
    let hans = "中国人abc你好";
    let expect = vec![
        "zhong".to_string(),
        "guo".to_string(),
        "ren".to_string(),
        "ni".to_string(),
        "hao".to_string(),
    ];
    let result = pinyin::lazy_pinyin(hans, &pinyin::Args::new());
    assert_eq!(expect, result);
}

#[test]
fn test_new_args() {
    let args = pinyin::Args::new();
    assert_eq!(pinyin::Style::Normal, args.style);
    assert_eq!(false, args.heteronym);
}

#[test]
fn test_no_initial() {
    let hans = "安";
    let expect = vec!["an".to_string()];
    let result = pinyin::lazy_pinyin(hans, &pinyin::Args::new());
    assert_eq!(expect, result);
}

#[test]
fn test_no_phonetic_symbol() {
    let hans = "啊";
    let expect = vec!["a".to_string()];
    let result = pinyin::lazy_pinyin(hans, &pinyin::Args::new());
    assert_eq!(expect, result);
}
<commit_msg>test pinyin::Args::default()<commit_after>extern crate pinyin;

struct TestCase {
    hans: String,
    args: pinyin::Args,
    result: Vec<Vec<String>>,
    lazy_result: Vec<String>,
}

impl TestCase {
    pub fn new(
        hans: String,
        args: pinyin::Args,
        result: Vec<Vec<String>>,
        lazy_result: Vec<String>,
    ) -> TestCase {
        TestCase {
            hans,
            args,
            result,
            lazy_result,
        }
    }
}

#[test]
fn test_pinyin() {
    let test_data = vec![
        TestCase::new(
            "中国人".to_string(),
            pinyin::Args::new(),
            vec![
                vec!["zhong".to_string()],
                vec!["guo".to_string()],
                vec!["ren".to_string()],
            ],
            vec!["zhong".to_string(), "guo".to_string(), "ren".to_string()],
        ),
        TestCase::new(
            "中国人".to_string(),
            pinyin::Args {
                style: pinyin::Style::Normal,
                heteronym: false,
            },
            vec![
                vec!["zhong".to_string()],
                vec!["guo".to_string()],
                vec!["ren".to_string()],
            ],
            vec!["zhong".to_string(), "guo".to_string(), "ren".to_string()],
        ),
        TestCase::new(
            "中国人".to_string(),
            pinyin::Args {
                style: pinyin::Style::Tone,
                heteronym: false,
            },
            vec![
                vec!["zhōng".to_string()],
                vec!["guó".to_string()],
                vec!["rén".to_string()],
            ],
            vec!["zhōng".to_string(), "guó".to_string(), "rén".to_string()],
        ),
        TestCase::new(
            "中国人".to_string(),
            pinyin::Args {
                style: pinyin::Style::Tone2,
                heteronym: false,
            },
            vec![
                vec!["zho1ng".to_string()],
                vec!["guo2".to_string()],
                vec!["re2n".to_string()],
            ],
            vec!["zho1ng".to_string(), "guo2".to_string(), "re2n".to_string()],
        ),
        TestCase::new(
            "中国人".to_string(),
            pinyin::Args {
                style: pinyin::Style::Initials,
                heteronym: false,
            },
            vec![
                vec!["zh".to_string()],
                vec!["g".to_string()],
                vec!["r".to_string()],
            ],
            vec!["zh".to_string(), "g".to_string(), "r".to_string()],
        ),
        TestCase::new(
            "中国人".to_string(),
            pinyin::Args {
                style: pinyin::Style::FirstLetter,
                heteronym: false,
            },
            vec![
                vec!["z".to_string()],
                vec!["g".to_string()],
                vec!["r".to_string()],
            ],
            vec!["z".to_string(), "g".to_string(), "r".to_string()],
        ),
        TestCase::new(
            "中国人".to_string(),
            pinyin::Args {
                style: pinyin::Style::Finals,
                heteronym: false,
            },
            vec![
                vec!["ong".to_string()],
                vec!["uo".to_string()],
                vec!["en".to_string()],
            ],
            vec!["ong".to_string(), "uo".to_string(), "en".to_string()],
        ),
        TestCase::new(
            "中国人".to_string(),
            pinyin::Args {
                style: pinyin::Style::FinalsTone,
                heteronym: false,
            },
            vec![
                vec!["ōng".to_string()],
                vec!["uó".to_string()],
                vec!["én".to_string()],
            ],
            vec!["ōng".to_string(), "uó".to_string(), "én".to_string()],
        ),
        TestCase::new(
            "中国人".to_string(),
            pinyin::Args {
                style: pinyin::Style::FinalsTone2,
                heteronym: false,
            },
            vec![
                vec!["o1ng".to_string()],
                vec!["uo2".to_string()],
                vec!["e2n".to_string()],
            ],
            vec!["o1ng".to_string(), "uo2".to_string(), "e2n".to_string()],
        ),
        TestCase::new(
            "中国人".to_string(),
            pinyin::Args {
                style: pinyin::Style::Normal,
                heteronym: true,
            },
            vec![
                vec!["zhong".to_string(), "zhong".to_string()],
                vec!["guo".to_string()],
                vec!["ren".to_string()],
            ],
            vec!["zhong".to_string(), "guo".to_string(), "ren".to_string()],
        ),
    ];
    for data in &test_data {
        assert_eq!(data.result, pinyin::pinyin(&data.hans, &data.args));
        assert_eq!(
            data.lazy_result,
            pinyin::lazy_pinyin(&data.hans, &data.args)
        );
    }
}

#[test]
fn test_non_chinese_pinyin() {
    let hans = "中国人abc你好";
    let expect = vec![
        vec!["zhong".to_string()],
        vec!["guo".to_string()],
        vec!["ren".to_string()],
        vec![],
        vec![],
        vec![],
        vec!["ni".to_string()],
        vec!["hao".to_string()],
    ];
    let result = pinyin::pinyin(hans, &pinyin::Args::new());
    assert_eq!(expect, result);
}

#[test]
fn test_non_chinese_lazy_pinyin() {
    let hans = "中国人abc你好";
    let expect = vec![
        "zhong".to_string(),
        "guo".to_string(),
        "ren".to_string(),
        "ni".to_string(),
        "hao".to_string(),
    ];
    let result = pinyin::lazy_pinyin(hans, &pinyin::Args::new());
    assert_eq!(expect, result);
}

#[test]
fn test_new_args() {
    let args = pinyin::Args::new();
    assert_eq!(pinyin::Style::Normal, args.style);
    assert_eq!(false, args.heteronym);
}

#[test]
fn test_default_args() {
    let args = pinyin::Args::default();
    assert_eq!(pinyin::Style::Normal, args.style);
    assert_eq!(false, args.heteronym);
}

#[test]
fn test_no_initial() {
    let hans = "安";
    let expect = vec!["an".to_string()];
    let result = pinyin::lazy_pinyin(hans, &pinyin::Args::new());
    assert_eq!(expect, result);
}

#[test]
fn test_no_phonetic_symbol() {
    let hans = "啊";
    let expect = vec!["a".to_string()];
    let result = pinyin::lazy_pinyin(hans, &pinyin::Args::new());
    assert_eq!(expect, result);
}
<|endoftext|>


<commit_before>extern crate tinyexpr;

#[test]
fn check_basics() {
    assert_eq!(tinyexpr::interp("2*2").unwrap(), 4.0);
    assert_eq!(tinyexpr::interp("2+2").unwrap(), 4.0);
    assert_eq!(tinyexpr::interp("5%2").unwrap(), 1.0);
    assert_eq!(tinyexpr::interp("2+2*2").unwrap(), 6.0);
    assert_eq!(tinyexpr::interp("(2+2)*2").unwrap(), 8.0);
    assert_eq!(tinyexpr::interp("(2+2)*2/2").unwrap(), 4.0);
    assert_eq!(tinyexpr::interp("abs(-1)").unwrap(), 1.0);
    assert_eq!(tinyexpr::interp("sqrt(728*728)").unwrap(), 728.0);
    assert_eq!(tinyexpr::interp("pow(2.0, 3.0)").unwrap(), 8.0);
    assert_eq!(tinyexpr::interp("exp(1)").unwrap(), tinyexpr::interp("e").unwrap());
    assert_eq!(tinyexpr::interp("floor(3.1415)").unwrap(), 3.0);
    assert_eq!(tinyexpr::interp("ceil(3.1415)*floor(3.1415)").unwrap(), 12.0);
}

#[test]
fn check_constants() {
    assert_eq!(tinyexpr::interp("pi").unwrap(), 3.141592653589793);
    assert_eq!(tinyexpr::interp("e").unwrap(), 2.718281828459045);
}

#[test]
fn check_logarithms() {
    assert_eq!(tinyexpr::interp("ln(e)").unwrap(), 1.0);
    assert_eq!(tinyexpr::interp("log(10)").unwrap(), 1.0);
    assert_eq!(tinyexpr::interp("log10(10)").unwrap(), 1.0);
}

#[test]
fn check_trigs() {
    assert_eq!(tinyexpr::interp("2*1/sin(3.14/2)").unwrap().round(), 2.0);
    assert_eq!(tinyexpr::interp("asin(1)").unwrap(), tinyexpr::interp("pi/2").unwrap());
    assert_eq!(tinyexpr::interp("tan(pi)").unwrap().round(), 0.0);
    assert_eq!(tinyexpr::interp("atan(pi/2)").unwrap().round(), 1.0);
    assert_eq!(tinyexpr::interp("atan2(pi, 2)").unwrap().round(), 1.0);
    assert_eq!(tinyexpr::interp("cos(0)").unwrap().round(), 1.0);
    assert_eq!(tinyexpr::interp("acos(1)").unwrap(), 0.0);
}

#[test]
fn check_hyberbolic_trigs() {
    assert_eq!(tinyexpr::interp("sinh(0)").unwrap(), 0.0);
    assert_eq!(tinyexpr::interp("cosh(0)").unwrap(), 1.0);
    assert_eq!(tinyexpr::interp("tanh(10000)").unwrap(), 1.0);
}

#[test]
#[should_panic]
fn parse_error()
{
    tinyexpr::interp("atan(foo)").unwrap();
}
<commit_msg>added sub to tests<commit_after>extern crate tinyexpr;

#[test]
fn check_basics() {
    assert_eq!(tinyexpr::interp("2*2").unwrap(), 4.0);
    assert_eq!(tinyexpr::interp("2+2").unwrap(), 4.0);
    assert_eq!(tinyexpr::interp("3-2").unwrap(), 1.0);
    assert_eq!(tinyexpr::interp("5%2").unwrap(), 1.0);
    assert_eq!(tinyexpr::interp("2+2*2").unwrap(), 6.0);
    assert_eq!(tinyexpr::interp("(2+2)*2").unwrap(), 8.0);
    assert_eq!(tinyexpr::interp("(2+2)*2/2").unwrap(), 4.0);
    assert_eq!(tinyexpr::interp("abs(-1)").unwrap(), 1.0);
    assert_eq!(tinyexpr::interp("sqrt(728*728)").unwrap(), 728.0);
    assert_eq!(tinyexpr::interp("pow(2.0, 3.0)").unwrap(), 8.0);
    assert_eq!(tinyexpr::interp("exp(1)").unwrap(), tinyexpr::interp("e").unwrap());
    assert_eq!(tinyexpr::interp("floor(3.1415)").unwrap(), 3.0);
    assert_eq!(tinyexpr::interp("ceil(3.1415)*floor(3.1415)").unwrap(), 12.0);
}

#[test]
fn check_constants() {
    assert_eq!(tinyexpr::interp("pi").unwrap(), 3.141592653589793);
    assert_eq!(tinyexpr::interp("e").unwrap(), 2.718281828459045);
}

#[test]
fn check_logarithms() {
    assert_eq!(tinyexpr::interp("ln(e)").unwrap(), 1.0);
    assert_eq!(tinyexpr::interp("log(10)").unwrap(), 1.0);
    assert_eq!(tinyexpr::interp("log10(10)").unwrap(), 1.0);
}

#[test]
fn check_trigs() {
    assert_eq!(tinyexpr::interp("2*1/sin(3.14/2)").unwrap().round(), 2.0);
    assert_eq!(tinyexpr::interp("asin(1)").unwrap(), tinyexpr::interp("pi/2").unwrap());
    assert_eq!(tinyexpr::interp("tan(pi)").unwrap().round(), 0.0);
    assert_eq!(tinyexpr::interp("atan(pi/2)").unwrap().round(), 1.0);
    assert_eq!(tinyexpr::interp("atan2(pi, 2)").unwrap().round(), 1.0);
    assert_eq!(tinyexpr::interp("cos(0)").unwrap().round(), 1.0);
    assert_eq!(tinyexpr::interp("acos(1)").unwrap(), 0.0);
}

#[test]
fn check_hyberbolic_trigs() {
    assert_eq!(tinyexpr::interp("sinh(0)").unwrap(), 0.0);
    assert_eq!(tinyexpr::interp("cosh(0)").unwrap(), 1.0);
    assert_eq!(tinyexpr::interp("tanh(10000)").unwrap(), 1.0);
}

#[test]
#[should_panic]
fn parse_error()
{
    tinyexpr::interp("atan(foo)").unwrap();
}
<|endoftext|>


<commit_before>use std::io;
use std::convert::TryFrom;
use byteorder::{ReadBytesExt, WriteBytesExt};
use super::{TryFromError, ReadError, ReadFrom, WriteTo};

#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum MessageType {
    Ping,
    Pong,
    AddEntry,
    EntryAdded,
    RemoveEntry,
    EntryRemoved,
    #[doc(hidden)]
    __NonExhaustive,
}

impl MessageType {
    pub fn is_command(self) -> bool {
        match self {
            MessageType::Ping | MessageType::AddEntry | MessageType::RemoveEntry => true,
            _ => false
        }
    }
}

impl ReadFrom for MessageType {
    fn read_from<R: io::Read>(source: &mut R) -> Result<Self, ReadError> {
        let value = source.read_u8()?;

        Ok(Self::try_from(value)?)
    }
}

impl WriteTo for MessageType {
    fn write_to<W: io::Write>(&self, target: &mut W) -> io::Result<()> {
        target.write_u8((*self).into())
    }
}

impl Into<u8> for MessageType {
    fn into(self) -> u8 {
        match self {
            MessageType::Ping => 0,
            MessageType::Pong => 1,
            MessageType::AddEntry => 2,
            MessageType::EntryAdded => 3,
            MessageType::RemoveEntry => 4,
            MessageType::EntryRemoved => 5,
            _ => panic!("Invalid Message type"),
        }
    }
}

impl TryFrom<u8> for MessageType {
    type Error = TryFromError;

    fn try_from(value: u8) -> Result<Self, Self::Error> {
        match value {
            0 => Ok(MessageType::Ping),
            1 => Ok(MessageType::Pong),
            2 => Ok(MessageType::AddEntry),
            3 => Ok(MessageType::EntryAdded),
            4 => Ok(MessageType::RemoveEntry),
            5 => Ok(MessageType::EntryRemoved),
            _ => Err(TryFromError::InvalidValue)
        }
    }
}

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

    macro_rules! test_message_type {
        ($test:ident, $msg:expr, $value:expr, $command:expr) => {
            #[test]
            fn $test() {
                let mut buf = vec![];
                assert!($msg.write_to(&mut buf).is_ok());
                assert_eq!(vec![$value], buf);

                assert_eq!($value as u8, $msg.into());
                assert_eq!($msg, MessageType::try_from($value as u8).unwrap());
                assert_eq!($command, $msg.is_command());

                assert_eq!($msg, MessageType::read_from(&mut [$value as u8].as_ref()).unwrap());
            }
        }
    }

    test_message_type!(test_ping, MessageType::Ping, 0, true);
    test_message_type!(test_pong, MessageType::Pong, 1, false);
    test_message_type!(test_add_entry, MessageType::AddEntry, 2, true);
    test_message_type!(test_entry_added, MessageType::EntryAdded, 3, false);
    test_message_type!(test_remove_entry, MessageType::RemoveEntry, 4, true);
    test_message_type!(test_entry_removed, MessageType::EntryRemoved, 5, false);
}<commit_msg>Add MessageType::EntryExpired<commit_after>use std::io;
use std::convert::TryFrom;
use byteorder::{ReadBytesExt, WriteBytesExt};
use super::{TryFromError, ReadError, ReadFrom, WriteTo};

#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum MessageType {
    Ping,
    Pong,
    AddEntry,
    EntryAdded,
    RemoveEntry,
    EntryRemoved,
    EntryExpired,
    #[doc(hidden)]
    __NonExhaustive,
}

impl MessageType {
    pub fn is_command(self) -> bool {
        match self {
            MessageType::Ping | MessageType::AddEntry | MessageType::RemoveEntry => true,
            _ => false
        }
    }
}

impl ReadFrom for MessageType {
    fn read_from<R: io::Read>(source: &mut R) -> Result<Self, ReadError> {
        let value = source.read_u8()?;

        Ok(Self::try_from(value)?)
    }
}

impl WriteTo for MessageType {
    fn write_to<W: io::Write>(&self, target: &mut W) -> io::Result<()> {
        target.write_u8((*self).into())
    }
}

impl Into<u8> for MessageType {
    fn into(self) -> u8 {
        match self {
            MessageType::Ping => 0,
            MessageType::Pong => 1,
            MessageType::AddEntry => 2,
            MessageType::EntryAdded => 3,
            MessageType::RemoveEntry => 4,
            MessageType::EntryRemoved => 5,
            MessageType::EntryExpired => 6,
            _ => panic!("Invalid Message type"),
        }
    }
}

impl TryFrom<u8> for MessageType {
    type Error = TryFromError;

    fn try_from(value: u8) -> Result<Self, Self::Error> {
        match value {
            0 => Ok(MessageType::Ping),
            1 => Ok(MessageType::Pong),
            2 => Ok(MessageType::AddEntry),
            3 => Ok(MessageType::EntryAdded),
            4 => Ok(MessageType::RemoveEntry),
            5 => Ok(MessageType::EntryRemoved),
            6 => Ok(MessageType::EntryExpired),
            _ => Err(TryFromError::InvalidValue),
        }
    }
}

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

    macro_rules! test_message_type {
        ($test:ident, $msg:expr, $value:expr, $command:expr) => {
            #[test]
            fn $test() {
                let mut buf = vec![];
                assert!($msg.write_to(&mut buf).is_ok());
                assert_eq!(vec![$value], buf);

                assert_eq!($value as u8, $msg.into());
                assert_eq!($msg, MessageType::try_from($value as u8).unwrap());
                assert_eq!($command, $msg.is_command());

                assert_eq!($msg, MessageType::read_from(&mut [$value as u8].as_ref()).unwrap());
            }
        }
    }

    test_message_type!(test_ping, MessageType::Ping, 0, true);
    test_message_type!(test_pong, MessageType::Pong, 1, false);
    test_message_type!(test_add_entry, MessageType::AddEntry, 2, true);
    test_message_type!(test_entry_added, MessageType::EntryAdded, 3, false);
    test_message_type!(test_remove_entry, MessageType::RemoveEntry, 4, true);
    test_message_type!(test_entry_removed, MessageType::EntryRemoved, 5, false);
    test_message_type!(test_entry_expired, MessageType::EntryExpired, 6, false);
}<|endoftext|>


<commit_before>// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
use std::convert::TryInto;

use seccomp::{
    allow_syscall, allow_syscall_if, BpfProgram, Error, SeccompAction, SeccompCmpArgLen as ArgLen,
    SeccompCmpOp::Eq, SeccompCondition as Cond, SeccompError, SeccompFilter, SeccompLevel,
    SeccompRule,
};
use utils::signal::sigrtmin;

/// The default filter containing the white listed syscall rules required by `Firecracker` to
/// function.
/// Any non-trivial modification to this allow list needs a proper comment to specify its source
/// or why the sycall/condition is needed.
pub fn default_filter() -> Result<SeccompFilter, Error> {
    Ok(SeccompFilter::new(
        vec![
            // Called by the api thread to receive data on socket
            allow_syscall_if(
                libc::SYS_accept4,
                or![and![Cond::new(
                    3,
                    ArgLen::DWORD,
                    Eq,
                    libc::SOCK_CLOEXEC as u64
                )?],],
            ),
            // Called for expanding the heap
            allow_syscall(libc::SYS_brk),
            // Used for metrics and logging, via the helpers in utils/src/time.rs
            // It's not called on some platforms, because of vdso optimisations. In those cases,
            // musl falls back to the regular syscall.
            allow_syscall(libc::SYS_clock_gettime),
            allow_syscall(libc::SYS_close),
            // Needed for vsock
            allow_syscall(libc::SYS_connect),
            allow_syscall(libc::SYS_epoll_ctl),
            allow_syscall(libc::SYS_epoll_pwait),
            #[cfg(all(target_env = "gnu", target_arch = "x86_64"))]
            allow_syscall(libc::SYS_epoll_wait),
            allow_syscall(libc::SYS_exit),
            allow_syscall(libc::SYS_exit_group),
            // Used by snapshotting, drive patching and rescanning
            allow_syscall_if(
                libc::SYS_fcntl,
                or![and![
                    Cond::new(1, ArgLen::DWORD, Eq, super::FCNTL_F_SETFD)?,
                    Cond::new(2, ArgLen::DWORD, Eq, super::FCNTL_FD_CLOEXEC)?,
                ],],
            ),
            // Used for drive patching & rescanning, for reading the local timezone
            allow_syscall(libc::SYS_fstat),
            // Used for snapshotting
            allow_syscall(libc::SYS_ftruncate),
            // Used for synchronization
            allow_syscall_if(
                libc::SYS_futex,
                or![
                    and![Cond::new(1, ArgLen::DWORD, Eq, super::FUTEX_WAIT_PRIVATE)?],
                    and![Cond::new(1, ArgLen::DWORD, Eq, super::FUTEX_WAKE_PRIVATE)?],
                    #[cfg(target_env = "gnu")]
                    and![Cond::new(
                        1,
                        ArgLen::DWORD,
                        Eq,
                        super::FUTEX_CMP_REQUEUE_PRIVATE
                    )?],
                ],
            ),
            // Used by glibc's tgkill
            #[cfg(target_env = "gnu")]
            allow_syscall(libc::SYS_getpid),
            allow_syscall_if(libc::SYS_ioctl, super::create_ioctl_seccomp_rule()?),
            // Used by the block device
            allow_syscall(libc::SYS_lseek),
            // Triggered by musl for some customer workloads
            #[cfg(target_env = "musl")]
            allow_syscall_if(
                libc::SYS_madvise,
                or![and![Cond::new(
                    2,
                    ArgLen::DWORD,
                    Eq,
                    libc::MADV_DONTNEED as u64
                )?],],
            ),
            // Used for re-allocating large memory regions, for example vectors
            allow_syscall(libc::SYS_mremap),
            // Used for freeing memory
            allow_syscall(libc::SYS_munmap),
            // Used for reading the timezone in LocalTime::now()
            allow_syscall_if(
                libc::SYS_mmap,
                or![
                    and![Cond::new(3, ArgLen::DWORD, Eq, libc::MAP_SHARED as u64)?],
                    and![Cond::new(
                        3,
                        ArgLen::DWORD,
                        Eq,
                        (libc::MAP_FIXED | libc::MAP_ANONYMOUS | libc::MAP_PRIVATE) as u64
                    )?],
                ],
            ),
            #[cfg(target_arch = "x86_64")]
            allow_syscall(libc::SYS_open),
            #[cfg(target_arch = "aarch64")]
            allow_syscall(libc::SYS_openat),
            allow_syscall(libc::SYS_read),
            // Used by the API thread and vsock
            allow_syscall(libc::SYS_recvfrom),
            // SYS_rt_sigreturn is needed in case a fault does occur, so that the signal handler
            // can return. Otherwise we get stuck in a fault loop.
            allow_syscall(libc::SYS_rt_sigreturn),
            // Used by the API thread and vsock
            allow_syscall_if(
                libc::SYS_socket,
                or![and![
                    Cond::new(0, ArgLen::DWORD, Eq, libc::AF_UNIX as u64)?,
                    Cond::new(
                        1,
                        ArgLen::DWORD,
                        Eq,
                        (libc::SOCK_STREAM as u64) | (libc::SOCK_CLOEXEC as u64)
                    )?,
                    Cond::new(2, ArgLen::DWORD, Eq, 0u64)?
                ],],
            ),
            // Used to kick vcpus
            allow_syscall_if(
                libc::SYS_tkill,
                or![and![Cond::new(
                    1,
                    ArgLen::DWORD,
                    Eq,
                    (sigrtmin() + super::super::vstate::vcpu::VCPU_RTSIG_OFFSET) as u64
                )?]],
            ),
            // Used to kick vcpus, on gnu
            #[cfg(target_env = "gnu")]
            allow_syscall(libc::SYS_tgkill),
            // Needed for rate limiting
            allow_syscall_if(
                libc::SYS_timerfd_create,
                or![and![
                    Cond::new(0, ArgLen::DWORD, Eq, libc::CLOCK_MONOTONIC as u64)?,
                    Cond::new(
                        1,
                        ArgLen::DWORD,
                        Eq,
                        (libc::TFD_CLOEXEC as u64) | (libc::TFD_NONBLOCK as u64)
                    )?,
                ],],
            ),
            // Needed for rate limiting
            allow_syscall_if(
                libc::SYS_timerfd_settime,
                or![and![Cond::new(1, ArgLen::DWORD, Eq, 0u64)?],],
            ),
            allow_syscall(libc::SYS_write),
        ]
        .into_iter()
        .collect(),
        SeccompAction::Trap,
    )?)
}

/// Generate a BPF program based on a seccomp level value.
pub fn get_seccomp_filter(seccomp_level: SeccompLevel) -> Result<BpfProgram, SeccompError> {
    match seccomp_level {
        SeccompLevel::None => Ok(vec![]),
        SeccompLevel::Basic => default_filter()
            .map(|filter| filter.allow_all())
            .and_then(|filter| filter.try_into())
            .map_err(SeccompError::SeccompFilter),
        SeccompLevel::Advanced => default_filter()
            .and_then(|filter| filter.try_into())
            .map_err(SeccompError::SeccompFilter),
    }
}

#[cfg(test)]
mod tests {
    use super::get_seccomp_filter;
    use seccomp::SeccompLevel;

    #[test]
    fn test_get_seccomp_filter() {
        assert!(get_seccomp_filter(SeccompLevel::None).is_ok());
        assert!(get_seccomp_filter(SeccompLevel::Basic).is_ok());
        assert!(get_seccomp_filter(SeccompLevel::Advanced).is_ok());
    }
}
<commit_msg>Add comments for mmap in allowlist<commit_after>// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
use std::convert::TryInto;

use seccomp::{
    allow_syscall, allow_syscall_if, BpfProgram, Error, SeccompAction, SeccompCmpArgLen as ArgLen,
    SeccompCmpOp::Eq, SeccompCondition as Cond, SeccompError, SeccompFilter, SeccompLevel,
    SeccompRule,
};
use utils::signal::sigrtmin;

/// The default filter containing the white listed syscall rules required by `Firecracker` to
/// function.
/// Any non-trivial modification to this allow list needs a proper comment to specify its source
/// or why the sycall/condition is needed.
pub fn default_filter() -> Result<SeccompFilter, Error> {
    Ok(SeccompFilter::new(
        vec![
            // Called by the api thread to receive data on socket
            allow_syscall_if(
                libc::SYS_accept4,
                or![and![Cond::new(
                    3,
                    ArgLen::DWORD,
                    Eq,
                    libc::SOCK_CLOEXEC as u64
                )?],],
            ),
            // Called for expanding the heap
            allow_syscall(libc::SYS_brk),
            // Used for metrics and logging, via the helpers in utils/src/time.rs
            // It's not called on some platforms, because of vdso optimisations. In those cases,
            // musl falls back to the regular syscall.
            allow_syscall(libc::SYS_clock_gettime),
            allow_syscall(libc::SYS_close),
            // Needed for vsock
            allow_syscall(libc::SYS_connect),
            allow_syscall(libc::SYS_epoll_ctl),
            allow_syscall(libc::SYS_epoll_pwait),
            #[cfg(all(target_env = "gnu", target_arch = "x86_64"))]
            allow_syscall(libc::SYS_epoll_wait),
            allow_syscall(libc::SYS_exit),
            allow_syscall(libc::SYS_exit_group),
            // Used by snapshotting, drive patching and rescanning
            allow_syscall_if(
                libc::SYS_fcntl,
                or![and![
                    Cond::new(1, ArgLen::DWORD, Eq, super::FCNTL_F_SETFD)?,
                    Cond::new(2, ArgLen::DWORD, Eq, super::FCNTL_FD_CLOEXEC)?,
                ],],
            ),
            // Used for drive patching & rescanning, for reading the local timezone
            allow_syscall(libc::SYS_fstat),
            // Used for snapshotting
            allow_syscall(libc::SYS_ftruncate),
            // Used for synchronization
            allow_syscall_if(
                libc::SYS_futex,
                or![
                    and![Cond::new(1, ArgLen::DWORD, Eq, super::FUTEX_WAIT_PRIVATE)?],
                    and![Cond::new(1, ArgLen::DWORD, Eq, super::FUTEX_WAKE_PRIVATE)?],
                    #[cfg(target_env = "gnu")]
                    and![Cond::new(
                        1,
                        ArgLen::DWORD,
                        Eq,
                        super::FUTEX_CMP_REQUEUE_PRIVATE
                    )?],
                ],
            ),
            // Used by glibc's tgkill
            #[cfg(target_env = "gnu")]
            allow_syscall(libc::SYS_getpid),
            allow_syscall_if(libc::SYS_ioctl, super::create_ioctl_seccomp_rule()?),
            // Used by the block device
            allow_syscall(libc::SYS_lseek),
            // Triggered by musl for some customer workloads
            #[cfg(target_env = "musl")]
            allow_syscall_if(
                libc::SYS_madvise,
                or![and![Cond::new(
                    2,
                    ArgLen::DWORD,
                    Eq,
                    libc::MADV_DONTNEED as u64
                )?],],
            ),
            // Used for re-allocating large memory regions, for example vectors
            allow_syscall(libc::SYS_mremap),
            // Used for freeing memory
            allow_syscall(libc::SYS_munmap),
            allow_syscall_if(
                libc::SYS_mmap,
                or![
                    // Used for reading the timezone in LocalTime::now()
                    and![Cond::new(3, ArgLen::DWORD, Eq, libc::MAP_SHARED as u64)?],
                    // Used by the balloon device
                    and![Cond::new(
                        3,
                        ArgLen::DWORD,
                        Eq,
                        (libc::MAP_FIXED | libc::MAP_ANONYMOUS | libc::MAP_PRIVATE) as u64
                    )?],
                ],
            ),
            #[cfg(target_arch = "x86_64")]
            allow_syscall(libc::SYS_open),
            #[cfg(target_arch = "aarch64")]
            allow_syscall(libc::SYS_openat),
            allow_syscall(libc::SYS_read),
            // Used by the API thread and vsock
            allow_syscall(libc::SYS_recvfrom),
            // SYS_rt_sigreturn is needed in case a fault does occur, so that the signal handler
            // can return. Otherwise we get stuck in a fault loop.
            allow_syscall(libc::SYS_rt_sigreturn),
            // Used by the API thread and vsock
            allow_syscall_if(
                libc::SYS_socket,
                or![and![
                    Cond::new(0, ArgLen::DWORD, Eq, libc::AF_UNIX as u64)?,
                    Cond::new(
                        1,
                        ArgLen::DWORD,
                        Eq,
                        (libc::SOCK_STREAM as u64) | (libc::SOCK_CLOEXEC as u64)
                    )?,
                    Cond::new(2, ArgLen::DWORD, Eq, 0u64)?
                ],],
            ),
            // Used to kick vcpus
            allow_syscall_if(
                libc::SYS_tkill,
                or![and![Cond::new(
                    1,
                    ArgLen::DWORD,
                    Eq,
                    (sigrtmin() + super::super::vstate::vcpu::VCPU_RTSIG_OFFSET) as u64
                )?]],
            ),
            // Used to kick vcpus, on gnu
            #[cfg(target_env = "gnu")]
            allow_syscall(libc::SYS_tgkill),
            // Needed for rate limiting
            allow_syscall_if(
                libc::SYS_timerfd_create,
                or![and![
                    Cond::new(0, ArgLen::DWORD, Eq, libc::CLOCK_MONOTONIC as u64)?,
                    Cond::new(
                        1,
                        ArgLen::DWORD,
                        Eq,
                        (libc::TFD_CLOEXEC as u64) | (libc::TFD_NONBLOCK as u64)
                    )?,
                ],],
            ),
            // Needed for rate limiting
            allow_syscall_if(
                libc::SYS_timerfd_settime,
                or![and![Cond::new(1, ArgLen::DWORD, Eq, 0u64)?],],
            ),
            allow_syscall(libc::SYS_write),
        ]
        .into_iter()
        .collect(),
        SeccompAction::Trap,
    )?)
}

/// Generate a BPF program based on a seccomp level value.
pub fn get_seccomp_filter(seccomp_level: SeccompLevel) -> Result<BpfProgram, SeccompError> {
    match seccomp_level {
        SeccompLevel::None => Ok(vec![]),
        SeccompLevel::Basic => default_filter()
            .map(|filter| filter.allow_all())
            .and_then(|filter| filter.try_into())
            .map_err(SeccompError::SeccompFilter),
        SeccompLevel::Advanced => default_filter()
            .and_then(|filter| filter.try_into())
            .map_err(SeccompError::SeccompFilter),
    }
}

#[cfg(test)]
mod tests {
    use super::get_seccomp_filter;
    use seccomp::SeccompLevel;

    #[test]
    fn test_get_seccomp_filter() {
        assert!(get_seccomp_filter(SeccompLevel::None).is_ok());
        assert!(get_seccomp_filter(SeccompLevel::Basic).is_ok());
        assert!(get_seccomp_filter(SeccompLevel::Advanced).is_ok());
    }
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::{mem, time};
use torrent::{Info, Peer, Bitfield};
use control::cio;

mod rarest;
mod sequential;

#[cfg(test)]
mod tests;

#[derive(Clone, Debug)]
pub struct Picker {
    /// Number of blocks per piece
    scale: u32,
    /// Number of detected seeders
    seeders: u16,
    /// Set of pieces which have blocks waiting. These should be prioritized.
    downloading: HashMap<u32, Vec<Downloading>>,
    /// Bitfield of unpicked pieces, not in progress or
    /// completed yet. A set bit is picked, unset is unpicked.
    unpicked: Bitfield,
    /// The current picker in use
    picker: PickerKind,
}

#[derive(Clone, Debug, PartialEq)]
pub struct Block {
    pub index: u32,
    pub offset: u32,
}

/// Pickers act solely as piece picking algorithm.
/// They will select the optimal next piece for a peer,
/// and can be told when a piece is complete(or invalid).
#[derive(Clone, Debug)]
enum PickerKind {
    Rarest(rarest::Picker),
    Sequential(sequential::Picker),
}

/// A downloading block and the peers it has been
/// requested from.
#[derive(Clone, Debug)]
struct Downloading {
    offset: u32,
    completed: bool,
    requested: Vec<Request>,
}

/// A request to a peer and the time it was initiated.
#[derive(Clone, Debug)]
struct Request {
    peer: usize,
    requested_at: time::Instant,
}

const MAX_DUP_REQS: usize = 3;

impl Picker {
    /// Creates a new picker, which will select over
    /// the given pieces. The algorithm used for selection
    /// will vary based on the current swarm state, but
    /// will default to rarest first.
    pub fn new(info: &Info, pieces: &Bitfield) -> Picker {
        let scale = info.piece_len / 16_384;
        let picker = rarest::Picker::new(pieces);
        Picker {
            picker: PickerKind::Rarest(picker),
            scale,
            seeders: 0,
            unpicked: pieces.clone(),
            downloading: HashMap::new(),
        }
    }

    /// Returns true if the current picker algorithm is sequential
    pub fn is_sequential(&self) -> bool {
        match self.picker {
            PickerKind::Sequential(_) => true,
            _ => false,
        }
    }

    /// Attempts to select a block for a peer.
    pub fn pick<T: cio::CIO>(&mut self, peer: &Peer<T>) -> Option<Block> {
        if let Some(b) = self.pick_expired(peer) {
            return Some(b);
        }

        let piece = match self.picker {
            PickerKind::Sequential(ref mut p) => p.pick(peer),
            PickerKind::Rarest(ref mut p) => p.pick(peer),
        };
        piece.and_then(|p| self.pick_piece(p, peer.id())).or_else(
            || {
                self.pick_downloading(peer)
            },
        )
    }

    /// Attempts to pick an expired block
    fn pick_expired<T: cio::CIO>(&mut self, peer: &Peer<T>) -> Option<Block> {
        // TODO: Use some form of heuristic here to say "we expect to have
        // downloaded some pieces by X, hit the picker with a tick which checks
        // that, flags shit as invalid, and then does a double request
        None
    }

    /// Picks a block from a given piece for a peer
    fn pick_piece(&mut self, piece: u32, id: usize) -> Option<Block> {
        self.downloading.entry(piece).or_insert_with(|| vec![]);
        let dl = self.downloading.get_mut(&piece).unwrap();
        let offset = dl.len() as u32 * 16_384;
        dl.push(Downloading {
            offset,
            completed: false,
            requested: vec![Request::new(id)],
        });

        if dl.len() == self.scale as usize {
            match self.picker {
                PickerKind::Sequential(ref mut p) => p.completed(piece),
                PickerKind::Rarest(ref mut p) => p.completed(piece),
            }
            self.unpicked.set_bit(piece as u64);
        }
        Some(Block {
            index: piece,
            offset,
        })
    }

    /// Attempts to pick an already requested block
    fn pick_downloading<T: cio::CIO>(&mut self, peer: &Peer<T>) -> Option<Block> {
        for (idx, dl) in &mut self.downloading {
            if peer.pieces().has_bit(*idx as u64) {
                let r = dl.iter_mut()
                    .find(|r| !r.completed && r.requested.len() < MAX_DUP_REQS)
                    .map(|r| {
                        r.requested.push(Request::new(peer.id()));
                        Block::new(*idx, r.offset)
                    });
                if r.is_some() {
                    return r;
                }
            }
        }
        None
    }

    /// Marks a block as completed. Returns a result indicating if the block
    /// was actually requested, the success value containing a bool indicating
    /// if the block is complete, and a vector of peers from which the block
    /// was requested(for cancellation).
    pub fn completed(&mut self, b: Block) -> Result<(bool, Vec<usize>), ()> {
        // Find the block in our downloading blocks, mark as true,
        // and extract the current peer list for return.
        let res = self.downloading
            .get_mut(&b.index)
            .and_then(|dl| {
                dl.iter_mut().find(|r| r.offset == b.offset).map(
                    |r| r.complete(),
                )
            })
            .map(|r| r.into_iter().map(|e| e.peer).collect());

        // If we've requested every single block for this piece and they're all complete, remove it
        // and report completion
        let scale = self.scale;
        let complete = self.downloading
            .get_mut(&b.index)
            .map(|r| r.len() as u32 == scale && r.iter().all(|d| d.completed))
            .unwrap_or(false);

        if complete {
            self.downloading.remove(&b.index);
        }

        res.map(|r| (complete, r)).ok_or(())
    }

    /// Invalidates a piece
    pub fn invalidate_piece(&mut self, idx: u32) {
        match self.picker {
            PickerKind::Sequential(ref mut p) => p.incomplete(idx),
            PickerKind::Rarest(ref mut p) => p.incomplete(idx),
        }
        self.unpicked.unset_bit(idx as u64);
    }

    pub fn piece_available(&mut self, idx: u32) {
        if let PickerKind::Rarest(ref mut p) = self.picker {
            p.piece_available(idx);
        }
    }

    pub fn add_peer<T: cio::CIO>(&mut self, peer: &Peer<T>) {
        if peer.pieces().complete() {
            self.seeders += 1;
        }
        if let PickerKind::Rarest(ref mut p) = self.picker {
            p.add_peer(peer);
        }
    }

    pub fn remove_peer<T: cio::CIO>(&mut self, peer: &Peer<T>) {
        if let PickerKind::Rarest(ref mut p) = self.picker {
            p.remove_peer(peer);
        }
    }

    /// Alters the picker to sequential/non sequential. If changing
    /// from sequential to non sequential, peer state will need to be loaded
    /// after this.
    pub fn change_picker(&mut self, sequential: bool) {
        self.picker = if sequential {
            PickerKind::Sequential(sequential::Picker::new(&self.unpicked))
        } else {
            PickerKind::Rarest(rarest::Picker::new(&self.unpicked))
        };
        for i in self.downloading.keys() {
            if let PickerKind::Rarest(ref mut p) = self.picker {
                p.dec_avail(*i);
            }
        }
    }

    pub fn refresh_picker(&mut self, pieces: &Bitfield) {
        self.unpicked = pieces.clone();
        self.picker = if self.is_sequential() {
            PickerKind::Sequential(sequential::Picker::new(&self.unpicked))
        } else {
            PickerKind::Rarest(rarest::Picker::new(&self.unpicked))
        };
    }
}

#[cfg(test)]
impl Picker {
    pub fn new_rarest(info: &Info, pieces: &Bitfield) -> Picker {
        let scale = info.piece_len / 16_384;
        let picker = rarest::Picker::new(pieces);
        Picker {
            picker: PickerKind::Rarest(picker),
            scale,
            seeders: 0,
            unpicked: pieces.clone(),
            downloading: HashMap::new(),
        }
    }

    pub fn new_sequential(info: &Info, pieces: &Bitfield) -> Picker {
        let scale = info.piece_len / 16_384;
        let picker = sequential::Picker::new(pieces);
        Picker {
            picker: PickerKind::Sequential(picker),
            scale,
            seeders: 0,
            unpicked: pieces.clone(),
            downloading: HashMap::new(),
        }
    }
}
impl Block {
    pub fn new(index: u32, offset: u32) -> Block {
        Block { index, offset }
    }
}

impl Request {
    fn new(peer: usize) -> Request {
        Request {
            peer,
            requested_at: time::Instant::now(),
        }
    }
}

impl Downloading {
    fn complete(&mut self) -> Vec<Request> {
        self.completed = true;
        mem::replace(&mut self.requested, Vec::with_capacity(0))
    }
}
<commit_msg>Ensure that pieces aren't double requested<commit_after>use std::collections::HashMap;
use std::{mem, time};
use torrent::{Info, Peer, Bitfield};
use control::cio;

mod rarest;
mod sequential;

#[cfg(test)]
mod tests;

#[derive(Clone, Debug)]
pub struct Picker {
    /// Number of blocks per piece
    scale: u32,
    /// Number of detected seeders
    seeders: u16,
    /// Set of pieces which have blocks waiting. These should be prioritized.
    downloading: HashMap<u32, Vec<Downloading>>,
    /// Bitfield of unpicked pieces, not in progress or
    /// completed yet. A set bit is picked, unset is unpicked.
    unpicked: Bitfield,
    /// The current picker in use
    picker: PickerKind,
}

#[derive(Clone, Debug, PartialEq)]
pub struct Block {
    pub index: u32,
    pub offset: u32,
}

/// Pickers act solely as piece picking algorithm.
/// They will select the optimal next piece for a peer,
/// and can be told when a piece is complete(or invalid).
#[derive(Clone, Debug)]
enum PickerKind {
    Rarest(rarest::Picker),
    Sequential(sequential::Picker),
}

/// A downloading block and the peers it has been
/// requested from.
#[derive(Clone, Debug)]
struct Downloading {
    offset: u32,
    completed: bool,
    requested: Vec<Request>,
}

/// A request to a peer and the time it was initiated.
#[derive(Clone, Debug)]
struct Request {
    peer: usize,
    requested_at: time::Instant,
}

const MAX_DUP_REQS: usize = 3;

impl Picker {
    /// Creates a new picker, which will select over
    /// the given pieces. The algorithm used for selection
    /// will vary based on the current swarm state, but
    /// will default to rarest first.
    pub fn new(info: &Info, pieces: &Bitfield) -> Picker {
        let scale = info.piece_len / 16_384;
        let picker = rarest::Picker::new(pieces);
        Picker {
            picker: PickerKind::Rarest(picker),
            scale,
            seeders: 0,
            unpicked: pieces.clone(),
            downloading: HashMap::new(),
        }
    }

    /// Returns true if the current picker algorithm is sequential
    pub fn is_sequential(&self) -> bool {
        match self.picker {
            PickerKind::Sequential(_) => true,
            _ => false,
        }
    }

    /// Attempts to select a block for a peer.
    pub fn pick<T: cio::CIO>(&mut self, peer: &Peer<T>) -> Option<Block> {
        if let Some(b) = self.pick_expired(peer) {
            return Some(b);
        }

        let piece = match self.picker {
            PickerKind::Sequential(ref mut p) => p.pick(peer),
            PickerKind::Rarest(ref mut p) => p.pick(peer),
        };
        piece.and_then(|p| self.pick_piece(p, peer.id())).or_else(
            || {
                self.pick_downloading(peer)
            },
        )
    }

    /// Attempts to pick an expired block
    fn pick_expired<T: cio::CIO>(&mut self, peer: &Peer<T>) -> Option<Block> {
        // TODO: Use some form of heuristic here to say "we expect to have
        // downloaded some pieces by X, hit the picker with a tick which checks
        // that, flags shit as invalid, and then does a double request
        None
    }

    /// Picks a block from a given piece for a peer
    fn pick_piece(&mut self, piece: u32, id: usize) -> Option<Block> {
        self.downloading.entry(piece).or_insert_with(|| vec![]);
        let dl = self.downloading.get_mut(&piece).unwrap();
        let offset = dl.len() as u32 * 16_384;
        dl.push(Downloading {
            offset,
            completed: false,
            requested: vec![Request::new(id)],
        });

        if dl.len() == self.scale as usize {
            match self.picker {
                PickerKind::Sequential(ref mut p) => p.completed(piece),
                PickerKind::Rarest(ref mut p) => p.completed(piece),
            }
            self.unpicked.set_bit(piece as u64);
        }
        Some(Block {
            index: piece,
            offset,
        })
    }

    /// Attempts to pick an already requested block
    fn pick_downloading<T: cio::CIO>(&mut self, peer: &Peer<T>) -> Option<Block> {
        for (idx, dl) in &mut self.downloading {
            if peer.pieces().has_bit(*idx as u64) {
                let r = dl.iter_mut()
                    .find(|r| {
                        !r.completed && r.requested.len() < MAX_DUP_REQS &&
                            r.requested.iter().all(|req| req.peer != peer.id())
                    })
                    .map(|r| {
                        r.requested.push(Request::new(peer.id()));
                        Block::new(*idx, r.offset)
                    });
                if r.is_some() {
                    return r;
                }
            }
        }
        None
    }

    /// Marks a block as completed. Returns a result indicating if the block
    /// was actually requested, the success value containing a bool indicating
    /// if the block is complete, and a vector of peers from which the block
    /// was requested(for cancellation).
    pub fn completed(&mut self, b: Block) -> Result<(bool, Vec<usize>), ()> {
        // Find the block in our downloading blocks, mark as true,
        // and extract the current peer list for return.
        let res = self.downloading
            .get_mut(&b.index)
            .and_then(|dl| {
                dl.iter_mut().find(|r| r.offset == b.offset).map(
                    |r| r.complete(),
                )
            })
            .map(|r| r.into_iter().map(|e| e.peer).collect());

        // If we've requested every single block for this piece and they're all complete, remove it
        // and report completion
        let scale = self.scale;
        let complete = self.downloading
            .get_mut(&b.index)
            .map(|r| r.len() as u32 == scale && r.iter().all(|d| d.completed))
            .unwrap_or(false);

        if complete {
            self.downloading.remove(&b.index);
        }

        res.map(|r| (complete, r)).ok_or(())
    }

    /// Invalidates a piece
    pub fn invalidate_piece(&mut self, idx: u32) {
        match self.picker {
            PickerKind::Sequential(ref mut p) => p.incomplete(idx),
            PickerKind::Rarest(ref mut p) => p.incomplete(idx),
        }
        self.unpicked.unset_bit(idx as u64);
    }

    pub fn piece_available(&mut self, idx: u32) {
        if let PickerKind::Rarest(ref mut p) = self.picker {
            p.piece_available(idx);
        }
    }

    pub fn add_peer<T: cio::CIO>(&mut self, peer: &Peer<T>) {
        if peer.pieces().complete() {
            self.seeders += 1;
        }
        if let PickerKind::Rarest(ref mut p) = self.picker {
            p.add_peer(peer);
        }
    }

    pub fn remove_peer<T: cio::CIO>(&mut self, peer: &Peer<T>) {
        if let PickerKind::Rarest(ref mut p) = self.picker {
            p.remove_peer(peer);
        }
    }

    /// Alters the picker to sequential/non sequential. If changing
    /// from sequential to non sequential, peer state will need to be loaded
    /// after this.
    pub fn change_picker(&mut self, sequential: bool) {
        self.picker = if sequential {
            PickerKind::Sequential(sequential::Picker::new(&self.unpicked))
        } else {
            PickerKind::Rarest(rarest::Picker::new(&self.unpicked))
        };
        for i in self.downloading.keys() {
            if let PickerKind::Rarest(ref mut p) = self.picker {
                p.dec_avail(*i);
            }
        }
    }

    pub fn refresh_picker(&mut self, pieces: &Bitfield) {
        self.unpicked = pieces.clone();
        self.picker = if self.is_sequential() {
            PickerKind::Sequential(sequential::Picker::new(&self.unpicked))
        } else {
            PickerKind::Rarest(rarest::Picker::new(&self.unpicked))
        };
    }
}

#[cfg(test)]
impl Picker {
    pub fn new_rarest(info: &Info, pieces: &Bitfield) -> Picker {
        let scale = info.piece_len / 16_384;
        let picker = rarest::Picker::new(pieces);
        Picker {
            picker: PickerKind::Rarest(picker),
            scale,
            seeders: 0,
            unpicked: pieces.clone(),
            downloading: HashMap::new(),
        }
    }

    pub fn new_sequential(info: &Info, pieces: &Bitfield) -> Picker {
        let scale = info.piece_len / 16_384;
        let picker = sequential::Picker::new(pieces);
        Picker {
            picker: PickerKind::Sequential(picker),
            scale,
            seeders: 0,
            unpicked: pieces.clone(),
            downloading: HashMap::new(),
        }
    }
}
impl Block {
    pub fn new(index: u32, offset: u32) -> Block {
        Block { index, offset }
    }
}

impl Request {
    fn new(peer: usize) -> Request {
        Request {
            peer,
            requested_at: time::Instant::now(),
        }
    }
}

impl Downloading {
    fn complete(&mut self) -> Vec<Request> {
        self.completed = true;
        mem::replace(&mut self.requested, Vec::with_capacity(0))
    }
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::io::Write;
use std::str;

use quick_xml::errors::Error as XmlError;
use quick_xml::events::{Event, BytesStart, BytesEnd, BytesText};
use quick_xml::writer::Writer;

use toxml::ToXml;

pub(crate) mod util;

/// A map of extension namespace prefixes to local names to elements.
pub type ExtensionMap = HashMap<String, HashMap<String, Vec<Extension>>>;

/// A namespaced extension.
#[derive(Debug, Default, Clone, PartialEq, Builder)]
pub struct Extension {
    /// The qualified name of the extension element.
    name: String,
    /// The content of the extension element.
    value: Option<String>,
    /// The attributes for the extension element.
    attrs: HashMap<String, String>,
    /// The children of the extension element. A map of local names to child elements.
    children: HashMap<String, Vec<Extension>>,
}

impl Extension {
    /// Return the qualified name of this extension.
    pub fn name(&self) -> &str {
        self.name.as_str()
    }

    /// Set the qualified name of this extension.
    pub fn set_name<V>(&mut self, name: V)
    where
        V: Into<String>,
    {
        self.name = name.into();
    }

    /// Return the text content of this extension.
    pub fn value(&self) -> Option<&str> {
        self.value.as_ref().map(|s| s.as_str())
    }

    /// Set the text content of this extension.
    pub fn set_value<V>(&mut self, value: V)
    where
        V: Into<Option<String>>,
    {
        self.value = value.into();
    }

    /// Return the attributes for the extension element.
    pub fn attrs(&self) -> &HashMap<String, String> {
        &self.attrs
    }

    /// Set the attributes for the extension element.
    pub fn set_attrs<V>(&mut self, attrs: V)
    where
        V: Into<HashMap<String, String>>,
    {
        self.attrs = attrs.into();
    }

    /// Return the children of the extension element.
    ///
    /// A map of local names to child elements.
    pub fn children(&self) -> &HashMap<String, Vec<Extension>> {
        &self.children
    }

    /// Set the children of the extension element.
    ///
    /// A map of local names to child elements.
    pub fn set_children<V>(&mut self, children: V)
    where
        V: Into<HashMap<String, Vec<Extension>>>,
    {
        self.children = children.into();
    }
}

impl ToXml for Extension {
    fn to_xml<W: Write>(&self, writer: &mut Writer<W>) -> Result<(), XmlError> {
        let name = self.name.as_bytes();
        let mut element = BytesStart::borrowed(name, name.len());
        element.extend_attributes(self.attrs.iter().map(|a| (a.0.as_bytes(), a.1.as_bytes())));
        writer.write_event(Event::Start(element))?;

        if let Some(value) = self.value.as_ref() {
            writer.write_event(
                Event::Text(BytesText::borrowed(value.as_bytes())),
            )?;
        }

        for extension in self.children.values().flat_map(|extensions| extensions) {
            extension.to_xml(writer)?;
        }

        writer.write_event(Event::End(BytesEnd::borrowed(name)))?;
        Ok(())
    }
}
<commit_msg>Added examples for Extension methods<commit_after>use std::collections::HashMap;
use std::io::Write;
use std::str;

use quick_xml::errors::Error as XmlError;
use quick_xml::events::{Event, BytesStart, BytesEnd, BytesText};
use quick_xml::writer::Writer;

use toxml::ToXml;

pub(crate) mod util;

/// A map of extension namespace prefixes to local names to elements.
pub type ExtensionMap = HashMap<String, HashMap<String, Vec<Extension>>>;

/// A namespaced extension.
#[derive(Debug, Default, Clone, PartialEq, Builder)]
pub struct Extension {
    /// The qualified name of the extension element.
    name: String,
    /// The content of the extension element.
    value: Option<String>,
    /// The attributes for the extension element.
    attrs: HashMap<String, String>,
    /// The children of the extension element. A map of local names to child elements.
    children: HashMap<String, Vec<Extension>>,
}

impl Extension {
    /// Return the qualified name of this extension.
    ///
    /// # Examples
    ///
    /// ```
    /// use atom_syndication::extension::Extension;
    ///
    /// let mut extension = Extension::default();
    /// extension.set_name("ext:name");
    /// assert_eq!(extension.name(), "ext:name");
    /// ```
    pub fn name(&self) -> &str {
        self.name.as_str()
    }

    /// Set the qualified name of this extension.
    ///
    /// # Examples
    ///
    /// ```
    /// use atom_syndication::extension::Extension;
    ///
    /// let mut extension = Extension::default();
    /// extension.set_name("ext:name");
    /// ```
    pub fn set_name<V>(&mut self, name: V)
    where
        V: Into<String>,
    {
        self.name = name.into();
    }

    /// Return the text content of this extension.
    ///
    /// # Examples
    ///
    /// ```
    /// use atom_syndication::extension::Extension;
    ///
    /// let mut extension = Extension::default();
    /// extension.set_value("John Doe".to_string());
    /// assert_eq!(extension.value(), Some("John Doe"));
    /// ```
    pub fn value(&self) -> Option<&str> {
        self.value.as_ref().map(|s| s.as_str())
    }

    /// Set the text content of this extension.
    ///
    /// # Examples
    ///
    /// ```
    /// use atom_syndication::extension::Extension;
    ///
    /// let mut extension = Extension::default();
    /// extension.set_value("John Doe".to_string());
    /// ```
    pub fn set_value<V>(&mut self, value: V)
    where
        V: Into<Option<String>>,
    {
        self.value = value.into();
    }

    /// Return the attributes for the extension element.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::collections::HashMap;
    /// use atom_syndication::extension::Extension;
    ///
    /// let mut extension = Extension::default();
    /// let mut attrs = HashMap::<String, String>::new();
    /// attrs.insert("email".to_string(), "johndoe@example.com".to_string());
    /// extension.set_attrs(attrs.clone());
    /// assert_eq!(*extension.attrs(), attrs);
    /// ```
    pub fn attrs(&self) -> &HashMap<String, String> {
        &self.attrs
    }

    /// Set the attributes for the extension element.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::collections::HashMap;
    /// use atom_syndication::extension::Extension;
    ///
    /// let mut extension = Extension::default();
    /// extension.set_attrs(HashMap::new());
    /// ```
    pub fn set_attrs<V>(&mut self, attrs: V)
    where
        V: Into<HashMap<String, String>>,
    {
        self.attrs = attrs.into();
    }

    /// Return the children of the extension element.
    ///
    /// A map of local names to child elements.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::collections::HashMap;
    /// use atom_syndication::extension::Extension;
    ///
    /// let mut extension = Extension::default();
    /// let mut children = HashMap::<String, Vec<Extension>>::new();
    /// children.insert("ext:child".to_string(), Vec::new());
    /// extension.set_children(children);
    /// assert!(extension.children().contains_key("ext:child"));
    /// ```
    pub fn children(&self) -> &HashMap<String, Vec<Extension>> {
        &self.children
    }

    /// Set the children of the extension element.
    ///
    /// A map of local names to child elements.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::collections::HashMap;
    /// use atom_syndication::extension::Extension;
    ///
    /// let mut extension = Extension::default();
    /// extension.set_children(HashMap::new());
    /// ```
    pub fn set_children<V>(&mut self, children: V)
    where
        V: Into<HashMap<String, Vec<Extension>>>,
    {
        self.children = children.into();
    }
}

impl ToXml for Extension {
    fn to_xml<W: Write>(&self, writer: &mut Writer<W>) -> Result<(), XmlError> {
        let name = self.name.as_bytes();
        let mut element = BytesStart::borrowed(name, name.len());
        element.extend_attributes(self.attrs.iter().map(|a| (a.0.as_bytes(), a.1.as_bytes())));
        writer.write_event(Event::Start(element))?;

        if let Some(value) = self.value.as_ref() {
            writer.write_event(
                Event::Text(BytesText::borrowed(value.as_bytes())),
            )?;
        }

        for extension in self.children.values().flat_map(|extensions| extensions) {
            extension.to_xml(writer)?;
        }

        writer.write_event(Event::End(BytesEnd::borrowed(name)))?;
        Ok(())
    }
}
<|endoftext|>


<commit_before>use std;
use rustc;

import std::fs;
import std::getopts;
import std::getopts::optopt;
import std::getopts::opt_present;
import std::getopts::opt_str;
import std::io;
import std::io::stdout;
import std::vec;
import std::str;
import std::uint;
import std::option;

import rustc::syntax::ast;
import rustc::syntax::fold;
import rustc::syntax::visit;
import rustc::syntax::codemap;
import rustc::syntax::parse::parser;
import rustc::syntax::print::pprust;

fn write_file(filename: &str, content: &str) {
    io::file_writer(filename, [io::create, io::truncate]).write_str(content);
    // Work around https://github.com/graydon/rust/issues/726
    std::run::run_program("chmod", ["644", filename]);
}

fn file_contains(filename: &str, needle: &str) -> bool {
    let contents = io::read_whole_file_str(filename);
    ret str::find(contents, needle) != -1;
}

fn contains(haystack: &str, needle: &str) -> bool {
    str::find(haystack, needle) != -1
}

fn find_rust_files(files: &mutable [str], path: &str) {
    if str::ends_with(path, ".rs") {
        if file_contains(path, "xfail-test") {
            //log_err "Skipping " + path + " because it is marked as xfail-test";
        } else { files += [path]; }
    } else if fs::file_is_dir(path)
        && str::find(path, "compile-fail") == -1 {
        for p in fs::list_dir(path) {
            find_rust_files(files, p);
        }
    }
}

fn safe_to_steal(e: ast::expr_) -> bool {
    alt e {

      // https://github.com/graydon/rust/issues/890
      ast::expr_lit(lit) {
        alt lit.node {
          ast::lit_str(_) { true }
          ast::lit_char(_) { true }
          ast::lit_int(_) { false }
          ast::lit_uint(_) { true }
          ast::lit_mach_int(_, _) { false }
          ast::lit_float(_) { false }
          ast::lit_mach_float(_, _) { false }
          ast::lit_nil. { true }
          ast::lit_bool(_) { true }
        }
      }

      // https://github.com/graydon/rust/issues/890
      ast::expr_cast(_, _) { false }
      ast::expr_assert(_) { false }
      ast::expr_binary(_, _, _) { false }
      ast::expr_assign(_, _) { false }
      ast::expr_assign_op(_, _, _) { false }

      // https://github.com/graydon/rust/issues/764
      ast::expr_fail(option::none.) { false }
      ast::expr_ret(option::none.) { false }
      ast::expr_put(option::none.) { false }

      // These prefix-operator keywords are not being parenthesized when in callee positions.
      // https://github.com/graydon/rust/issues/891
      ast::expr_ret(_) { false }
      ast::expr_put(_) { false }
      ast::expr_check(_, _) { false }
      ast::expr_log(_, _) { false }

      _ { true }
    }
}

fn steal_exprs(crate: &ast::crate) -> [ast::expr] {
    let exprs: @mutable [ast::expr] = @mutable [];
    // "Stash" is not type-parameterized because of the need for safe_to_steal
    fn stash_expr(es: @mutable [ast::expr], e: &@ast::expr) {
        if safe_to_steal(e.node) {
            *es += [*e];
        } else {/* now my indices are wrong :( */ }
    }
    let v =
        visit::mk_simple_visitor(@{visit_expr: bind stash_expr(exprs, _)
                                      with *visit::default_simple_visitor()});
    visit::visit_crate(crate, (), v);;
    *exprs
}

// https://github.com/graydon/rust/issues/652
fn safe_to_replace(e: ast::expr_) -> bool {
    alt e {
      ast::expr_if(_, _, _) { false }
      ast::expr_block(_) { false }
      _ { true }
    }
}

// Replace the |i|th expr (in fold order) of |crate| with |newexpr|.
fn replace_expr_in_crate(crate: &ast::crate, i: uint, newexpr: ast::expr_) ->
   ast::crate {
    let j: @mutable uint = @mutable 0u;
    fn fold_expr_rep(j_: @mutable uint, i_: uint, newexpr_: &ast::expr_,
                     original: &ast::expr_, fld: fold::ast_fold) ->
       ast::expr_ {
        *j_ += 1u;
        if i_ + 1u == *j_ && safe_to_replace(original) {
            newexpr_
        } else { fold::noop_fold_expr(original, fld) }
    }
    let afp =
        {fold_expr: bind fold_expr_rep(j, i, newexpr, _, _)
            with *fold::default_ast_fold()};
    let af = fold::make_fold(afp);
    let crate2: @ast::crate = @af.fold_crate(crate);
    fold::dummy_out(af); // work around a leak (https://github.com/graydon/rust/issues/651)
    ;
    *crate2
}

iter under(n: uint) -> uint {
    let i: uint = 0u;
    while i < n { put i; i += 1u; }
}

fn devnull() -> io::writer { std::io::string_writer().get_writer() }

fn as_str(f: fn(io::writer)) -> str {
    let w = std::io::string_writer();
    f(w.get_writer());
    ret w.get_str();
}

fn check_variants_of_ast(crate: &ast::crate, codemap: &codemap::codemap,
                         filename: &str) {
    let exprs = steal_exprs(crate);
    let exprsL = vec::len(exprs);
    if exprsL < 100u {
        for each i: uint in under(uint::min(exprsL, 20u)) {
            log_err "Replacing... " + pprust::expr_to_str(@exprs[i]);
            for each j: uint in under(uint::min(exprsL, 5u)) {
                log_err "With... " + pprust::expr_to_str(@exprs[j]);
                let crate2 = @replace_expr_in_crate(crate, i, exprs[j].node);
                // It would be best to test the *crate* for stability, but testing the
                // string for stability is easier and ok for now.
                let str3 =
                    as_str(bind pprust::print_crate(codemap, crate2,
                                                    filename,
                                                    io::string_reader(""), _,
                                                    pprust::no_ann()));
                check_roundtrip_convergence(str3, 1u);
                //let label = #fmt("buggy_%s_%ud_%ud.rs", last_part(filename), i, j);
                //check_whole_compiler(str3, label);
            }
        }
    }
}

fn last_part(filename: &str) -> str {
  let ix = str::rindex(filename, 47u8 /* '/' */);
  assert ix >= 0;
  str::slice(filename, ix as uint + 1u, str::byte_len(filename) - 3u)
}

tag compile_result { known_bug(str); passed(str); failed(str); }

// We'd find more bugs if we could take an AST here, but
// - that would find many "false positives" or unimportant bugs
// - that would be tricky, requiring use of tasks or serialization or randomness.
// This seems to find plenty of bugs as it is :)
fn check_whole_compiler(code: &str, suggested_filename: &str) {
    let filename = "test.rs";
    write_file(filename, code);
    alt check_whole_compiler_inner(filename) {
      known_bug(s) {
        log_err "Ignoring known bug: " + s;
      }
      failed(s) {
        log_err "check_whole_compiler failure: " + s;
        write_file(suggested_filename, code);
        log_err "Saved as: " + suggested_filename;
      }
      passed(_) { }
    }
}

fn check_whole_compiler_inner(filename: &str) -> compile_result {
    let p = std::run::program_output(
            "/Users/jruderman/code/rust/build/stage1/rustc",
            ["-c", filename]);

    //log_err #fmt("Status: %d", p.status);
    if p.err != "" {
        if contains(p.err, "May only branch on boolean predicates") {
            known_bug("https://github.com/graydon/rust/issues/892")
        } else if contains(p.err, "(S->getType()->isPointerTy() && \"Invalid cast\")") {
            known_bug("https://github.com/graydon/rust/issues/895")
        } else {
            log_err "Stderr: " + p.err;
            failed("Unfamiliar error message")
        }
    } else if p.status == 6 {
        if contains(p.out, "get_id_ident: can't find item in ext_map") {
            known_bug("https://github.com/graydon/rust/issues/876")
        } else if contains(p.out, "Assertion !cx.terminated failed") {
            known_bug("https://github.com/graydon/rust/issues/893 or https://github.com/graydon/rust/issues/894")
        } else if !contains(p.out, "error:") {
            log_err "Stdout: " + p.out;
            failed("Rejected the input program without a span-error explanation")
        } else {
            passed("Rejected the input program cleanly")
        }
    } else if p.status == 11 {
        failed("Crashed!?")
    } else if p.status == 0 {
        passed("Accepted the input program")
    } else {
        log_err p.status;
        log_err p.out;
        failed("Unfamiliar status code")
    }
}

fn parse_and_print(code: &str) -> str {
    let filename = "tmp.rs";
    let sess = @{cm: codemap::new_codemap(), mutable next_id: 0};
    //write_file(filename, code);
    let crate = parser::parse_crate_from_source_str(
        filename, code, [], sess);
    ret as_str(bind pprust::print_crate(sess.cm, crate,
                                        filename,
                                        io::string_reader(code), _,
                                        pprust::no_ann()));
}

fn content_is_dangerous_to_modify(code: &str) -> bool {
    let dangerous_patterns =
        ["#macro", // not safe to steal things inside of it, because they have a special syntax
         "#",      // strange representation of the arguments to #fmt, for example
         " be "];  // don't want to replace its child with a non-call: "Non-call expression in tail call"

    for p: str in dangerous_patterns { if contains(code, p) { ret true; } }
    ret false;
}

fn content_is_confusing(code: &str) -> bool {
    let confusing_patterns =
        ["self",       // crazy rules enforced by parser rather than typechecker?
        "spawn",       // precedence issues?
         "bind",       // precedence issues?
         "\n\n\n\n\n"  // https://github.com/graydon/rust/issues/850
        ];

    for p: str in confusing_patterns { if contains(code, p) { ret true; } }
    ret false;
}

fn file_is_confusing(filename: &str) -> bool {
    let confusing_files = [];

    for f in confusing_files { if contains(filename, f) { ret true; } }

    ret false;
}

fn check_roundtrip_convergence(code: &str, maxIters: uint) {

    let i = 0u;
    let new = code;
    let old = code;

    while i < maxIters {
        old = new;
        if content_is_confusing(old) { ret; }
        new = parse_and_print(old);
        if old == new { break; }
        i += 1u;
    }

    if old == new {
        log_err #fmt["Converged after %u iterations", i];
    } else {
        log_err #fmt["Did not converge after %u iterations!", i];
        write_file("round-trip-a.rs", old);
        write_file("round-trip-b.rs", new);
        std::run::run_program("diff",
                              ["-w", "-u", "round-trip-a.rs",
                               "round-trip-b.rs"]);
        fail "Mismatch";
    }
}

fn check_convergence(files: &[str]) {
    log_err #fmt["pp convergence tests: %u files", vec::len(files)];
    for file in files {
        if !file_is_confusing(file) {
            let s = io::read_whole_file_str(file);
            if !content_is_confusing(s) {
                log_err #fmt["pp converge: %s", file];
                // Change from 7u to 2u once https://github.com/graydon/rust/issues/850 is fixed
                check_roundtrip_convergence(s, 7u);
            }
        }
    }
}

fn check_variants(files: &[str]) {
    for file in files {
        if !file_is_confusing(file) {
            let s = io::read_whole_file_str(file);
            if content_is_dangerous_to_modify(s) || content_is_confusing(s) {
                cont;
            }
            log_err "check_variants: " + file;
            let sess = @{cm: codemap::new_codemap(), mutable next_id: 0};
            let crate =
                parser::parse_crate_from_source_str(
                    file,
                    s, [], sess);
            log_err as_str(bind pprust::print_crate(sess.cm, crate,
                                                    file,
                                                    io::string_reader(s), _,
                                                    pprust::no_ann()));
            check_variants_of_ast(*crate, sess.cm, file);
        }
    }
}

fn main(args: [str]) {
    if vec::len(args) != 2u {
        log_err #fmt["usage: %s <testdir>", args[0]];
        ret;
    }
    let files = [];
    let root = args[1];

    find_rust_files(files, root);
    check_convergence(files);
    check_variants(files);
    log_err "Fuzzer done";
}

// Local Variables:
// mode: rust;
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// compile-command: "make -k -C $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
// End:
<commit_msg>Fuzzer: first steps of making steal_exprs more generic<commit_after>use std;
use rustc;

import std::fs;
import std::getopts;
import std::getopts::optopt;
import std::getopts::opt_present;
import std::getopts::opt_str;
import std::io;
import std::io::stdout;
import std::vec;
import std::str;
import std::uint;
import std::option;

import rustc::syntax::ast;
import rustc::syntax::fold;
import rustc::syntax::visit;
import rustc::syntax::codemap;
import rustc::syntax::parse::parser;
import rustc::syntax::print::pprust;

fn write_file(filename: &str, content: &str) {
    io::file_writer(filename, [io::create, io::truncate]).write_str(content);
    // Work around https://github.com/graydon/rust/issues/726
    std::run::run_program("chmod", ["644", filename]);
}

fn file_contains(filename: &str, needle: &str) -> bool {
    let contents = io::read_whole_file_str(filename);
    ret str::find(contents, needle) != -1;
}

fn contains(haystack: &str, needle: &str) -> bool {
    str::find(haystack, needle) != -1
}

fn find_rust_files(files: &mutable [str], path: &str) {
    if str::ends_with(path, ".rs") {
        if file_contains(path, "xfail-test") {
            //log_err "Skipping " + path + " because it is marked as xfail-test";
        } else { files += [path]; }
    } else if fs::file_is_dir(path)
        && str::find(path, "compile-fail") == -1 {
        for p in fs::list_dir(path) {
            find_rust_files(files, p);
        }
    }
}

fn safe_to_steal_expr(e: &@ast::expr) -> bool {
    alt e.node {

      // https://github.com/graydon/rust/issues/890
      ast::expr_lit(lit) {
        alt lit.node {
          ast::lit_str(_) { true }
          ast::lit_char(_) { true }
          ast::lit_int(_) { false }
          ast::lit_uint(_) { true }
          ast::lit_mach_int(_, _) { false }
          ast::lit_float(_) { false }
          ast::lit_mach_float(_, _) { false }
          ast::lit_nil. { true }
          ast::lit_bool(_) { true }
        }
      }

      // https://github.com/graydon/rust/issues/890
      ast::expr_cast(_, _) { false }
      ast::expr_assert(_) { false }
      ast::expr_binary(_, _, _) { false }
      ast::expr_assign(_, _) { false }
      ast::expr_assign_op(_, _, _) { false }

      // https://github.com/graydon/rust/issues/764
      ast::expr_fail(option::none.) { false }
      ast::expr_ret(option::none.) { false }
      ast::expr_put(option::none.) { false }

      // These prefix-operator keywords are not being parenthesized when in callee positions.
      // https://github.com/graydon/rust/issues/891
      ast::expr_ret(_) { false }
      ast::expr_put(_) { false }
      ast::expr_check(_, _) { false }
      ast::expr_log(_, _) { false }

      _ { true }
    }
}

// Not type-parameterized: https://github.com/graydon/rust/issues/898
fn stash_expr_if(c: fn(&@ast::expr)->bool, es: @mutable [ast::expr], e: &@ast::expr) {
    if c(e) {
        *es += [*e];
    } else {/* now my indices are wrong :( */ }
}

fn steal_exprs(crate: &ast::crate) -> [ast::expr] {
    let exprs: @mutable [ast::expr] = @mutable [];
    let v = visit::mk_simple_visitor(@{
        visit_expr: bind stash_expr_if(safe_to_steal_expr, exprs, _)
        with *visit::default_simple_visitor()
    });
    visit::visit_crate(crate, (), v);;
    *exprs
}

// https://github.com/graydon/rust/issues/652
fn safe_to_replace(e: ast::expr_) -> bool {
    alt e {
      ast::expr_if(_, _, _) { false }
      ast::expr_block(_) { false }
      _ { true }
    }
}

// Replace the |i|th expr (in fold order) of |crate| with |newexpr|.
fn replace_expr_in_crate(crate: &ast::crate, i: uint, newexpr: ast::expr_) ->
   ast::crate {
    let j: @mutable uint = @mutable 0u;
    fn fold_expr_rep(j_: @mutable uint, i_: uint, newexpr_: &ast::expr_,
                     original: &ast::expr_, fld: fold::ast_fold) ->
       ast::expr_ {
        *j_ += 1u;
        if i_ + 1u == *j_ && safe_to_replace(original) {
            newexpr_
        } else { fold::noop_fold_expr(original, fld) }
    }
    let afp =
        {fold_expr: bind fold_expr_rep(j, i, newexpr, _, _)
            with *fold::default_ast_fold()};
    let af = fold::make_fold(afp);
    let crate2: @ast::crate = @af.fold_crate(crate);
    fold::dummy_out(af); // work around a leak (https://github.com/graydon/rust/issues/651)
    ;
    *crate2
}

iter under(n: uint) -> uint {
    let i: uint = 0u;
    while i < n { put i; i += 1u; }
}

fn devnull() -> io::writer { std::io::string_writer().get_writer() }

fn as_str(f: fn(io::writer)) -> str {
    let w = std::io::string_writer();
    f(w.get_writer());
    ret w.get_str();
}

fn check_variants_of_ast(crate: &ast::crate, codemap: &codemap::codemap,
                         filename: &str) {
    let exprs = steal_exprs(crate);
    let exprsL = vec::len(exprs);
    if exprsL < 100u {
        for each i: uint in under(uint::min(exprsL, 20u)) {
            log_err "Replacing... " + pprust::expr_to_str(@exprs[i]);
            for each j: uint in under(uint::min(exprsL, 5u)) {
                log_err "With... " + pprust::expr_to_str(@exprs[j]);
                let crate2 = @replace_expr_in_crate(crate, i, exprs[j].node);
                // It would be best to test the *crate* for stability, but testing the
                // string for stability is easier and ok for now.
                let str3 =
                    as_str(bind pprust::print_crate(codemap, crate2,
                                                    filename,
                                                    io::string_reader(""), _,
                                                    pprust::no_ann()));
                check_roundtrip_convergence(str3, 1u);
                //let label = #fmt("buggy_%s_%ud_%ud.rs", last_part(filename), i, j);
                //check_whole_compiler(str3, label);
            }
        }
    }
}

fn last_part(filename: &str) -> str {
  let ix = str::rindex(filename, 47u8 /* '/' */);
  assert ix >= 0;
  str::slice(filename, ix as uint + 1u, str::byte_len(filename) - 3u)
}

tag compile_result { known_bug(str); passed(str); failed(str); }

// We'd find more bugs if we could take an AST here, but
// - that would find many "false positives" or unimportant bugs
// - that would be tricky, requiring use of tasks or serialization or randomness.
// This seems to find plenty of bugs as it is :)
fn check_whole_compiler(code: &str, suggested_filename: &str) {
    let filename = "test.rs";
    write_file(filename, code);
    alt check_whole_compiler_inner(filename) {
      known_bug(s) {
        log_err "Ignoring known bug: " + s;
      }
      failed(s) {
        log_err "check_whole_compiler failure: " + s;
        write_file(suggested_filename, code);
        log_err "Saved as: " + suggested_filename;
      }
      passed(_) { }
    }
}

fn check_whole_compiler_inner(filename: &str) -> compile_result {
    let p = std::run::program_output(
            "/Users/jruderman/code/rust/build/stage1/rustc",
            ["-c", filename]);

    //log_err #fmt("Status: %d", p.status);
    if p.err != "" {
        if contains(p.err, "May only branch on boolean predicates") {
            known_bug("https://github.com/graydon/rust/issues/892")
        } else if contains(p.err, "(S->getType()->isPointerTy() && \"Invalid cast\")") {
            known_bug("https://github.com/graydon/rust/issues/895")
        } else {
            log_err "Stderr: " + p.err;
            failed("Unfamiliar error message")
        }
    } else if p.status == 6 {
        if contains(p.out, "get_id_ident: can't find item in ext_map") {
            known_bug("https://github.com/graydon/rust/issues/876")
        } else if contains(p.out, "Assertion !cx.terminated failed") {
            known_bug("https://github.com/graydon/rust/issues/893 or https://github.com/graydon/rust/issues/894")
        } else if !contains(p.out, "error:") {
            log_err "Stdout: " + p.out;
            failed("Rejected the input program without a span-error explanation")
        } else {
            passed("Rejected the input program cleanly")
        }
    } else if p.status == 11 {
        failed("Crashed!?")
    } else if p.status == 0 {
        passed("Accepted the input program")
    } else {
        log_err p.status;
        log_err p.out;
        failed("Unfamiliar status code")
    }
}

fn parse_and_print(code: &str) -> str {
    let filename = "tmp.rs";
    let sess = @{cm: codemap::new_codemap(), mutable next_id: 0};
    //write_file(filename, code);
    let crate = parser::parse_crate_from_source_str(
        filename, code, [], sess);
    ret as_str(bind pprust::print_crate(sess.cm, crate,
                                        filename,
                                        io::string_reader(code), _,
                                        pprust::no_ann()));
}

fn content_is_dangerous_to_modify(code: &str) -> bool {
    let dangerous_patterns =
        ["#macro", // not safe to steal things inside of it, because they have a special syntax
         "#",      // strange representation of the arguments to #fmt, for example
         " be "];  // don't want to replace its child with a non-call: "Non-call expression in tail call"

    for p: str in dangerous_patterns { if contains(code, p) { ret true; } }
    ret false;
}

fn content_is_confusing(code: &str) -> bool {
    let confusing_patterns =
        ["self",       // crazy rules enforced by parser rather than typechecker?
        "spawn",       // precedence issues?
         "bind",       // precedence issues?
         "\n\n\n\n\n"  // https://github.com/graydon/rust/issues/850
        ];

    for p: str in confusing_patterns { if contains(code, p) { ret true; } }
    ret false;
}

fn file_is_confusing(filename: &str) -> bool {
    let confusing_files = [];

    for f in confusing_files { if contains(filename, f) { ret true; } }

    ret false;
}

fn check_roundtrip_convergence(code: &str, maxIters: uint) {

    let i = 0u;
    let new = code;
    let old = code;

    while i < maxIters {
        old = new;
        if content_is_confusing(old) { ret; }
        new = parse_and_print(old);
        if old == new { break; }
        i += 1u;
    }

    if old == new {
        log_err #fmt["Converged after %u iterations", i];
    } else {
        log_err #fmt["Did not converge after %u iterations!", i];
        write_file("round-trip-a.rs", old);
        write_file("round-trip-b.rs", new);
        std::run::run_program("diff",
                              ["-w", "-u", "round-trip-a.rs",
                               "round-trip-b.rs"]);
        fail "Mismatch";
    }
}

fn check_convergence(files: &[str]) {
    log_err #fmt["pp convergence tests: %u files", vec::len(files)];
    for file in files {
        if !file_is_confusing(file) {
            let s = io::read_whole_file_str(file);
            if !content_is_confusing(s) {
                log_err #fmt["pp converge: %s", file];
                // Change from 7u to 2u once https://github.com/graydon/rust/issues/850 is fixed
                check_roundtrip_convergence(s, 7u);
            }
        }
    }
}

fn check_variants(files: &[str]) {
    for file in files {
        if !file_is_confusing(file) {
            let s = io::read_whole_file_str(file);
            if content_is_dangerous_to_modify(s) || content_is_confusing(s) {
                cont;
            }
            log_err "check_variants: " + file;
            let sess = @{cm: codemap::new_codemap(), mutable next_id: 0};
            let crate =
                parser::parse_crate_from_source_str(
                    file,
                    s, [], sess);
            log_err as_str(bind pprust::print_crate(sess.cm, crate,
                                                    file,
                                                    io::string_reader(s), _,
                                                    pprust::no_ann()));
            check_variants_of_ast(*crate, sess.cm, file);
        }
    }
}

fn main(args: [str]) {
    if vec::len(args) != 2u {
        log_err #fmt["usage: %s <testdir>", args[0]];
        ret;
    }
    let files = [];
    let root = args[1];

    find_rust_files(files, root);
    check_convergence(files);
    check_variants(files);
    log_err "Fuzzer done";
}

// Local Variables:
// mode: rust;
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// compile-command: "make -k -C $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
// End:
<|endoftext|>


<commit_before>extern crate sdl2;

use sdl2::video::{Window, WindowPos, OPENGL};
use sdl2::render::{RenderDriverIndex, ACCELERATED, Renderer};
use sdl2::pixels::Color;
use sdl2::event::poll_event;
use sdl2::event::Event::{Quit, KeyDown};
use sdl2::keycode::KeyCode;

use std::num::Float;
fn main()
{
    sdl2::init(sdl2::INIT_VIDEO);
    let window = match Window::new("Wolf3D", WindowPos::PosCentered,
                                   WindowPos::PosCentered, 800, 600, OPENGL)
    {
        Ok(window) => window,
        Err(err) => panic!("failed to create window: {}", err)
    };
    let renderer = match Renderer::from_window(window, RenderDriverIndex::Auto, ACCELERATED)
    {
        Ok(renderer) => renderer,
        Err(err) => panic!("failed to create renderer: {}", err)
    };
    let _ = renderer.set_draw_color(Color::RGB(255, 0, 0));
    let _ = renderer.clear();
    renderer.present();

    let x: u64 = 0;
    let y: u64 = 0;
    let r = 0f64;
    let width = 320f64;
    let height = 200f64;
    let fov = 60f64;
    let plane_center = (width / 2f64, height / 2f64);
    let d  = (width / 2f64) / (fov / 2f64).to_radians().tan();
    let step = (fov / width).to_radians();
    println!("{:?}", cast(fov, (96, 224)));
    loop
    {
        match poll_event()
        {
            Quit(_) => break,
            KeyDown(_, _, key, _, _, _) =>
            {
                if key == KeyCode::Escape {break;}
            }
            _ => {}
        }
    }
    sdl2::quit();
}

fn raycast(step: f64, width: f64, fov: f64, r: f64)
{
    let mut start = r - (fov / 2f64);

    for _ in range(0, width as u64)
    {
        // cast
        // trace
        // distance
        start += step;
    }
}

fn cast(angle: f64, pos: (u64, u64)) -> (u64, u64)
{
    let (x, y) = pos;
    let r = angle.to_radians().tan();
    let (a_y, ya) = if 0f64 <= angle && angle < 180f64
    {
        ((y / 64) * 64 - 1, -64)
    }
    else
    {
        ((y / 64) * 64 + 64, 64)
    };
    let a_x = x + ((y - a_y) as f64 / angle.to_radians().tan()) as u64;
    let xa = (64f64 / r) as u64;
    println!("{}", check(a_x / 64, a_y / 64));
    (a_x, a_y)
}

fn check(col: u64, row: u64) -> bool
{
    let map = [[1, 1, 1, 1], [1, 0, 0, 1], [1, 0, 0, 1], [1, 1, 1, 1]];
    map[row as usize][col as usize] != 0
}
<commit_msg>careful<commit_after>extern crate sdl2;

use sdl2::video::{Window, WindowPos, OPENGL};
use sdl2::render::{RenderDriverIndex, ACCELERATED, Renderer};
use sdl2::pixels::Color;
use sdl2::event::poll_event;
use sdl2::event::Event::{Quit, KeyDown};
use sdl2::keycode::KeyCode;

use std::num::Float;
fn main()
{
    sdl2::init(sdl2::INIT_VIDEO);
    let window = match Window::new("Wolf3D", WindowPos::PosCentered,
                                   WindowPos::PosCentered, 800, 600, OPENGL)
    {
        Ok(window) => window,
        Err(err) => panic!("failed to create window: {}", err)
    };
    let renderer = match Renderer::from_window(window, RenderDriverIndex::Auto, ACCELERATED)
    {
        Ok(renderer) => renderer,
        Err(err) => panic!("failed to create renderer: {}", err)
    };
    let _ = renderer.set_draw_color(Color::RGB(255, 0, 0));
    let _ = renderer.clear();
    renderer.present();

    let x: u64 = 0;
    let y: u64 = 0;
    let r = 0f64;
    let width = 320f64;
    let height = 200f64;
    let fov = 60f64;
    let plane_center = (width / 2f64, height / 2f64);
    let d  = (width / 2f64) / (fov / 2f64).to_radians().tan();
    let step = (fov / width).to_radians();
    let map = vec![vec![1u8, 1, 1, 1], vec![1, 0, 0, 1], vec![1, 0, 0, 1], vec![1, 1, 1, 1]];
    println!("{:?}", cast(fov, (96, 224), map));
    loop
    {
        match poll_event()
        {
            Quit(_) => break,
            KeyDown(_, _, key, _, _, _) =>
            {
                if key == KeyCode::Escape {break;}
            }
            _ => {}
        }
    }
    sdl2::quit();
}

fn raycast(step: f64, width: f64, fov: f64, r: f64)
{
    let mut start = r - (fov / 2f64);

    for _ in range(0, width as u64)
    {
        // cast
        // trace
        // distance
        start += step;
    }
}

fn cast(angle: f64, pos: (u64, u64), map: Vec<Vec<u8>>) -> (u64, u64)
{
    let (x, y) = pos;
    let r = angle.to_radians().tan();
    let (a_y, ya) = if 0f64 <= angle && angle < 180f64
    {
        ((y / 64) * 64 - 1, -64)
    }
    else
    {
        ((y / 64) * 64 + 64, 64)
    };
    let a_x = x + ((y - a_y) as f64 / angle.to_radians().tan()) as u64;
    let xa = (64f64 / r) as u64;
    println!("{}", check(a_x / 64, a_y / 64, map));
    (a_x, a_y)
}

fn check(col: u64, row: u64, map: Vec<Vec<u8>>) -> bool
{
    map[row as usize][col as usize] != 0
}
<|endoftext|>