<commit_before>extern crate reqwest;
extern crate url;

use self::reqwest::Client;
use self::reqwest::header::{CONNECTION, CONTENT_LENGTH, REFERER};

use std::fs::{self, File};
use std::io::{Read, Write};
use std::path::Path;
use std::sync::{Arc, Mutex};
use std::thread::{self, JoinHandle};

use rom::{Progress, RomConfig, RomFlags};

trait FindFrom {
    fn find_from(&self, pat: &str, start: usize) -> Option<usize>;
}

impl FindFrom for str {
    fn find_from(&self, pat: &str, start: usize) -> Option<usize> {
        if let Some(pos) = self[start .. ].find(pat) {
            Some(start + pos)
        } else {
            None
        }
    }
}

trait FindSkip {
    fn find_skip(&self, pat: &str) -> Option<usize>;
}

impl FindSkip for str {
    fn find_skip(&self, pat: &str) -> Option<usize> {
        if let Some(pos) = self.find(pat) {
            Some(pos + pat.len())
        } else {
            None
        }
    }
}

trait FindFromSkip {
    fn find_from_skip(&self, pat: &str, start: usize) -> Option<usize>;
}

impl FindFromSkip for str {
    fn find_from_skip(&self, pat: &str, start: usize) -> Option<usize> {
        if let Some(pos) = self[start .. ].find(pat) {
            Some(start + pos + pat.len())
        } else {
            None
        }
    }
}

#[derive(Debug, Default)]
struct Page {
    count: usize,
    index: usize,
    total: usize,
}

fn parse(html: &str, roms: &mut Vec<RomConfig>) -> Page {
    let mut page = Page::default();

    let mut entry = RomConfig::default();
    for line in html.lines() {
        if line.find("<meta name=\"description\" content=\"Now listing roms for ").is_some() {
            if let Some(p) = line.find_skip("Showing ") {
                if let Some(n) = line.find_from(" ", p) {
                    if let Ok(count) = usize::from_str_radix(&line[p .. n].replace(",", ""), 10) {
                        page.count = count;
                    }
                }

                if let Some(p) = line.find_from_skip("index ", p) {
                    if let Some(n) = line.find_from(" ", p) {
                        if let Ok(index) = usize::from_str_radix(&line[p .. n].replace(",", ""), 10) {
                            page.index = index;
                        }
                    }

                    if let Some(p) = line.find_from_skip("of ", p) {
                        if let Some(n) = line.find_from(" ", p) {
                            if let Ok(total) = usize::from_str_radix(&line[p .. n].replace(",", ""), 10) {
                                page.total = total;
                            }
                        }
                    }
                }
            }
        }

        if line.find("").is_some() {
            if let Some(p) = line.find_skip("<img src=\"https://www.doperoms.com/") {
                if let Some(n) = line.find_from(".gif\" ", p) {
                    let flag = line[p .. n].to_string();
                    if flag == "good" {
                        entry.flags.push(RomFlags::Good);
                    } else if flag == "cracked" {
                        entry.flags.push(RomFlags::Cracked);
                    } else if flag == "alternate" {
                        entry.flags.push(RomFlags::Alternate);
                    } else if flag == "trainer" {
                        entry.flags.push(RomFlags::Trainer);
                    } else if flag == "fix" {
                        entry.flags.push(RomFlags::Fix);
                    } else if flag == "hack" {
                        entry.flags.push(RomFlags::Hack);
                    } else if flag == "publicdomain" {
                        entry.flags.push(RomFlags::PublicDomain);
                    } else if flag == "bad" {
                        entry.flags.push(RomFlags::Bad);
                    } else if flag == "overdump" {
                        entry.flags.push(RomFlags::OverDump);
                    }
                }
            }
        }

        if line.find("<a id=\"listing\" ").is_some() {
            if let Some(p) = line.find_skip("name=\"") {
                if let Some(n) = line.find_from("\" ", p) {
                    entry.file = line[p .. n].to_string();
                }

                if let Some(p) = line.find_from_skip("<img src=\\'", p) {
                    if let Some(n) = line.find_from("\\' ", p) {
                        entry.image = line[p .. n].to_string();
                    }

                    if let Some(p) = line.find_from_skip("Game Name: 	", p) {
                        if let Some(n) = line.find_from(" ", p) {
                            entry.name = line[p .. n].to_string();
                        }
                    }
                }
            }

            if entry.file != "No Roms" {
                roms.push(entry);
                entry = RomConfig::default();
            }
        }
    }

    page
}

pub struct List {
    progress: Arc<Mutex<Progress>>,
    result: JoinHandle<Vec<RomConfig>>,
}

impl List {
    pub fn new(system: &str) -> List {
        let progress = Arc::new(Mutex::new(Progress::Connecting));
        let progress_child = progress.clone();
        let system_child = system.to_string();

        let result = thread::spawn(move || -> Vec<RomConfig> {
            let mut roms = Vec::new();

            let client = Client::new();
            let mut next_index = 0;
            'downloading: loop {
                match client
                    .get(&format!("https://doperoms.com/roms/{}/ALL/{}.html", system_child, next_index))
                    .header(CONNECTION, "keep-alive")
                    .header(REFERER, "https://www.doperoms.com/")
                    .send()
                    .and_then(|x| x.error_for_status())
                {
                    Ok(mut res) => {
                        match res.text() {
                            Ok(html) => {
                                let page = parse(&html, &mut roms);

                                if let Ok(mut progress) = progress_child.lock() {
                                    *progress = Progress::InProgress(page.index as u64, page.total as u64);
                                }

                                next_index = page.index + page.count;
                                if next_index >= page.total {
                                    if let Ok(mut progress) = progress_child.lock() {
                                        *progress = Progress::Complete;
                                    }
                                    break 'downloading;
                                }
                            },
                            Err(err) => {
                                if let Ok(mut progress) = progress_child.lock() {
                                    println!("list res.text: {}", err);
                                    *progress = Progress::Error(format!("{}", err));
                                }
                                break 'downloading;
                            }
                        }
                    },
                    Err(err) => {
                        if let Ok(mut progress) = progress_child.lock() {
                            println!("list client send: {}", err);
                            *progress = Progress::Error(format!("{}", err));
                        }
                        break 'downloading;
                    }
                }
            }

            roms
        });

        List {
            progress: progress,
            result: result,
        }
    }

    pub fn progress(&self) -> Progress {
        match self.progress.lock() {
            Ok(progress) => progress.clone(),
            Err(err) => Progress::Error(format!("{}", err))
        }
    }

    pub fn result(self) -> Vec<RomConfig> {
        if let Some(roms) = self.result.join().ok() {
            roms
        } else {
            Vec::new()
        }
    }
}

pub struct Download {
    progress: Arc<Mutex<Progress>>,
    result: JoinHandle<()>
}

impl Download {
    pub fn new(url: &str, path: &Path) -> Download {
        let progress = Arc::new(Mutex::new(Progress::Connecting));
        let progress_child = progress.clone();
        let url_child = url.to_string();
        let path_child = path.to_path_buf();

        let result = thread::spawn(move || {
            if let Some(parent) = path_child.parent() {
                if ! parent.is_dir() {
                    match fs::create_dir_all(parent) {
                        Ok(_) => (),
                        Err(err) => {
                            println!("create dir: {}", err);
                            if let Ok(mut progress) = progress_child.lock() {
                                *progress = Progress::Error(format!("{}", err));
                            }
                            return;
                        }
                    }
                }
            }

            match File::create(&path_child) {
                Ok(mut file) => {
                    match Client::new()
                            .get(&url_child)
                            .header(CONNECTION, "keep-alive")
                            .header(REFERER, "https://doperoms.com/")
                            .send()
                    {
                        Ok(mut res) => {
                            if let Some(total) = res.headers()
                                .get(CONTENT_LENGTH)
                                .and_then(|x| x.to_str().ok())
                                .and_then(|x| x.parse().ok())
                            {
                                let mut downloaded = 0;
                                if let Ok(mut progress) = progress_child.lock() {
                                    *progress = Progress::InProgress(downloaded, total);
                                }

                                let mut bytes = [0; 4096];
                                'downloading: loop {
                                    match res.read(&mut bytes) {
                                        Ok(0) => {
                                            if let Ok(mut progress) = progress_child.lock() {
                                                *progress = Progress::Complete;
                                            }
                                            break 'downloading;
                                        }
                                        Ok(count) => {
                                            match file.write(&bytes[ .. count]) {
                                                Ok(_) => {
                                                    downloaded += count as u64;
                                                    if let Ok(mut progress) = progress_child.lock() {
                                                        *progress = Progress::InProgress(downloaded, total);
                                                    }
                                                },
                                                Err(err) => {
                                                    println!("file write: {}", err);
                                                    if let Ok(mut progress) = progress_child.lock() {
                                                        *progress = Progress::Error(format!("{}", err));
                                                    }
                                                    break 'downloading;
                                                }
                                            }
                                        }
                                        Err(err) => {
                                            println!("res read: {}", err);
                                            if let Ok(mut progress) = progress_child.lock() {
                                                *progress = Progress::Error(format!("{}", err));
                                            }
                                            break 'downloading;
                                        }
                                    }
                                }
                            } else {
                                println!("no content length");
                                if let Ok(mut progress) = progress_child.lock() {
                                    *progress = Progress::Error("No ContentLength".to_string());
                                }
                            }
                        },
                        Err(err) => {
                            println!("client send: {}", err);
                            if let Ok(mut progress) = progress_child.lock() {
                                *progress = Progress::Error(format!("{}", err));
                            }
                        }
                    }
                },
                Err(err) => {
                    println!("file open: {}", err);
                    if let Ok(mut progress) = progress_child.lock() {
                        *progress = Progress::Error(format!("{}", err));
                    }
                }
            }
        });

        Download {
            progress: progress,
            result: result
        }
    }

    pub fn rom(system: &str, rom: &str, path: &Path) -> Download {
        Download::new(
            &format!("https://doperoms.com/files/roms/{}/GETFILE_{}", system, url::percent_encoding::utf8_percent_encode(rom, url::percent_encoding::DEFAULT_ENCODE_SET)),
            path
        )
    }

    pub fn progress(&self) -> Progress {
        match self.progress.lock() {
            Ok(progress) => progress.clone(),
            Err(err) => Progress::Error(format!("{}", err))
        }
    }

    pub fn result(self) -> bool {
        match self.result.join() {
            Ok(_) => true,
            Err(_) => false
        }
    }
}
<commit_msg>Fix failures to download lists<commit_after>extern crate reqwest;
extern crate url;

use self::reqwest::ClientBuilder;
use self::reqwest::header::{CONNECTION, CONTENT_LENGTH, REFERER};

use std::fs::{self, File};
use std::io::{Read, Write};
use std::path::Path;
use std::sync::{Arc, Mutex};
use std::thread::{self, JoinHandle};

use rom::{Progress, RomConfig, RomFlags};

trait FindFrom {
    fn find_from(&self, pat: &str, start: usize) -> Option<usize>;
}

impl FindFrom for str {
    fn find_from(&self, pat: &str, start: usize) -> Option<usize> {
        if let Some(pos) = self[start .. ].find(pat) {
            Some(start + pos)
        } else {
            None
        }
    }
}

trait FindSkip {
    fn find_skip(&self, pat: &str) -> Option<usize>;
}

impl FindSkip for str {
    fn find_skip(&self, pat: &str) -> Option<usize> {
        if let Some(pos) = self.find(pat) {
            Some(pos + pat.len())
        } else {
            None
        }
    }
}

trait FindFromSkip {
    fn find_from_skip(&self, pat: &str, start: usize) -> Option<usize>;
}

impl FindFromSkip for str {
    fn find_from_skip(&self, pat: &str, start: usize) -> Option<usize> {
        if let Some(pos) = self[start .. ].find(pat) {
            Some(start + pos + pat.len())
        } else {
            None
        }
    }
}

#[derive(Debug, Default)]
struct Page {
    count: usize,
    index: usize,
    total: usize,
}

fn parse(html: &str, roms: &mut Vec<RomConfig>) -> Page {
    let mut page = Page::default();

    let mut entry = RomConfig::default();
    for line in html.lines() {
        if line.find("<meta name=\"description\" content=\"Now listing roms for ").is_some() {
            if let Some(p) = line.find_skip("Showing ") {
                if let Some(n) = line.find_from(" ", p) {
                    if let Ok(count) = usize::from_str_radix(&line[p .. n].replace(",", ""), 10) {
                        page.count = count;
                    }
                }

                if let Some(p) = line.find_from_skip("index ", p) {
                    if let Some(n) = line.find_from(" ", p) {
                        if let Ok(index) = usize::from_str_radix(&line[p .. n].replace(",", ""), 10) {
                            page.index = index;
                        }
                    }

                    if let Some(p) = line.find_from_skip("of ", p) {
                        if let Some(n) = line.find_from(" ", p) {
                            if let Ok(total) = usize::from_str_radix(&line[p .. n].replace(",", ""), 10) {
                                page.total = total;
                            }
                        }
                    }
                }
            }
        }

        if line.find("").is_some() {
            if let Some(p) = line.find_skip("<img src=\"https://www.doperoms.com/") {
                if let Some(n) = line.find_from(".gif\" ", p) {
                    let flag = line[p .. n].to_string();
                    if flag == "good" {
                        entry.flags.push(RomFlags::Good);
                    } else if flag == "cracked" {
                        entry.flags.push(RomFlags::Cracked);
                    } else if flag == "alternate" {
                        entry.flags.push(RomFlags::Alternate);
                    } else if flag == "trainer" {
                        entry.flags.push(RomFlags::Trainer);
                    } else if flag == "fix" {
                        entry.flags.push(RomFlags::Fix);
                    } else if flag == "hack" {
                        entry.flags.push(RomFlags::Hack);
                    } else if flag == "publicdomain" {
                        entry.flags.push(RomFlags::PublicDomain);
                    } else if flag == "bad" {
                        entry.flags.push(RomFlags::Bad);
                    } else if flag == "overdump" {
                        entry.flags.push(RomFlags::OverDump);
                    }
                }
            }
        }

        if line.find("<a id=\"listing\" ").is_some() {
            if let Some(p) = line.find_skip("name=\"") {
                if let Some(n) = line.find_from("\" ", p) {
                    entry.file = line[p .. n].to_string();
                }

                if let Some(p) = line.find_from_skip("<img src=\\'", p) {
                    if let Some(n) = line.find_from("\\' ", p) {
                        entry.image = line[p .. n].to_string();
                    }

                    if let Some(p) = line.find_from_skip("Game Name: 	", p) {
                        if let Some(n) = line.find_from(" ", p) {
                            entry.name = line[p .. n].to_string();
                        }
                    }
                }
            }

            if entry.file != "No Roms" {
                roms.push(entry);
                entry = RomConfig::default();
            }
        }
    }

    page
}

pub struct List {
    progress: Arc<Mutex<Progress>>,
    result: JoinHandle<Vec<RomConfig>>,
}

impl List {
    pub fn new(system: &str) -> List {
        let progress = Arc::new(Mutex::new(Progress::Connecting));
        let progress_child = progress.clone();
        let system_child = system.to_string();

        let result = thread::spawn(move || -> Vec<RomConfig> {
            let mut roms = Vec::new();

            let client = ClientBuilder::new()
                .gzip(false)
                .build().unwrap();
            let mut next_index = 0;
            'downloading: loop {
                match client
                    .get(&format!("https://doperoms.com/roms/{}/ALL/{}.html", system_child, next_index))
                    .header(CONNECTION, "keep-alive")
                    .header(REFERER, "https://www.doperoms.com/")
                    .send()
                    .and_then(|x| x.error_for_status())
                {
                    Ok(mut res) => {
                        match res.text() {
                            Ok(html) => {
                                let page = parse(&html, &mut roms);

                                if let Ok(mut progress) = progress_child.lock() {
                                    *progress = Progress::InProgress(page.index as u64, page.total as u64);
                                }

                                next_index = page.index + page.count;
                                if next_index >= page.total {
                                    if let Ok(mut progress) = progress_child.lock() {
                                        *progress = Progress::Complete;
                                    }
                                    break 'downloading;
                                }
                            },
                            Err(err) => {
                                if let Ok(mut progress) = progress_child.lock() {
                                    println!("list res.text: {}", err);
                                    *progress = Progress::Error(format!("{}", err));
                                }
                                break 'downloading;
                            }
                        }
                    },
                    Err(err) => {
                        if let Ok(mut progress) = progress_child.lock() {
                            println!("list client send: {}", err);
                            *progress = Progress::Error(format!("{}", err));
                        }
                        break 'downloading;
                    }
                }
            }

            roms
        });

        List {
            progress: progress,
            result: result,
        }
    }

    pub fn progress(&self) -> Progress {
        match self.progress.lock() {
            Ok(progress) => progress.clone(),
            Err(err) => Progress::Error(format!("{}", err))
        }
    }

    pub fn result(self) -> Vec<RomConfig> {
        if let Some(roms) = self.result.join().ok() {
            roms
        } else {
            Vec::new()
        }
    }
}

pub struct Download {
    progress: Arc<Mutex<Progress>>,
    result: JoinHandle<()>
}

impl Download {
    pub fn new(url: &str, path: &Path) -> Download {
        let progress = Arc::new(Mutex::new(Progress::Connecting));
        let progress_child = progress.clone();
        let url_child = url.to_string();
        let path_child = path.to_path_buf();

        let result = thread::spawn(move || {
            if let Some(parent) = path_child.parent() {
                if ! parent.is_dir() {
                    match fs::create_dir_all(parent) {
                        Ok(_) => (),
                        Err(err) => {
                            println!("create dir: {}", err);
                            if let Ok(mut progress) = progress_child.lock() {
                                *progress = Progress::Error(format!("{}", err));
                            }
                            return;
                        }
                    }
                }
            }

            match File::create(&path_child) {
                Ok(mut file) => {
                    match ClientBuilder::new()
                            .build().unwrap()
                            .get(&url_child)
                            .header(CONNECTION, "keep-alive")
                            .header(REFERER, "https://doperoms.com/")
                            .send()
                    {
                        Ok(mut res) => {
                            if let Some(total) = res.headers()
                                .get(CONTENT_LENGTH)
                                .and_then(|x| x.to_str().ok())
                                .and_then(|x| x.parse().ok())
                            {
                                let mut downloaded = 0;
                                if let Ok(mut progress) = progress_child.lock() {
                                    *progress = Progress::InProgress(downloaded, total);
                                }

                                let mut bytes = [0; 4096];
                                'downloading: loop {
                                    match res.read(&mut bytes) {
                                        Ok(0) => {
                                            if let Ok(mut progress) = progress_child.lock() {
                                                *progress = Progress::Complete;
                                            }
                                            break 'downloading;
                                        }
                                        Ok(count) => {
                                            match file.write(&bytes[ .. count]) {
                                                Ok(_) => {
                                                    downloaded += count as u64;
                                                    if let Ok(mut progress) = progress_child.lock() {
                                                        *progress = Progress::InProgress(downloaded, total);
                                                    }
                                                },
                                                Err(err) => {
                                                    println!("file write: {}", err);
                                                    if let Ok(mut progress) = progress_child.lock() {
                                                        *progress = Progress::Error(format!("{}", err));
                                                    }
                                                    break 'downloading;
                                                }
                                            }
                                        }
                                        Err(err) => {
                                            println!("res read: {}", err);
                                            if let Ok(mut progress) = progress_child.lock() {
                                                *progress = Progress::Error(format!("{}", err));
                                            }
                                            break 'downloading;
                                        }
                                    }
                                }
                            } else {
                                println!("no content length");
                                if let Ok(mut progress) = progress_child.lock() {
                                    *progress = Progress::Error("No ContentLength".to_string());
                                }
                            }
                        },
                        Err(err) => {
                            println!("client send: {}", err);
                            if let Ok(mut progress) = progress_child.lock() {
                                *progress = Progress::Error(format!("{}", err));
                            }
                        }
                    }
                },
                Err(err) => {
                    println!("file open: {}", err);
                    if let Ok(mut progress) = progress_child.lock() {
                        *progress = Progress::Error(format!("{}", err));
                    }
                }
            }
        });

        Download {
            progress: progress,
            result: result
        }
    }

    pub fn rom(system: &str, rom: &str, path: &Path) -> Download {
        Download::new(
            &format!("https://doperoms.com/files/roms/{}/GETFILE_{}", system, url::percent_encoding::utf8_percent_encode(rom, url::percent_encoding::DEFAULT_ENCODE_SET)),
            path
        )
    }

    pub fn progress(&self) -> Progress {
        match self.progress.lock() {
            Ok(progress) => progress.clone(),
            Err(err) => Progress::Error(format!("{}", err))
        }
    }

    pub fn result(self) -> bool {
        match self.result.join() {
            Ok(_) => true,
            Err(_) => false
        }
    }
}
<|endoftext|>


<commit_before>use clap::{SubCommand, App, Arg, ArgMatches};

use std::path::Path;
use std::marker::PhantomData;
use std::fs;

use config::ConfigFile;
use node::NodeConfig;
use storage::Storage;
use helpers::generate_testnet_config;

pub struct GenerateCommand<'a, 'b>
    where 'a: 'b
{
    _p: PhantomData<App<'a, 'b>>,
}

// TODO avoid unwraps here, implement Error chain

impl<'a, 'b> GenerateCommand<'a, 'b>
    where 'a: 'b
{
    pub fn new() -> App<'a, 'b> {
        SubCommand::with_name("generate")
            .about("Generates genesis configuration")
            .arg(Arg::with_name("OUTPUT_DIR")
                     .short("o")
                     .long("output-dir")
                     .value_name("OUTPUT_DIR")
                     .required(true)
                     .takes_value(true))
            .arg(Arg::with_name("START_PORT")
                     .short("p")
                     .long("start-port")
                     .value_name("START_PORT")
                     .required(false)
                     .takes_value(true))
            .arg(Arg::with_name("COUNT")
                     .help("Validators count")
                     .required(true)
                     .index(1))
    }

    pub fn output_dir(matches: &'a ArgMatches<'a>) -> &'a Path {
        Path::new(matches.value_of("OUTPUT_DIR").unwrap())
    }

    pub fn validators_count(matches: &'a ArgMatches<'a>) -> u8 {
        matches.value_of("COUNT").unwrap().parse().unwrap()
    }

    pub fn start_port(matches: &'a ArgMatches<'a>) -> Option<u16> {
        matches.value_of("START_PORT").map(|p| p.parse().unwrap())
    }

    pub fn execute(matches: &'a ArgMatches<'a>) {
        let dir = Self::output_dir(matches);
        let count = Self::validators_count(matches);
        let start_port = Self::start_port(matches).unwrap_or_else(|| 2000);

        let dir = dir.join("validators");
        if !dir.exists() {
            fs::create_dir_all(&dir).unwrap();
        }

        let configs = generate_testnet_config(count, start_port);
        for (idx, cfg) in configs.into_iter().enumerate() {
            let file_name = format!("{}.toml", idx);
            ConfigFile::save(&cfg, &dir.join(file_name)).unwrap();
        }
    }
}

pub struct RunCommand<'a, 'b>
    where 'a: 'b
{
    _p: PhantomData<App<'a, 'b>>,
}

impl<'a, 'b> RunCommand<'a, 'b>
    where 'a: 'b
{
    pub fn new() -> App<'a, 'b> {
        SubCommand::with_name("run")
            .arg(Arg::with_name("NODE_CONFIG_PATH")
                     .short("c")
                     .long("node-config")
                     .value_name("NODE_CONFIG_PATH")
                     .help("Path to node configuration file")
                     .required(true)
                     .takes_value(true))
            .arg(Arg::with_name("LEVELDB_PATH")
                     .short("d")
                     .long("leveldb-path")
                     .value_name("LEVELDB_PATH")
                     .help("Use leveldb database with the given path")
                     .required(false)
                     .takes_value(true))
    }

    pub fn node_config_path(matches: &'a ArgMatches<'a>) -> &'a Path {
        Path::new(matches.value_of("NODE_CONFIG_PATH").unwrap())
    }

    pub fn node_config(matches: &'a ArgMatches<'a>) -> NodeConfig {
        let path = Self::node_config_path(matches);
        ConfigFile::load(path).unwrap()
    }

    pub fn leveldb_path(matches: &'a ArgMatches<'a>) -> Option<&'a Path> {
        matches.value_of("LEVELDB_PATH").map(Path::new)
    }

    #[cfg(not(feature="memorydb"))]
    pub fn db(matches: &'a ArgMatches<'a>) -> Storage {
        use storage::{LevelDB, LevelDBOptions};

        let path = Self::leveldb_path(matches).unwrap();
        let mut options = LevelDBOptions::new();
        options.create_if_missing = true;
        LevelDB::new(path, options).unwrap()
    }

    #[cfg(feature="memorydb")]
    pub fn db(_: &'a ArgMatches<'a>) -> Storage {
        use storage::MemoryDB;
        MemoryDB::new()
    }
}
<commit_msg>Add clap helpers for api adresses<commit_after>use clap::{SubCommand, App, Arg, ArgMatches};

use std::path::Path;
use std::marker::PhantomData;
use std::fs;
use std::net::SocketAddr;

use config::ConfigFile;
use node::NodeConfig;
use storage::Storage;
use helpers::generate_testnet_config;

pub struct GenerateCommand<'a, 'b>
    where 'a: 'b
{
    _p: PhantomData<App<'a, 'b>>,
}

// TODO avoid unwraps here, implement Error chain

impl<'a, 'b> GenerateCommand<'a, 'b>
    where 'a: 'b
{
    pub fn new() -> App<'a, 'b> {
        SubCommand::with_name("generate")
            .about("Generates genesis configuration")
            .arg(Arg::with_name("OUTPUT_DIR")
                     .short("o")
                     .long("output-dir")
                     .value_name("OUTPUT_DIR")
                     .required(true)
                     .takes_value(true))
            .arg(Arg::with_name("START_PORT")
                     .short("p")
                     .long("start-port")
                     .value_name("START_PORT")
                     .required(false)
                     .takes_value(true))
            .arg(Arg::with_name("COUNT")
                     .help("Validators count")
                     .required(true)
                     .index(1))
    }

    pub fn output_dir(matches: &'a ArgMatches<'a>) -> &'a Path {
        Path::new(matches.value_of("OUTPUT_DIR").unwrap())
    }

    pub fn validators_count(matches: &'a ArgMatches<'a>) -> u8 {
        matches.value_of("COUNT").unwrap().parse().unwrap()
    }

    pub fn start_port(matches: &'a ArgMatches<'a>) -> Option<u16> {
        matches.value_of("START_PORT").map(|p| p.parse().unwrap())
    }

    pub fn execute(matches: &'a ArgMatches<'a>) {
        let dir = Self::output_dir(matches);
        let count = Self::validators_count(matches);
        let start_port = Self::start_port(matches).unwrap_or_else(|| 2000);

        let dir = dir.join("validators");
        if !dir.exists() {
            fs::create_dir_all(&dir).unwrap();
        }

        let configs = generate_testnet_config(count, start_port);
        for (idx, cfg) in configs.into_iter().enumerate() {
            let file_name = format!("{}.toml", idx);
            ConfigFile::save(&cfg, &dir.join(file_name)).unwrap();
        }
    }
}

pub struct RunCommand<'a, 'b>
    where 'a: 'b
{
    _p: PhantomData<App<'a, 'b>>,
}

impl<'a, 'b> RunCommand<'a, 'b>
    where 'a: 'b
{
    pub fn new() -> App<'a, 'b> {
        SubCommand::with_name("run")
            .about("Run node with given configuration")
            .arg(Arg::with_name("NODE_CONFIG_PATH")
                     .short("c")
                     .long("node-config")
                     .value_name("NODE_CONFIG_PATH")
                     .help("Path to node configuration file")
                     .required(true)
                     .takes_value(true))
            .arg(Arg::with_name("LEVELDB_PATH")
                     .short("d")
                     .long("leveldb-path")
                     .help("Use leveldb database with the given path")
                     .required(false)
                     .takes_value(true))
            .arg(Arg::with_name("PUBLIC_API_ADDRESS")
                     .short("p")
                     .long("public-api-address")
                     .help("Listen address for public api")
                     .required(false)
                     .takes_value(true))
            .arg(Arg::with_name("PRIVATE_API_ADDRESS")
                     .short("s")
                     .long("private-api-address")
                     .help("Listen address for private api")
                     .required(false)
                     .takes_value(true))
    }

    pub fn node_config_path(matches: &'a ArgMatches<'a>) -> &'a Path {
        matches
            .value_of("NODE_CONFIG_PATH")
            .map(Path::new)
            .expect("Path to node configuration is no setted")
    }

    pub fn node_config(matches: &'a ArgMatches<'a>) -> NodeConfig {
        let path = Self::node_config_path(matches);
        ConfigFile::load(path).unwrap()
    }

    pub fn public_api_address(matches: &'a ArgMatches<'a>) -> Option<SocketAddr> {
        matches
            .value_of("PUBLIC_API_ADDRESS")
            .map(|s| s.parse().expect("Public api address has incorrect format"))
    }

    pub fn private_api_address(matches: &'a ArgMatches<'a>) -> Option<SocketAddr> {
        matches
            .value_of("PRIVATE_API_ADDRESS")
            .map(|s| s.parse().expect("Private api address has incorrect format"))
    }

    pub fn leveldb_path(matches: &'a ArgMatches<'a>) -> Option<&'a Path> {
        matches.value_of("LEVELDB_PATH").map(Path::new)
    }

    #[cfg(not(feature="memorydb"))]
    pub fn db(matches: &'a ArgMatches<'a>) -> Storage {
        use storage::{LevelDB, LevelDBOptions};

        let path = Self::leveldb_path(matches).unwrap();
        let mut options = LevelDBOptions::new();
        options.create_if_missing = true;
        LevelDB::new(path, options).unwrap()
    }

    #[cfg(feature="memorydb")]
    pub fn db(_: &'a ArgMatches<'a>) -> Storage {
        use storage::MemoryDB;
        MemoryDB::new()
    }
}
<|endoftext|>


<commit_before>extern crate target_build_utils;
extern crate walkdir;

use std::env;
use std::ffi::OsStr;
use std::ffi::OsString;
use std::fs::File;
use std::io::Write;
use std::path::{Path, PathBuf};
use std::process::Command;
use target_build_utils::TargetInfo;
use walkdir::{DirEntry, WalkDir};

fn build_hermit(src_dir: &Path, target_dir_opt: Option<&Path>) {
	assert!(
		src_dir.exists(),
		"rusty_hermit source folder does not exist"
	);
	let target = TargetInfo::new().expect("Could not get target info");
	let profile = env::var("PROFILE").expect("PROFILE was not set");
	let mut cmd = Command::new("cargo");

	cmd.env_remove("RUSTUP_TOOLCHAIN");
	if option_env!("RUSTC_WORKSPACE_WRAPPER")
		.unwrap_or_default()
		.ends_with("clippy-driver")
	{
		cmd.env("RUSTC_WORKSPACE_WRAPPER", "clippy-driver");
	}

	cmd.env("CARGO_TERM_COLOR", "always");

	if target.target_arch() == "x86_64" {
		cmd.current_dir(src_dir)
			.arg("build")
			.arg("-Z")
			.arg("build-std=core,alloc")
			.arg("--target")
			.arg("x86_64-unknown-none-hermitkernel");
	} else if target.target_arch() == "aarch64" {
		cmd.current_dir(src_dir)
			.arg("build")
			.arg("-Z")
			.arg("build-std=core,alloc")
			.arg("--target")
			.arg("aarch64-unknown-hermit");
	} else {
		panic!("Try to build for an unsupported platform");
	}

	if let Some(target_dir) = target_dir_opt {
		cmd.arg("--target-dir").arg(target_dir);
	}
	let target_dir = match target_dir_opt {
		Some(target_dir) => target_dir.to_path_buf(),
		None => src_dir.join("target"),
	};

	if profile == "release" {
		cmd.arg("--release");
	}

	// disable all default features
	cmd.arg("--no-default-features");

	if target.target_arch() == "aarch64" {
		cmd.arg("--features");
		cmd.arg("aarch64-qemu-stdout");
	}

	// do we have to enable PCI support?
	#[cfg(feature = "pci")]
	{
		cmd.arg("--features");
		cmd.arg("pci");
	}

	// do we have to enable acpi support?
	#[cfg(feature = "acpi")]
	{
		cmd.arg("--features");
		cmd.arg("acpi");
	}

	// do we have to enable FSGSBASE support?
	#[cfg(feature = "fsgs_base")]
	{
		cmd.arg("--features");
		cmd.arg("fsgs_base");
	}

	// do we support multi-processor systems?
	#[cfg(feature = "smp")]
	{
		cmd.arg("--features");
		cmd.arg("smp");
	}

	// do we have to enable VGA support
	#[cfg(feature = "vga")]
	{
		cmd.arg("--features");
		cmd.arg("vga");
	}

	let mut rustflags = vec!["-Zmutable-noalias=no".to_string()];
	let outer_rustflags = env::var("CARGO_ENCODED_RUSTFLAGS").unwrap();

	#[cfg(feature = "instrument")]
	{
		rustflags.push("-Zinstrument-mcount".to_string());
		// Add outer rustflags to command
		rustflags.push(outer_rustflags);
	}

	#[cfg(not(feature = "instrument"))]
	{
		// If the `instrument` feature feature is not enabled,
		// filter it from outer rustflags before adding them to the command.
		if !outer_rustflags.is_empty() {
			let flags = outer_rustflags
				.split('\x1f')
				.filter(|&flag| !flag.contains("instrument-mcount"))
				.map(String::from);
			rustflags.extend(flags);
		}
	}

	cmd.env("CARGO_ENCODED_RUSTFLAGS", rustflags.join("\x1f"));

	let status = cmd.status().expect("failed to start kernel build");
	assert!(status.success());

	let lib_location = if target.target_arch() == "x86_64" {
		target_dir
			.join("x86_64-unknown-none-hermitkernel")
			.join(&profile)
			.canonicalize()
			.unwrap() // Must exist after building
	} else if target.target_arch() == "aarch64" {
		target_dir
			.join("aarch64-unknown-hermit")
			.join(&profile)
			.canonicalize()
			.unwrap() // Must exist after building
	} else {
		panic!("Try to build for an unsupported platform");
	};
	println!("Lib location: {}", lib_location.display());

	let lib = lib_location.join("libhermit.a");

	rename_symbol("rust_begin_unwind", &lib);
	rename_symbol("rust_oom", &lib);

	println!("cargo:rustc-link-search=native={}", lib_location.display());
	println!("cargo:rustc-link-lib=static=hermit");

	// HERMIT_LOG_LEVEL_FILTER sets the log level filter at compile time
	println!("cargo:rerun-if-env-changed=HERMIT_LOG_LEVEL_FILTER");
}

/// Kernel and user space has its own versions of panic handler, oom handler, memcpy, memset, etc,
/// Consequently, we rename the functions in the libos to avoid collisions.
fn rename_symbol(symbol: impl AsRef<OsStr>, lib: impl AsRef<Path>) {
	let arg = IntoIterator::into_iter([symbol.as_ref(), "=kernel-".as_ref(), symbol.as_ref()])
		.collect::<OsString>();
	let status = Command::new("rust-objcopy")
		.arg("--redefine-sym")
		.arg(arg)
		.arg(lib.as_ref())
		.status()
		.expect("Failed to execute rust-objcopy. Is cargo-binutils installed?");
	assert!(status.success(), "rust-objcopy was not successful");
}

#[cfg(all(not(feature = "rustc-dep-of-std"), not(feature = "with_submodule")))]
fn build() {
	#[cfg(windows)]
	let out_dir = env::temp_dir();
	#[cfg(not(windows))]
	let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
	let src_dir = out_dir.join("rusty-hermit");

	if !src_dir.as_path().exists() {
		let status = Command::new(env!("CARGO"))
			.current_dir(out_dir)
			.arg("download")
			.arg("--output")
			.arg(src_dir.clone().into_os_string())
			.arg("--extract")
			.arg("rusty-hermit")
			.status()
			.expect("failed to start kernel download");
		assert!(
			status.success(),
			"Unable to download rusty-hermit. Is cargo-download installed?"
		);
	}

	build_hermit(src_dir.as_ref(), None);
}

#[cfg(all(not(feature = "rustc-dep-of-std"), feature = "with_submodule"))]
fn build() {
	let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
	let target_dir = out_dir.join("target");
	let src_dir = env::current_dir()
		.unwrap()
		.parent()
		.unwrap()
		.join("libhermit-rs");

	build_hermit(src_dir.as_ref(), Some(target_dir.as_ref()));
	configure_cargo_rerun_if_changed(src_dir.as_ref());
}

#[cfg(all(not(feature = "rustc-dep-of-std"), feature = "with_submodule"))]
fn configure_cargo_rerun_if_changed(src_dir: &Path) {
	fn is_not_ignored(entry: &DirEntry) -> bool {
		// Ignore .git .vscode and target directories, but not .cargo or .github
		if entry.depth() == 1
			&& entry.path().is_dir()
			&& (entry.path().ends_with("target")
				|| entry.path().ends_with(".git")
				|| entry.path().ends_with(".vscode"))
		{
			return false;
		}
		true
	}

	WalkDir::new(src_dir)
		.into_iter()
		.filter_entry(is_not_ignored)
		.filter_map(|v| v.ok())
		.filter_map(|v| v.path().canonicalize().ok())
		.for_each(|x| println!("cargo:rerun-if-changed={}", x.display()));
}

#[cfg(not(feature = "rustc-dep-of-std"))]
fn create_constants() {
	let out_dir = env::var("OUT_DIR").expect("No out dir");
	let dest_path = Path::new(&out_dir).join("constants.rs");
	let mut f = File::create(&dest_path).expect("Could not create file");

	let ip = option_env!("HERMIT_IP");
	let ip = ip.map_or("10.0.5.3", |v| v);

	let gateway = option_env!("HERMIT_GATEWAY");
	let gateway = gateway.map_or("10.0.5.1", |v| v);

	let mask = option_env!("HERMIT_MASK");
	let mask = mask.map_or("255.255.255.0", |v| v);

	writeln!(&mut f, "const HERMIT_IP: &str = \"{}\";", ip).expect("Could not write file");
	println!("cargo:rerun-if-env-changed=HERMIT_IP");

	writeln!(&mut f, "const HERMIT_GATEWAY: &str = \"{}\";", gateway)
		.expect("Could not write file");
	println!("cargo:rerun-if-env-changed=HERMIT_GATEWAY");

	writeln!(&mut f, "const HERMIT_MASK: &str = \"{}\";", mask).expect("Could not write file");
	println!("cargo:rerun-if-env-changed=HERMIT_MASK");
}

fn main() {
	#[cfg(not(feature = "rustc-dep-of-std"))]
	create_constants();
	#[cfg(not(feature = "rustc-dep-of-std"))]
	build();
}
<commit_msg>build.rs: Call objcopy only once<commit_after>extern crate target_build_utils;
extern crate walkdir;

use std::borrow::Cow;
use std::env;
use std::ffi::OsStr;
use std::ffi::OsString;
use std::fs::File;
use std::io::Write;
use std::path::{Path, PathBuf};
use std::process::Command;
use target_build_utils::TargetInfo;
use walkdir::{DirEntry, WalkDir};

fn build_hermit(src_dir: &Path, target_dir_opt: Option<&Path>) {
	assert!(
		src_dir.exists(),
		"rusty_hermit source folder does not exist"
	);
	let target = TargetInfo::new().expect("Could not get target info");
	let profile = env::var("PROFILE").expect("PROFILE was not set");
	let mut cmd = Command::new("cargo");

	cmd.env_remove("RUSTUP_TOOLCHAIN");
	if option_env!("RUSTC_WORKSPACE_WRAPPER")
		.unwrap_or_default()
		.ends_with("clippy-driver")
	{
		cmd.env("RUSTC_WORKSPACE_WRAPPER", "clippy-driver");
	}

	cmd.env("CARGO_TERM_COLOR", "always");

	if target.target_arch() == "x86_64" {
		cmd.current_dir(src_dir)
			.arg("build")
			.arg("-Z")
			.arg("build-std=core,alloc")
			.arg("--target")
			.arg("x86_64-unknown-none-hermitkernel");
	} else if target.target_arch() == "aarch64" {
		cmd.current_dir(src_dir)
			.arg("build")
			.arg("-Z")
			.arg("build-std=core,alloc")
			.arg("--target")
			.arg("aarch64-unknown-hermit");
	} else {
		panic!("Try to build for an unsupported platform");
	}

	if let Some(target_dir) = target_dir_opt {
		cmd.arg("--target-dir").arg(target_dir);
	}
	let target_dir = match target_dir_opt {
		Some(target_dir) => target_dir.to_path_buf(),
		None => src_dir.join("target"),
	};

	if profile == "release" {
		cmd.arg("--release");
	}

	// disable all default features
	cmd.arg("--no-default-features");

	if target.target_arch() == "aarch64" {
		cmd.arg("--features");
		cmd.arg("aarch64-qemu-stdout");
	}

	// do we have to enable PCI support?
	#[cfg(feature = "pci")]
	{
		cmd.arg("--features");
		cmd.arg("pci");
	}

	// do we have to enable acpi support?
	#[cfg(feature = "acpi")]
	{
		cmd.arg("--features");
		cmd.arg("acpi");
	}

	// do we have to enable FSGSBASE support?
	#[cfg(feature = "fsgs_base")]
	{
		cmd.arg("--features");
		cmd.arg("fsgs_base");
	}

	// do we support multi-processor systems?
	#[cfg(feature = "smp")]
	{
		cmd.arg("--features");
		cmd.arg("smp");
	}

	// do we have to enable VGA support
	#[cfg(feature = "vga")]
	{
		cmd.arg("--features");
		cmd.arg("vga");
	}

	let mut rustflags = vec!["-Zmutable-noalias=no".to_string()];
	let outer_rustflags = env::var("CARGO_ENCODED_RUSTFLAGS").unwrap();

	#[cfg(feature = "instrument")]
	{
		rustflags.push("-Zinstrument-mcount".to_string());
		// Add outer rustflags to command
		rustflags.push(outer_rustflags);
	}

	#[cfg(not(feature = "instrument"))]
	{
		// If the `instrument` feature feature is not enabled,
		// filter it from outer rustflags before adding them to the command.
		if !outer_rustflags.is_empty() {
			let flags = outer_rustflags
				.split('\x1f')
				.filter(|&flag| !flag.contains("instrument-mcount"))
				.map(String::from);
			rustflags.extend(flags);
		}
	}

	cmd.env("CARGO_ENCODED_RUSTFLAGS", rustflags.join("\x1f"));

	let status = cmd.status().expect("failed to start kernel build");
	assert!(status.success());

	let lib_location = if target.target_arch() == "x86_64" {
		target_dir
			.join("x86_64-unknown-none-hermitkernel")
			.join(&profile)
			.canonicalize()
			.unwrap() // Must exist after building
	} else if target.target_arch() == "aarch64" {
		target_dir
			.join("aarch64-unknown-hermit")
			.join(&profile)
			.canonicalize()
			.unwrap() // Must exist after building
	} else {
		panic!("Try to build for an unsupported platform");
	};
	println!("Lib location: {}", lib_location.display());

	let lib = lib_location.join("libhermit.a");

	let symbols = vec!["rust_begin_unwind", "rust_oom"];
	rename_symbols(symbols, &lib);

	println!("cargo:rustc-link-search=native={}", lib_location.display());
	println!("cargo:rustc-link-lib=static=hermit");

	// HERMIT_LOG_LEVEL_FILTER sets the log level filter at compile time
	println!("cargo:rerun-if-env-changed=HERMIT_LOG_LEVEL_FILTER");
}

/// Kernel and user space has its own versions of panic handler, oom handler, memcpy, memset, etc,
/// Consequently, we rename the functions in the libos to avoid collisions.
fn rename_symbols(symbols: impl IntoIterator<Item = impl AsRef<OsStr>>, lib: impl AsRef<Path>) {
	let args = symbols.into_iter().flat_map(|symbol| {
		let option = OsStr::new("--redefine-sym");
		let arg = [symbol.as_ref(), "=kernel-".as_ref(), symbol.as_ref()]
			.into_iter()
			.collect::<OsString>();
		[Cow::Borrowed(option), Cow::Owned(arg)]
	});

	let status = Command::new("rust-objcopy")
		.args(args)
		.arg(lib.as_ref())
		.status()
		.expect("Failed to execute rust-objcopy. Is cargo-binutils installed?");
	assert!(status.success(), "rust-objcopy was not successful");
}

#[cfg(all(not(feature = "rustc-dep-of-std"), not(feature = "with_submodule")))]
fn build() {
	#[cfg(windows)]
	let out_dir = env::temp_dir();
	#[cfg(not(windows))]
	let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
	let src_dir = out_dir.join("rusty-hermit");

	if !src_dir.as_path().exists() {
		let status = Command::new(env!("CARGO"))
			.current_dir(out_dir)
			.arg("download")
			.arg("--output")
			.arg(src_dir.clone().into_os_string())
			.arg("--extract")
			.arg("rusty-hermit")
			.status()
			.expect("failed to start kernel download");
		assert!(
			status.success(),
			"Unable to download rusty-hermit. Is cargo-download installed?"
		);
	}

	build_hermit(src_dir.as_ref(), None);
}

#[cfg(all(not(feature = "rustc-dep-of-std"), feature = "with_submodule"))]
fn build() {
	let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
	let target_dir = out_dir.join("target");
	let src_dir = env::current_dir()
		.unwrap()
		.parent()
		.unwrap()
		.join("libhermit-rs");

	build_hermit(src_dir.as_ref(), Some(target_dir.as_ref()));
	configure_cargo_rerun_if_changed(src_dir.as_ref());
}

#[cfg(all(not(feature = "rustc-dep-of-std"), feature = "with_submodule"))]
fn configure_cargo_rerun_if_changed(src_dir: &Path) {
	fn is_not_ignored(entry: &DirEntry) -> bool {
		// Ignore .git .vscode and target directories, but not .cargo or .github
		if entry.depth() == 1
			&& entry.path().is_dir()
			&& (entry.path().ends_with("target")
				|| entry.path().ends_with(".git")
				|| entry.path().ends_with(".vscode"))
		{
			return false;
		}
		true
	}

	WalkDir::new(src_dir)
		.into_iter()
		.filter_entry(is_not_ignored)
		.filter_map(|v| v.ok())
		.filter_map(|v| v.path().canonicalize().ok())
		.for_each(|x| println!("cargo:rerun-if-changed={}", x.display()));
}

#[cfg(not(feature = "rustc-dep-of-std"))]
fn create_constants() {
	let out_dir = env::var("OUT_DIR").expect("No out dir");
	let dest_path = Path::new(&out_dir).join("constants.rs");
	let mut f = File::create(&dest_path).expect("Could not create file");

	let ip = option_env!("HERMIT_IP");
	let ip = ip.map_or("10.0.5.3", |v| v);

	let gateway = option_env!("HERMIT_GATEWAY");
	let gateway = gateway.map_or("10.0.5.1", |v| v);

	let mask = option_env!("HERMIT_MASK");
	let mask = mask.map_or("255.255.255.0", |v| v);

	writeln!(&mut f, "const HERMIT_IP: &str = \"{}\";", ip).expect("Could not write file");
	println!("cargo:rerun-if-env-changed=HERMIT_IP");

	writeln!(&mut f, "const HERMIT_GATEWAY: &str = \"{}\";", gateway)
		.expect("Could not write file");
	println!("cargo:rerun-if-env-changed=HERMIT_GATEWAY");

	writeln!(&mut f, "const HERMIT_MASK: &str = \"{}\";", mask).expect("Could not write file");
	println!("cargo:rerun-if-env-changed=HERMIT_MASK");
}

fn main() {
	#[cfg(not(feature = "rustc-dep-of-std"))]
	create_constants();
	#[cfg(not(feature = "rustc-dep-of-std"))]
	build();
}
<|endoftext|>


<commit_before>use std::borrow::Cow;
use std::env;
use std::ffi::{OsStr, OsString};
use std::path::{Path, PathBuf};
use std::process::Command;

fn main() {
	let kernel_src = if has_feature("with_submodule") {
		KernelSrc::from_submodule()
	} else {
		KernelSrc::download()
	};

	kernel_src.build();
}

struct KernelSrc {
	src_dir: PathBuf,
}

impl KernelSrc {
	fn from_submodule() -> Self {
		let mut src_dir = PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").unwrap());
		src_dir.set_file_name("libhermit-rs");
		Self { src_dir }
	}

	fn download() -> Self {
		let out_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap());
		let src_dir = out_dir.join("rusty-hermit");

		if !src_dir.exists() {
			let status = Command::new("cargo")
				.current_dir(out_dir)
				.arg("download")
				.arg("--output")
				.arg(&src_dir)
				.arg("--extract")
				.arg("rusty-hermit")
				.status()
				.expect("failed to start kernel download");
			assert!(
				status.success(),
				"Unable to download rusty-hermit. Is cargo-download installed?"
			);
		}

		Self { src_dir }
	}

	fn build(self) {
		let target_dir = {
			let mut target_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap());
			target_dir.push("target");
			target_dir
		};
		let manifest_path = self.src_dir.join("Cargo.toml");
		assert!(
			manifest_path.exists(),
			"kernel manifest path `{}` does not exist",
			manifest_path.display()
		);
		let target_arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap();
		assert!(
			target_arch == "aarch64" || target_arch == "x86_64",
			"Target {} isn't supported!",
			target_arch
		);
		let profile = env::var("PROFILE").expect("PROFILE was not set");
		let mut cmd = Command::new("cargo");

		let kernel_triple = match target_arch.as_str() {
			"x86_64" => "x86_64-unknown-none-hermitkernel",
			"aarch64" => "aarch64-unknown-none-hermitkernel",
			_ => panic!("Unsupported target arch: {}", target_arch),
		};

		cmd.arg("build")
			.arg("-Z")
			.arg("build-std=core,alloc")
			.arg("--target")
			.arg(kernel_triple)
			.arg("--manifest-path")
			.arg("Cargo.toml");

		cmd.current_dir(&self.src_dir);

		cmd.env_remove("RUSTUP_TOOLCHAIN");
		if option_env!("RUSTC_WORKSPACE_WRAPPER")
			.unwrap_or_default()
			.ends_with("clippy-driver")
		{
			cmd.env("RUSTC_WORKSPACE_WRAPPER", "clippy-driver");
		}

		cmd.env("CARGO_TERM_COLOR", "always");

		cmd.arg("--target-dir").arg(&target_dir);

		if profile == "release" {
			cmd.arg("--release");
		}

		// Control enabled features via this crate's features
		cmd.arg("--no-default-features");
		forward_features(
			&mut cmd,
			["acpi", "fsgsbase", "pci", "smp", "vga"].into_iter(),
		);

		let mut rustflags = vec!["-Zmutable-noalias=no".to_string()];
		let outer_rustflags = env::var("CARGO_ENCODED_RUSTFLAGS").unwrap();

		if has_feature("instrument") {
			rustflags.push("-Zinstrument-mcount".to_string());
			// Add outer rustflags to command
			rustflags.push(outer_rustflags);
		} else {
			// If the `instrument` feature feature is not enabled,
			// filter it from outer rustflags before adding them to the command.
			if !outer_rustflags.is_empty() {
				let flags = outer_rustflags
					.split('\x1f')
					.filter(|&flag| !flag.contains("instrument-mcount"))
					.map(String::from);
				rustflags.extend(flags);
			}
		}

		cmd.env("CARGO_ENCODED_RUSTFLAGS", rustflags.join("\x1f"));

		let status = cmd.status().expect("failed to start kernel build");
		assert!(status.success());

		let lib_location = target_dir
			.join(kernel_triple)
			.join(&profile)
			.canonicalize()
			.unwrap();

		println!("Lib location: {}", lib_location.display());

		let lib = lib_location.join("libhermit.a");

		let mut symbols = vec!["rust_begin_unwind", "rust_oom"];

		if target_arch == "aarch64" {
			symbols.extend(include_str!("aarch64-duplicate-symbols").lines());
		} else if target_arch == "x86_64" {
			symbols.extend(include_str!("x86_64-duplicate-symbols").lines());
		}

		// Kernel and user space has its own versions of panic handler, oom handler, memcpy, memset, etc,
		// Consequently, we rename the functions in the libos to avoid collisions.
		rename_symbols(symbols.iter(), &lib);

		println!("cargo:rustc-link-search=native={}", lib_location.display());
		println!("cargo:rustc-link-lib=static=hermit");

		println!("cargo:rerun-if-changed={}", self.src_dir.display());
		// HERMIT_LOG_LEVEL_FILTER sets the log level filter at compile time
		println!("cargo:rerun-if-env-changed=HERMIT_LOG_LEVEL_FILTER");
	}
}

fn has_feature(feature: &str) -> bool {
	let mut var = "CARGO_FEATURE_".to_string();

	var.extend(feature.chars().map(|c| match c {
		'-' => '_',
		c => c.to_ascii_uppercase(),
	}));

	env::var_os(&var).is_some()
}

fn forward_features<'a>(cmd: &mut Command, features: impl Iterator<Item = &'a str>) {
	let features = features.filter(|f| has_feature(f)).collect::<Vec<_>>();
	if !features.is_empty() {
		cmd.arg("--features");
		cmd.arg(features.join(" "));
	}
}

fn rename_symbols(symbols: impl Iterator<Item = impl AsRef<OsStr>>, lib: &Path) {
	let args = symbols.into_iter().flat_map(|symbol| {
		let option = OsStr::new("--redefine-sym");
		let arg = [symbol.as_ref(), "=kernel-".as_ref(), symbol.as_ref()]
			.into_iter()
			.collect::<OsString>();
		[Cow::Borrowed(option), Cow::Owned(arg)]
	});

	let status = Command::new("rust-objcopy")
		.args(args)
		.arg(lib)
		.status()
		.expect("Failed to execute rust-objcopy. Is cargo-binutils installed?");
	assert!(status.success(), "rust-objcopy was not successful");
}
<commit_msg>remove obsolete assertion<commit_after>use std::borrow::Cow;
use std::env;
use std::ffi::{OsStr, OsString};
use std::path::{Path, PathBuf};
use std::process::Command;

fn main() {
	let kernel_src = if has_feature("with_submodule") {
		KernelSrc::from_submodule()
	} else {
		KernelSrc::download()
	};

	kernel_src.build();
}

struct KernelSrc {
	src_dir: PathBuf,
}

impl KernelSrc {
	fn from_submodule() -> Self {
		let mut src_dir = PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").unwrap());
		src_dir.set_file_name("libhermit-rs");
		Self { src_dir }
	}

	fn download() -> Self {
		let out_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap());
		let src_dir = out_dir.join("rusty-hermit");

		if !src_dir.exists() {
			let status = Command::new("cargo")
				.current_dir(out_dir)
				.arg("download")
				.arg("--output")
				.arg(&src_dir)
				.arg("--extract")
				.arg("rusty-hermit")
				.status()
				.expect("failed to start kernel download");
			assert!(
				status.success(),
				"Unable to download rusty-hermit. Is cargo-download installed?"
			);
		}

		Self { src_dir }
	}

	fn build(self) {
		let target_dir = {
			let mut target_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap());
			target_dir.push("target");
			target_dir
		};
		let manifest_path = self.src_dir.join("Cargo.toml");
		assert!(
			manifest_path.exists(),
			"kernel manifest path `{}` does not exist",
			manifest_path.display()
		);
		let target_arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap();
		let profile = env::var("PROFILE").expect("PROFILE was not set");
		let mut cmd = Command::new("cargo");

		let kernel_triple = match target_arch.as_str() {
			"x86_64" => "x86_64-unknown-none-hermitkernel",
			"aarch64" => "aarch64-unknown-none-hermitkernel",
			_ => panic!("Unsupported target arch: {}", target_arch),
		};

		cmd.arg("build")
			.arg("-Z")
			.arg("build-std=core,alloc")
			.arg("--target")
			.arg(kernel_triple)
			.arg("--manifest-path")
			.arg("Cargo.toml");

		cmd.current_dir(&self.src_dir);

		cmd.env_remove("RUSTUP_TOOLCHAIN");
		if option_env!("RUSTC_WORKSPACE_WRAPPER")
			.unwrap_or_default()
			.ends_with("clippy-driver")
		{
			cmd.env("RUSTC_WORKSPACE_WRAPPER", "clippy-driver");
		}

		cmd.env("CARGO_TERM_COLOR", "always");

		cmd.arg("--target-dir").arg(&target_dir);

		if profile == "release" {
			cmd.arg("--release");
		}

		// Control enabled features via this crate's features
		cmd.arg("--no-default-features");
		forward_features(
			&mut cmd,
			["acpi", "fsgsbase", "pci", "smp", "vga"].into_iter(),
		);

		let mut rustflags = vec!["-Zmutable-noalias=no".to_string()];
		let outer_rustflags = env::var("CARGO_ENCODED_RUSTFLAGS").unwrap();

		if has_feature("instrument") {
			rustflags.push("-Zinstrument-mcount".to_string());
			// Add outer rustflags to command
			rustflags.push(outer_rustflags);
		} else {
			// If the `instrument` feature feature is not enabled,
			// filter it from outer rustflags before adding them to the command.
			if !outer_rustflags.is_empty() {
				let flags = outer_rustflags
					.split('\x1f')
					.filter(|&flag| !flag.contains("instrument-mcount"))
					.map(String::from);
				rustflags.extend(flags);
			}
		}

		cmd.env("CARGO_ENCODED_RUSTFLAGS", rustflags.join("\x1f"));

		let status = cmd.status().expect("failed to start kernel build");
		assert!(status.success());

		let lib_location = target_dir
			.join(kernel_triple)
			.join(&profile)
			.canonicalize()
			.unwrap();

		println!("Lib location: {}", lib_location.display());

		let lib = lib_location.join("libhermit.a");

		let mut symbols = vec!["rust_begin_unwind", "rust_oom"];

		if target_arch == "aarch64" {
			symbols.extend(include_str!("aarch64-duplicate-symbols").lines());
		} else if target_arch == "x86_64" {
			symbols.extend(include_str!("x86_64-duplicate-symbols").lines());
		}

		// Kernel and user space has its own versions of panic handler, oom handler, memcpy, memset, etc,
		// Consequently, we rename the functions in the libos to avoid collisions.
		rename_symbols(symbols.iter(), &lib);

		println!("cargo:rustc-link-search=native={}", lib_location.display());
		println!("cargo:rustc-link-lib=static=hermit");

		println!("cargo:rerun-if-changed={}", self.src_dir.display());
		// HERMIT_LOG_LEVEL_FILTER sets the log level filter at compile time
		println!("cargo:rerun-if-env-changed=HERMIT_LOG_LEVEL_FILTER");
	}
}

fn has_feature(feature: &str) -> bool {
	let mut var = "CARGO_FEATURE_".to_string();

	var.extend(feature.chars().map(|c| match c {
		'-' => '_',
		c => c.to_ascii_uppercase(),
	}));

	env::var_os(&var).is_some()
}

fn forward_features<'a>(cmd: &mut Command, features: impl Iterator<Item = &'a str>) {
	let features = features.filter(|f| has_feature(f)).collect::<Vec<_>>();
	if !features.is_empty() {
		cmd.arg("--features");
		cmd.arg(features.join(" "));
	}
}

fn rename_symbols(symbols: impl Iterator<Item = impl AsRef<OsStr>>, lib: &Path) {
	let args = symbols.into_iter().flat_map(|symbol| {
		let option = OsStr::new("--redefine-sym");
		let arg = [symbol.as_ref(), "=kernel-".as_ref(), symbol.as_ref()]
			.into_iter()
			.collect::<OsString>();
		[Cow::Borrowed(option), Cow::Owned(arg)]
	});

	let status = Command::new("rust-objcopy")
		.args(args)
		.arg(lib)
		.status()
		.expect("Failed to execute rust-objcopy. Is cargo-binutils installed?");
	assert!(status.success(), "rust-objcopy was not successful");
}
<|endoftext|>


<commit_before>use std::io::{self, Read};
use std::fmt;

use {AsyncRead, AsyncWrite};
use codec::Decoder;
use framed::Fuse;

use futures::{Async, AsyncSink, Poll, Stream, Sink, StartSend};
use bytes::BytesMut;

macro_rules! mock {
    ($($x:expr,)*) => {{
        let mut v = VecDeque::new();
        v.extend(vec![$($x),*]);
        Mock { calls: v }
    }};
}

/// Trait of helper objects to write out messages as bytes, for use with
/// `FramedWrite`.
pub trait Encoder {
    /// The type of items consumed by the `Encoder`
    type Item;

    /// The type of encoding errors.
    ///
    /// `FramedWrite` requires `Encoder`s errors to implement `From<io::Error>`
    /// in the interest letting it return `Error`s directly.
    type Error: From<io::Error>;

    /// Encodes a frame into the buffer provided.
    ///
    /// This method will encode `msg` into the byte buffer provided by `buf`.
    /// The `buf` provided is an internal buffer of the `Framed` instance and
    /// will be written out when possible.
    fn encode(&mut self, item: Self::Item, dst: &mut BytesMut)
              -> Result<(), Self::Error>;
}

/// A `Sink` of frames encoded to an `AsyncWrite`.
pub struct FramedWrite<T, E> {
    inner: FramedWrite2<Fuse<T, E>>,
}

pub struct FramedWrite2<T> {
    inner: T,
    buffer: BytesMut,
}

const INITIAL_CAPACITY: usize = 8 * 1024;
const BACKPRESSURE_BOUNDARY: usize = INITIAL_CAPACITY;

impl<T, E> FramedWrite<T, E>
    where T: AsyncWrite,
          E: Encoder,
{
    /// Creates a new `FramedWrite` with the given `encoder`.
    pub fn new(inner: T, encoder: E) -> FramedWrite<T, E> {
        FramedWrite {
            inner: framed_write2(Fuse(inner, encoder)),
        }
    }
}

impl<T, E> FramedWrite<T, E> {
    /// Returns a reference to the underlying I/O stream wrapped by
    /// `FramedWrite`.
    ///
    /// Note that care should be taken to not tamper with the underlying stream
    /// of data coming in as it may corrupt the stream of frames otherwise
    /// being worked with.
    pub fn get_ref(&self) -> &T {
        &self.inner.inner.0
    }

    /// Returns a mutable reference to the underlying I/O stream wrapped by
    /// `FramedWrite`.
    ///
    /// Note that care should be taken to not tamper with the underlying stream
    /// of data coming in as it may corrupt the stream of frames otherwise
    /// being worked with.
    pub fn get_mut(&mut self) -> &mut T {
        &mut self.inner.inner.0
    }

    /// Consumes the `FramedWrite`, returning its underlying I/O stream.
    ///
    /// Note that care should be taken to not tamper with the underlying stream
    /// of data coming in as it may corrupt the stream of frames otherwise
    /// being worked with.
    pub fn into_inner(self) -> T {
        self.inner.inner.0
    }

    /// Returns a reference to the underlying decoder.
    pub fn encoder(&self) -> &E {
        &self.inner.inner.1
    }

    /// Returns a mutable reference to the underlying decoder.
    pub fn encoder_mut(&mut self) -> &mut E {
        &mut self.inner.inner.1
    }
}

impl<T, E> Sink for FramedWrite<T, E>
    where T: AsyncWrite,
          E: Encoder,
{
    type SinkItem = E::Item;
    type SinkError = E::Error;

    fn start_send(&mut self, item: E::Item) -> StartSend<E::Item, E::Error> {
        self.inner.start_send(item)
    }

    fn poll_complete(&mut self) -> Poll<(), Self::SinkError> {
        self.inner.poll_complete()
    }

    fn close(&mut self) -> Poll<(), Self::SinkError> {
        Ok(try!(self.inner.close()))
    }
}

impl<T, D> Stream for FramedWrite<T, D>
    where T: Stream,
{
    type Item = T::Item;
    type Error = T::Error;

    fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
        self.inner.inner.0.poll()
    }
}

impl<T, U> fmt::Debug for FramedWrite<T, U>
    where T: fmt::Debug,
          U: fmt::Debug,
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("FramedWrite")
         .field("inner", &self.inner.get_ref().0)
         .field("encoder", &self.inner.get_ref().1)
         .field("buffer", &self.inner.buffer)
         .finish()
    }
}

// ===== impl FramedWrite2 =====

pub fn framed_write2<T>(inner: T) -> FramedWrite2<T> {
    FramedWrite2 {
        inner: inner,
        buffer: BytesMut::with_capacity(INITIAL_CAPACITY),
    }
}

pub fn framed_write2_with_buffer<T>(inner: T, mut buf: BytesMut) -> FramedWrite2<T> {
    if buf.capacity() < INITIAL_CAPACITY {
        let bytes_to_reserve = INITIAL_CAPACITY - buf.capacity();
        buf.reserve(bytes_to_reserve);
    }
    FramedWrite2 {
        inner: inner,
        buffer: buf,
    }
}

impl<T> FramedWrite2<T> {
    pub fn get_ref(&self) -> &T {
        &self.inner
    }

    pub fn into_inner(self) -> T {
        self.inner
    }

    pub fn into_parts(self) -> (T, BytesMut) {
        (self.inner, self.buffer)
    }

    pub fn get_mut(&mut self) -> &mut T {
        &mut self.inner
    }
}

impl<T> Sink for FramedWrite2<T>
    where T: AsyncWrite + Encoder,
{
    type SinkItem = T::Item;
    type SinkError = T::Error;

    fn start_send(&mut self, item: T::Item) -> StartSend<T::Item, T::Error> {
        // If the buffer is already over 8KiB, then attempt to flush it. If after flushing it's
        // *still* over 8KiB, then apply backpressure (reject the send).
        if self.buffer.len() >= BACKPRESSURE_BOUNDARY {
            try!(self.poll_complete());

            if self.buffer.len() >= BACKPRESSURE_BOUNDARY {
                return Ok(AsyncSink::NotReady(item));
            }
        }

        try!(self.inner.encode(item, &mut self.buffer));

        Ok(AsyncSink::Ready)
    }

    fn poll_complete(&mut self) -> Poll<(), Self::SinkError> {
        trace!("flushing framed transport");

        while !self.buffer.is_empty() {
            trace!("writing; remaining={}", self.buffer.len());

            let n = try_nb!(self.inner.write(&self.buffer));

            if n == 0 {
                return Err(io::Error::new(io::ErrorKind::WriteZero, "failed to
                                          write frame to transport").into());
            }

            // TODO: Add a way to `bytes` to do this w/o returning the drained
            // data.
            let _ = self.buffer.split_to(n);
        }

        // Try flushing the underlying IO
        try_nb!(self.inner.flush());

        trace!("framed transport flushed");
        return Ok(Async::Ready(()));
    }

    fn close(&mut self) -> Poll<(), Self::SinkError> {
        try_ready!(self.poll_complete());
        Ok(try!(self.inner.shutdown()))
    }
}

impl<T: Decoder> Decoder for FramedWrite2<T> {
    type Item = T::Item;
    type Error = T::Error;

    fn decode(&mut self, src: &mut BytesMut) -> Result<Option<T::Item>, T::Error> {
        self.inner.decode(src)
    }

    fn decode_eof(&mut self, src: &mut BytesMut) -> Result<Option<T::Item>, T::Error> {
        self.inner.decode_eof(src)
    }
}

impl<T: Read> Read for FramedWrite2<T> {
    fn read(&mut self, dst: &mut [u8]) -> io::Result<usize> {
        self.inner.read(dst)
    }
}

impl<T: AsyncRead> AsyncRead for FramedWrite2<T> {
    unsafe fn prepare_uninitialized_buffer(&self, buf: &mut [u8]) -> bool {
        self.inner.prepare_uninitialized_buffer(buf)
    }
}
<commit_msg>Remove unused macro<commit_after>use std::io::{self, Read};
use std::fmt;

use {AsyncRead, AsyncWrite};
use codec::Decoder;
use framed::Fuse;

use futures::{Async, AsyncSink, Poll, Stream, Sink, StartSend};
use bytes::BytesMut;

/// Trait of helper objects to write out messages as bytes, for use with
/// `FramedWrite`.
pub trait Encoder {
    /// The type of items consumed by the `Encoder`
    type Item;

    /// The type of encoding errors.
    ///
    /// `FramedWrite` requires `Encoder`s errors to implement `From<io::Error>`
    /// in the interest letting it return `Error`s directly.
    type Error: From<io::Error>;

    /// Encodes a frame into the buffer provided.
    ///
    /// This method will encode `msg` into the byte buffer provided by `buf`.
    /// The `buf` provided is an internal buffer of the `Framed` instance and
    /// will be written out when possible.
    fn encode(&mut self, item: Self::Item, dst: &mut BytesMut)
              -> Result<(), Self::Error>;
}

/// A `Sink` of frames encoded to an `AsyncWrite`.
pub struct FramedWrite<T, E> {
    inner: FramedWrite2<Fuse<T, E>>,
}

pub struct FramedWrite2<T> {
    inner: T,
    buffer: BytesMut,
}

const INITIAL_CAPACITY: usize = 8 * 1024;
const BACKPRESSURE_BOUNDARY: usize = INITIAL_CAPACITY;

impl<T, E> FramedWrite<T, E>
    where T: AsyncWrite,
          E: Encoder,
{
    /// Creates a new `FramedWrite` with the given `encoder`.
    pub fn new(inner: T, encoder: E) -> FramedWrite<T, E> {
        FramedWrite {
            inner: framed_write2(Fuse(inner, encoder)),
        }
    }
}

impl<T, E> FramedWrite<T, E> {
    /// Returns a reference to the underlying I/O stream wrapped by
    /// `FramedWrite`.
    ///
    /// Note that care should be taken to not tamper with the underlying stream
    /// of data coming in as it may corrupt the stream of frames otherwise
    /// being worked with.
    pub fn get_ref(&self) -> &T {
        &self.inner.inner.0
    }

    /// Returns a mutable reference to the underlying I/O stream wrapped by
    /// `FramedWrite`.
    ///
    /// Note that care should be taken to not tamper with the underlying stream
    /// of data coming in as it may corrupt the stream of frames otherwise
    /// being worked with.
    pub fn get_mut(&mut self) -> &mut T {
        &mut self.inner.inner.0
    }

    /// Consumes the `FramedWrite`, returning its underlying I/O stream.
    ///
    /// Note that care should be taken to not tamper with the underlying stream
    /// of data coming in as it may corrupt the stream of frames otherwise
    /// being worked with.
    pub fn into_inner(self) -> T {
        self.inner.inner.0
    }

    /// Returns a reference to the underlying decoder.
    pub fn encoder(&self) -> &E {
        &self.inner.inner.1
    }

    /// Returns a mutable reference to the underlying decoder.
    pub fn encoder_mut(&mut self) -> &mut E {
        &mut self.inner.inner.1
    }
}

impl<T, E> Sink for FramedWrite<T, E>
    where T: AsyncWrite,
          E: Encoder,
{
    type SinkItem = E::Item;
    type SinkError = E::Error;

    fn start_send(&mut self, item: E::Item) -> StartSend<E::Item, E::Error> {
        self.inner.start_send(item)
    }

    fn poll_complete(&mut self) -> Poll<(), Self::SinkError> {
        self.inner.poll_complete()
    }

    fn close(&mut self) -> Poll<(), Self::SinkError> {
        Ok(try!(self.inner.close()))
    }
}

impl<T, D> Stream for FramedWrite<T, D>
    where T: Stream,
{
    type Item = T::Item;
    type Error = T::Error;

    fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
        self.inner.inner.0.poll()
    }
}

impl<T, U> fmt::Debug for FramedWrite<T, U>
    where T: fmt::Debug,
          U: fmt::Debug,
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("FramedWrite")
         .field("inner", &self.inner.get_ref().0)
         .field("encoder", &self.inner.get_ref().1)
         .field("buffer", &self.inner.buffer)
         .finish()
    }
}

// ===== impl FramedWrite2 =====

pub fn framed_write2<T>(inner: T) -> FramedWrite2<T> {
    FramedWrite2 {
        inner: inner,
        buffer: BytesMut::with_capacity(INITIAL_CAPACITY),
    }
}

pub fn framed_write2_with_buffer<T>(inner: T, mut buf: BytesMut) -> FramedWrite2<T> {
    if buf.capacity() < INITIAL_CAPACITY {
        let bytes_to_reserve = INITIAL_CAPACITY - buf.capacity();
        buf.reserve(bytes_to_reserve);
    }
    FramedWrite2 {
        inner: inner,
        buffer: buf,
    }
}

impl<T> FramedWrite2<T> {
    pub fn get_ref(&self) -> &T {
        &self.inner
    }

    pub fn into_inner(self) -> T {
        self.inner
    }

    pub fn into_parts(self) -> (T, BytesMut) {
        (self.inner, self.buffer)
    }

    pub fn get_mut(&mut self) -> &mut T {
        &mut self.inner
    }
}

impl<T> Sink for FramedWrite2<T>
    where T: AsyncWrite + Encoder,
{
    type SinkItem = T::Item;
    type SinkError = T::Error;

    fn start_send(&mut self, item: T::Item) -> StartSend<T::Item, T::Error> {
        // If the buffer is already over 8KiB, then attempt to flush it. If after flushing it's
        // *still* over 8KiB, then apply backpressure (reject the send).
        if self.buffer.len() >= BACKPRESSURE_BOUNDARY {
            try!(self.poll_complete());

            if self.buffer.len() >= BACKPRESSURE_BOUNDARY {
                return Ok(AsyncSink::NotReady(item));
            }
        }

        try!(self.inner.encode(item, &mut self.buffer));

        Ok(AsyncSink::Ready)
    }

    fn poll_complete(&mut self) -> Poll<(), Self::SinkError> {
        trace!("flushing framed transport");

        while !self.buffer.is_empty() {
            trace!("writing; remaining={}", self.buffer.len());

            let n = try_nb!(self.inner.write(&self.buffer));

            if n == 0 {
                return Err(io::Error::new(io::ErrorKind::WriteZero, "failed to
                                          write frame to transport").into());
            }

            // TODO: Add a way to `bytes` to do this w/o returning the drained
            // data.
            let _ = self.buffer.split_to(n);
        }

        // Try flushing the underlying IO
        try_nb!(self.inner.flush());

        trace!("framed transport flushed");
        return Ok(Async::Ready(()));
    }

    fn close(&mut self) -> Poll<(), Self::SinkError> {
        try_ready!(self.poll_complete());
        Ok(try!(self.inner.shutdown()))
    }
}

impl<T: Decoder> Decoder for FramedWrite2<T> {
    type Item = T::Item;
    type Error = T::Error;

    fn decode(&mut self, src: &mut BytesMut) -> Result<Option<T::Item>, T::Error> {
        self.inner.decode(src)
    }

    fn decode_eof(&mut self, src: &mut BytesMut) -> Result<Option<T::Item>, T::Error> {
        self.inner.decode_eof(src)
    }
}

impl<T: Read> Read for FramedWrite2<T> {
    fn read(&mut self, dst: &mut [u8]) -> io::Result<usize> {
        self.inner.read(dst)
    }
}

impl<T: AsyncRead> AsyncRead for FramedWrite2<T> {
    unsafe fn prepare_uninitialized_buffer(&self, buf: &mut [u8]) -> bool {
        self.inner.prepare_uninitialized_buffer(buf)
    }
}
<|endoftext|>


<commit_before>use crate::{
    api::{ColorProxy, Error, LilaVariant},
    indexer::IndexerOpt,
    model::{GameId, Speed, UserName},
};
use chrono::{DateTime, Utc};
use futures_util::stream::{Stream, StreamExt as _, TryStreamExt as _};
use serde::Deserialize;
use serde_with::{
    serde_as, DisplayFromStr, FromInto, SpaceSeparator, StringWithSeparator, TimestampMilliSeconds,
};
use shakmaty::{fen::Fen, san::San, Color};
use std::io;
use tokio::io::AsyncBufReadExt as _;
use tokio_stream::wrappers::LinesStream;
use tokio_util::io::StreamReader;

pub struct Lila {
    client: reqwest::Client,
    opt: IndexerOpt,
}

impl Lila {
    pub fn new(opt: IndexerOpt) -> Lila {
        Lila {
            client: reqwest::Client::builder().build().expect("reqwest client"),
            opt,
        }
    }

    pub async fn user_games(
        &self,
        user: &UserName,
        since_created_at: u64,
    ) -> Result<impl Stream<Item = Result<Game, Error>>, Error> {
        // https://lichess.org/api#operation/apiGamesUser
        let stream = self
            .client
            .get(format!(
                "{}/api/games/user/{}?sort=dateAsc&ongoing=true",
                self.opt.lila, user
            ))
            .query(&[("since", since_created_at)])
            .header("Accept", "application/x-ndjson")
            .send()
            .await
            .and_then(|r| r.error_for_status())
            .map_err(Error::IndexerRequestError)?
            .bytes_stream()
            .map_err(|err| io::Error::new(io::ErrorKind::Other, err));

        Ok(Box::pin(
            LinesStream::new(StreamReader::new(stream).lines()).filter_map(|line| async move {
                match line {
                    Ok(line) if line.is_empty() => None,
                    Ok(line) => Some(serde_json::from_str::<Game>(&line).map_err(|err| {
                        Error::IndexerGameError {
                            err: err.into(),
                            line,
                        }
                    })),
                    Err(err) => Some(Err(Error::IndexerStreamError(err))),
                }
            }),
        ))
    }
}

#[serde_as]
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Game {
    #[serde_as(as = "DisplayFromStr")]
    pub id: GameId,
    pub rated: bool,
    pub created_at: u64,
    #[serde_as(as = "TimestampMilliSeconds")]
    pub last_move_at: DateTime<Utc>,
    pub status: Status,
    pub variant: LilaVariant,
    pub players: Players,
    pub speed: Speed,
    #[serde_as(as = "StringWithSeparator::<SpaceSeparator, San>")]
    pub moves: Vec<San>,
    #[serde_as(as = "Option<FromInto<ColorProxy>>")]
    #[serde(default)]
    pub winner: Option<Color>,
    #[serde_as(as = "Option<DisplayFromStr>")]
    #[serde(default)]
    pub initial_fen: Option<Fen>,
}

impl Game {
    pub fn user_name(&self, color: Color) -> Option<&UserName> {
        self.players.by_color(color).user.as_ref().map(|u| &u.name)
    }
}

#[derive(Debug, Deserialize)]
pub struct Players {
    pub white: Player,
    pub black: Player,
}

impl Players {
    fn by_color(&self, color: Color) -> &Player {
        match color {
            Color::White => &self.white,
            Color::Black => &self.black,
        }
    }
}

#[derive(Debug, Deserialize)]
pub struct Player {
    #[serde(default)]
    pub user: Option<User>,
    #[serde(default)]
    pub rating: Option<u16>,
}

#[serde_as]
#[derive(Debug, Deserialize)]
pub struct User {
    #[serde_as(as = "DisplayFromStr")]
    pub name: UserName,
}

#[derive(Debug, Deserialize, Copy, Clone)]
#[serde(rename_all = "camelCase")]
pub enum Status {
    Created,
    Started,
    Aborted,
    Mate,
    Resign,
    Stalemate,
    Timeout,
    Draw,
    #[serde(rename = "outoftime")]
    OutOfTime,
    Cheat,
    NoStart,
    UnknownFinish,
    VariantEnd,
}

impl Status {
    pub fn is_ongoing(self) -> bool {
        matches!(self, Status::Created | Status::Started)
    }

    pub fn is_unindexable(self) -> bool {
        matches!(
            self,
            Status::UnknownFinish | Status::NoStart | Status::Aborted
        )
    }
}
<commit_msg>use createdAt - 1 to ensure overlap<commit_after>use crate::{
    api::{ColorProxy, Error, LilaVariant},
    indexer::IndexerOpt,
    model::{GameId, Speed, UserName},
};
use chrono::{DateTime, Utc};
use futures_util::stream::{Stream, StreamExt as _, TryStreamExt as _};
use serde::Deserialize;
use serde_with::{
    serde_as, DisplayFromStr, FromInto, SpaceSeparator, StringWithSeparator, TimestampMilliSeconds,
};
use shakmaty::{fen::Fen, san::San, Color};
use std::io;
use tokio::io::AsyncBufReadExt as _;
use tokio_stream::wrappers::LinesStream;
use tokio_util::io::StreamReader;

pub struct Lila {
    client: reqwest::Client,
    opt: IndexerOpt,
}

impl Lila {
    pub fn new(opt: IndexerOpt) -> Lila {
        Lila {
            client: reqwest::Client::builder().build().expect("reqwest client"),
            opt,
        }
    }

    pub async fn user_games(
        &self,
        user: &UserName,
        since_created_at: u64,
    ) -> Result<impl Stream<Item = Result<Game, Error>>, Error> {
        // https://lichess.org/api#operation/apiGamesUser
        let stream = self
            .client
            .get(format!(
                "{}/api/games/user/{}?sort=dateAsc&ongoing=true",
                self.opt.lila, user
            ))
            .query(&[("since", since_created_at.saturating_sub(1))])
            .header("Accept", "application/x-ndjson")
            .send()
            .await
            .and_then(|r| r.error_for_status())
            .map_err(Error::IndexerRequestError)?
            .bytes_stream()
            .map_err(|err| io::Error::new(io::ErrorKind::Other, err));

        Ok(Box::pin(
            LinesStream::new(StreamReader::new(stream).lines()).filter_map(|line| async move {
                match line {
                    Ok(line) if line.is_empty() => None,
                    Ok(line) => Some(serde_json::from_str::<Game>(&line).map_err(|err| {
                        Error::IndexerGameError {
                            err: err.into(),
                            line,
                        }
                    })),
                    Err(err) => Some(Err(Error::IndexerStreamError(err))),
                }
            }),
        ))
    }
}

#[serde_as]
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Game {
    #[serde_as(as = "DisplayFromStr")]
    pub id: GameId,
    pub rated: bool,
    pub created_at: u64,
    #[serde_as(as = "TimestampMilliSeconds")]
    pub last_move_at: DateTime<Utc>,
    pub status: Status,
    pub variant: LilaVariant,
    pub players: Players,
    pub speed: Speed,
    #[serde_as(as = "StringWithSeparator::<SpaceSeparator, San>")]
    pub moves: Vec<San>,
    #[serde_as(as = "Option<FromInto<ColorProxy>>")]
    #[serde(default)]
    pub winner: Option<Color>,
    #[serde_as(as = "Option<DisplayFromStr>")]
    #[serde(default)]
    pub initial_fen: Option<Fen>,
}

impl Game {
    pub fn user_name(&self, color: Color) -> Option<&UserName> {
        self.players.by_color(color).user.as_ref().map(|u| &u.name)
    }
}

#[derive(Debug, Deserialize)]
pub struct Players {
    pub white: Player,
    pub black: Player,
}

impl Players {
    fn by_color(&self, color: Color) -> &Player {
        match color {
            Color::White => &self.white,
            Color::Black => &self.black,
        }
    }
}

#[derive(Debug, Deserialize)]
pub struct Player {
    #[serde(default)]
    pub user: Option<User>,
    #[serde(default)]
    pub rating: Option<u16>,
}

#[serde_as]
#[derive(Debug, Deserialize)]
pub struct User {
    #[serde_as(as = "DisplayFromStr")]
    pub name: UserName,
}

#[derive(Debug, Deserialize, Copy, Clone)]
#[serde(rename_all = "camelCase")]
pub enum Status {
    Created,
    Started,
    Aborted,
    Mate,
    Resign,
    Stalemate,
    Timeout,
    Draw,
    #[serde(rename = "outoftime")]
    OutOfTime,
    Cheat,
    NoStart,
    UnknownFinish,
    VariantEnd,
}

impl Status {
    pub fn is_ongoing(self) -> bool {
        matches!(self, Status::Created | Status::Started)
    }

    pub fn is_unindexable(self) -> bool {
        matches!(
            self,
            Status::UnknownFinish | Status::NoStart | Status::Aborted
        )
    }
}
<|endoftext|>


<commit_before>use core::intrinsics;
use core::mem;

// Kernel-provided user-mode helper functions:
// https://www.kernel.org/doc/Documentation/arm/kernel_user_helpers.txt
unsafe fn __kuser_cmpxchg(oldval: u32, newval: u32, ptr: *mut u32) -> bool {
    let out: u32;
    // FIXME: we can't use BLX on ARMv4
    asm!("blx ${0}"
         : "={r0}" (out)
         : "r" (0xffff0fc0u32)
           "{r0}" (oldval),
           "{r1}" (newval),
           "{r2}" (ptr)
         : "r3", "r12", "lr", "cc", "memory");
    out == 0
}
unsafe fn __kuser_memory_barrier() {
    // FIXME: we can't use BLX on ARMv4
    asm!("blx ${0}"
         :
         : "r" (0xffff0fa0u32)
         : "lr", "memory");
}

// Word-align a pointer
fn align_ptr<T>(ptr: *mut T) -> *mut u32 {
    // This gives us a mask of 0 when T == u32 since the pointer is already
    // supposed to be aligned, which avoids any masking in that case.
    let ptr_mask = 3 & (4 - mem::size_of::<T>());
    (ptr as usize & !ptr_mask) as *mut u32
}

// Calculate the shift and mask of a value inside an aligned word
fn get_shift_mask<T>(ptr: *mut T) -> (u32, u32) {
    // Mask to get the low byte/halfword/word
    let mask = match mem::size_of::<T>() {
        1 => 0xff,
        2 => 0xffff,
        4 => 0xffffffff,
        _ => unreachable!(),
    };

    // If we are on big-endian then we need to adjust the shift accordingly
    let endian_adjust = if cfg!(target_endian = "little") {
        0
    } else {
        4 - mem::size_of::<T>() as u32
    };

    // Shift to get the desired element in the word
    let ptr_mask = 3 & (4 - mem::size_of::<T>());
    let shift = ((ptr as usize & ptr_mask) as u32 ^ endian_adjust) * 8;

    (shift, mask)
}

// Extract a value from an aligned word
fn extract_aligned(aligned: u32, shift: u32, mask: u32) -> u32 {
    (aligned >> shift) & mask
}

// Insert a value into an aligned word
fn insert_aligned(aligned: u32, val: u32, shift: u32, mask: u32) -> u32 {
    (aligned & !(mask << shift)) | ((val & mask) << shift)
}

// Generic atomic read-modify-write operation
unsafe fn atomic_rmw<T, F: Fn(u32) -> u32>(ptr: *mut T, f: F) -> u32 {
    let aligned_ptr = align_ptr(ptr);
    let (shift, mask) = get_shift_mask(ptr);

    loop {
        let curval_aligned = intrinsics::atomic_load_unordered(aligned_ptr);
        let curval = extract_aligned(curval_aligned, shift, mask);
        let newval = f(curval);
        let newval_aligned = insert_aligned(curval_aligned, newval, shift, mask);
        if __kuser_cmpxchg(curval_aligned, newval_aligned, aligned_ptr) {
            return curval;
        }
    }
}

// Generic atomic compare-exchange operation
unsafe fn atomic_cmpxchg<T>(oldval: u32, newval: u32, ptr: *mut T) -> u32 {
    let aligned_ptr = align_ptr(ptr);
    let (shift, mask) = get_shift_mask(ptr);

    loop {
        let curval_aligned = intrinsics::atomic_load_unordered(aligned_ptr);
        let curval = extract_aligned(curval_aligned, shift, mask);
        if curval != oldval {
            return curval;
        }
        let newval_aligned = insert_aligned(curval_aligned, newval, shift, mask);
        if __kuser_cmpxchg(curval_aligned, newval_aligned, aligned_ptr) {
            return oldval;
        }
    }
}

macro_rules! atomic_rmw {
    ($name:ident, $ty:ty, $op:expr) => {
        #[cfg_attr(not(feature = "mangled-names"), no_mangle)]
        pub unsafe extern "C" fn $name(ptr: *mut $ty, val: $ty) -> $ty {
            atomic_rmw(ptr, |x| $op(x as $ty, val) as u32) as $ty
        }
    }
}
macro_rules! atomic_cmpxchg {
    ($name:ident, $ty:ty) => {
        #[cfg_attr(not(feature = "mangled-names"), no_mangle)]
        pub unsafe extern "C" fn $name(oldval: $ty, newval: $ty, ptr: *mut $ty) -> $ty {
            atomic_cmpxchg(oldval as u32, newval as u32, ptr) as $ty
        }
    }
}

atomic_rmw!(__sync_fetch_and_add_1, u8, |a: u8, b: u8| a.wrapping_add(b));
atomic_rmw!(__sync_fetch_and_add_2, u16, |a: u16, b: u16| a.wrapping_add(b));
atomic_rmw!(__sync_fetch_and_add_4, u32, |a: u32, b: u32| a.wrapping_add(b));

atomic_rmw!(__sync_fetch_and_sub_1, u8, |a: u8, b: u8| a.wrapping_sub(b));
atomic_rmw!(__sync_fetch_and_sub_2, u16, |a: u16, b: u16| a.wrapping_sub(b));
atomic_rmw!(__sync_fetch_and_sub_4, u32, |a: u32, b: u32| a.wrapping_sub(b));

atomic_rmw!(__sync_fetch_and_and_1, u8, |a: u8, b: u8| a & b);
atomic_rmw!(__sync_fetch_and_and_2, u16, |a: u16, b: u16| a & b);
atomic_rmw!(__sync_fetch_and_and_4, u32, |a: u32, b: u32| a & b);

atomic_rmw!(__sync_fetch_and_or_1, u8, |a: u8, b: u8| a | b);
atomic_rmw!(__sync_fetch_and_or_2, u16, |a: u16, b: u16| a | b);
atomic_rmw!(__sync_fetch_and_or_4, u32, |a: u32, b: u32| a | b);

atomic_rmw!(__sync_fetch_and_xor_1, u8, |a: u8, b: u8| a ^ b);
atomic_rmw!(__sync_fetch_and_xor_2, u16, |a: u16, b: u16| a ^ b);
atomic_rmw!(__sync_fetch_and_xor_4, u32, |a: u32, b: u32| a ^ b);

atomic_rmw!(__sync_fetch_and_nand_1, u8, |a: u8, b: u8| !a & b);
atomic_rmw!(__sync_fetch_and_nand_2, u16, |a: u16, b: u16| !a & b);
atomic_rmw!(__sync_fetch_and_nand_4, u32, |a: u32, b: u32| !a & b);

atomic_rmw!(__sync_fetch_and_max_1, i8, |a: i8, b: i8| if a > b { a } else { b });
atomic_rmw!(__sync_fetch_and_max_2, i16, |a: i16, b: i16| if a > b { a } else { b });
atomic_rmw!(__sync_fetch_and_max_4, i32, |a: i32, b: i32| if a > b { a } else { b });

atomic_rmw!(__sync_fetch_and_umax_1, u8, |a: u8, b: u8| if a > b { a } else { b });
atomic_rmw!(__sync_fetch_and_umax_2, u16, |a: u16, b: u16| if a > b { a } else { b });
atomic_rmw!(__sync_fetch_and_umax_4, u32, |a: u32, b: u32| if a > b { a } else { b });

atomic_rmw!(__sync_fetch_and_min_1, i8, |a: i8, b: i8| if a < b { a } else { b });
atomic_rmw!(__sync_fetch_and_min_2, i16, |a: i16, b: i16| if a < b { a } else { b });
atomic_rmw!(__sync_fetch_and_min_4, i32, |a: i32, b: i32| if a < b { a } else { b });

atomic_rmw!(__sync_fetch_and_umin_1, u8, |a: u8, b: u8| if a < b { a } else { b });
atomic_rmw!(__sync_fetch_and_umin_2, u16, |a: u16, b: u16| if a < b { a } else { b });
atomic_rmw!(__sync_fetch_and_umin_4, u32, |a: u32, b: u32| if a < b { a } else { b });

atomic_rmw!(__sync_lock_test_and_set_1, u8, |_: u8, b: u8| b);
atomic_rmw!(__sync_lock_test_and_set_2, u16, |_: u16, b: u16| b);
atomic_rmw!(__sync_lock_test_and_set_4, u32, |_: u32, b: u32| b);

atomic_cmpxchg!(__sync_val_compare_and_swap_1, u8);
atomic_cmpxchg!(__sync_val_compare_and_swap_2, u16);
atomic_cmpxchg!(__sync_val_compare_and_swap_4, u32);

#[cfg_attr(not(feature = "mangled-names"), no_mangle)]
pub unsafe extern "C" fn __sync_synchronize() {
    __kuser_memory_barrier();
}
<commit_msg>Auto merge of #218 - Amanieu:blx_fix, r=alexcrichton<commit_after>use core::intrinsics;
use core::mem;

// Kernel-provided user-mode helper functions:
// https://www.kernel.org/doc/Documentation/arm/kernel_user_helpers.txt
unsafe fn __kuser_cmpxchg(oldval: u32, newval: u32, ptr: *mut u32) -> bool {
    let f: extern "C" fn (u32, u32, *mut u32) -> u32 = mem::transmute(0xffff0fc0u32);
    f(oldval, newval, ptr) == 0
}
unsafe fn __kuser_memory_barrier() {
    let f: extern "C" fn () = mem::transmute(0xffff0fa0u32);
    f();
}

// Word-align a pointer
fn align_ptr<T>(ptr: *mut T) -> *mut u32 {
    // This gives us a mask of 0 when T == u32 since the pointer is already
    // supposed to be aligned, which avoids any masking in that case.
    let ptr_mask = 3 & (4 - mem::size_of::<T>());
    (ptr as usize & !ptr_mask) as *mut u32
}

// Calculate the shift and mask of a value inside an aligned word
fn get_shift_mask<T>(ptr: *mut T) -> (u32, u32) {
    // Mask to get the low byte/halfword/word
    let mask = match mem::size_of::<T>() {
        1 => 0xff,
        2 => 0xffff,
        4 => 0xffffffff,
        _ => unreachable!(),
    };

    // If we are on big-endian then we need to adjust the shift accordingly
    let endian_adjust = if cfg!(target_endian = "little") {
        0
    } else {
        4 - mem::size_of::<T>() as u32
    };

    // Shift to get the desired element in the word
    let ptr_mask = 3 & (4 - mem::size_of::<T>());
    let shift = ((ptr as usize & ptr_mask) as u32 ^ endian_adjust) * 8;

    (shift, mask)
}

// Extract a value from an aligned word
fn extract_aligned(aligned: u32, shift: u32, mask: u32) -> u32 {
    (aligned >> shift) & mask
}

// Insert a value into an aligned word
fn insert_aligned(aligned: u32, val: u32, shift: u32, mask: u32) -> u32 {
    (aligned & !(mask << shift)) | ((val & mask) << shift)
}

// Generic atomic read-modify-write operation
unsafe fn atomic_rmw<T, F: Fn(u32) -> u32>(ptr: *mut T, f: F) -> u32 {
    let aligned_ptr = align_ptr(ptr);
    let (shift, mask) = get_shift_mask(ptr);

    loop {
        let curval_aligned = intrinsics::atomic_load_unordered(aligned_ptr);
        let curval = extract_aligned(curval_aligned, shift, mask);
        let newval = f(curval);
        let newval_aligned = insert_aligned(curval_aligned, newval, shift, mask);
        if __kuser_cmpxchg(curval_aligned, newval_aligned, aligned_ptr) {
            return curval;
        }
    }
}

// Generic atomic compare-exchange operation
unsafe fn atomic_cmpxchg<T>(oldval: u32, newval: u32, ptr: *mut T) -> u32 {
    let aligned_ptr = align_ptr(ptr);
    let (shift, mask) = get_shift_mask(ptr);

    loop {
        let curval_aligned = intrinsics::atomic_load_unordered(aligned_ptr);
        let curval = extract_aligned(curval_aligned, shift, mask);
        if curval != oldval {
            return curval;
        }
        let newval_aligned = insert_aligned(curval_aligned, newval, shift, mask);
        if __kuser_cmpxchg(curval_aligned, newval_aligned, aligned_ptr) {
            return oldval;
        }
    }
}

macro_rules! atomic_rmw {
    ($name:ident, $ty:ty, $op:expr) => {
        #[cfg_attr(not(feature = "mangled-names"), no_mangle)]
        pub unsafe extern "C" fn $name(ptr: *mut $ty, val: $ty) -> $ty {
            atomic_rmw(ptr, |x| $op(x as $ty, val) as u32) as $ty
        }
    }
}
macro_rules! atomic_cmpxchg {
    ($name:ident, $ty:ty) => {
        #[cfg_attr(not(feature = "mangled-names"), no_mangle)]
        pub unsafe extern "C" fn $name(oldval: $ty, newval: $ty, ptr: *mut $ty) -> $ty {
            atomic_cmpxchg(oldval as u32, newval as u32, ptr) as $ty
        }
    }
}

atomic_rmw!(__sync_fetch_and_add_1, u8, |a: u8, b: u8| a.wrapping_add(b));
atomic_rmw!(__sync_fetch_and_add_2, u16, |a: u16, b: u16| a.wrapping_add(b));
atomic_rmw!(__sync_fetch_and_add_4, u32, |a: u32, b: u32| a.wrapping_add(b));

atomic_rmw!(__sync_fetch_and_sub_1, u8, |a: u8, b: u8| a.wrapping_sub(b));
atomic_rmw!(__sync_fetch_and_sub_2, u16, |a: u16, b: u16| a.wrapping_sub(b));
atomic_rmw!(__sync_fetch_and_sub_4, u32, |a: u32, b: u32| a.wrapping_sub(b));

atomic_rmw!(__sync_fetch_and_and_1, u8, |a: u8, b: u8| a & b);
atomic_rmw!(__sync_fetch_and_and_2, u16, |a: u16, b: u16| a & b);
atomic_rmw!(__sync_fetch_and_and_4, u32, |a: u32, b: u32| a & b);

atomic_rmw!(__sync_fetch_and_or_1, u8, |a: u8, b: u8| a | b);
atomic_rmw!(__sync_fetch_and_or_2, u16, |a: u16, b: u16| a | b);
atomic_rmw!(__sync_fetch_and_or_4, u32, |a: u32, b: u32| a | b);

atomic_rmw!(__sync_fetch_and_xor_1, u8, |a: u8, b: u8| a ^ b);
atomic_rmw!(__sync_fetch_and_xor_2, u16, |a: u16, b: u16| a ^ b);
atomic_rmw!(__sync_fetch_and_xor_4, u32, |a: u32, b: u32| a ^ b);

atomic_rmw!(__sync_fetch_and_nand_1, u8, |a: u8, b: u8| !a & b);
atomic_rmw!(__sync_fetch_and_nand_2, u16, |a: u16, b: u16| !a & b);
atomic_rmw!(__sync_fetch_and_nand_4, u32, |a: u32, b: u32| !a & b);

atomic_rmw!(__sync_fetch_and_max_1, i8, |a: i8, b: i8| if a > b { a } else { b });
atomic_rmw!(__sync_fetch_and_max_2, i16, |a: i16, b: i16| if a > b { a } else { b });
atomic_rmw!(__sync_fetch_and_max_4, i32, |a: i32, b: i32| if a > b { a } else { b });

atomic_rmw!(__sync_fetch_and_umax_1, u8, |a: u8, b: u8| if a > b { a } else { b });
atomic_rmw!(__sync_fetch_and_umax_2, u16, |a: u16, b: u16| if a > b { a } else { b });
atomic_rmw!(__sync_fetch_and_umax_4, u32, |a: u32, b: u32| if a > b { a } else { b });

atomic_rmw!(__sync_fetch_and_min_1, i8, |a: i8, b: i8| if a < b { a } else { b });
atomic_rmw!(__sync_fetch_and_min_2, i16, |a: i16, b: i16| if a < b { a } else { b });
atomic_rmw!(__sync_fetch_and_min_4, i32, |a: i32, b: i32| if a < b { a } else { b });

atomic_rmw!(__sync_fetch_and_umin_1, u8, |a: u8, b: u8| if a < b { a } else { b });
atomic_rmw!(__sync_fetch_and_umin_2, u16, |a: u16, b: u16| if a < b { a } else { b });
atomic_rmw!(__sync_fetch_and_umin_4, u32, |a: u32, b: u32| if a < b { a } else { b });

atomic_rmw!(__sync_lock_test_and_set_1, u8, |_: u8, b: u8| b);
atomic_rmw!(__sync_lock_test_and_set_2, u16, |_: u16, b: u16| b);
atomic_rmw!(__sync_lock_test_and_set_4, u32, |_: u32, b: u32| b);

atomic_cmpxchg!(__sync_val_compare_and_swap_1, u8);
atomic_cmpxchg!(__sync_val_compare_and_swap_2, u16);
atomic_cmpxchg!(__sync_val_compare_and_swap_4, u32);

#[cfg_attr(not(feature = "mangled-names"), no_mangle)]
pub unsafe extern "C" fn __sync_synchronize() {
    __kuser_memory_barrier();
}
<|endoftext|>