<commit_before>#![feature(box_patterns)]
#[cfg(feature = "trace_log")]
extern crate env_logger;

extern crate radeco_lib;
extern crate r2pipe;
extern crate r2api;
extern crate base64;
extern crate rustyline;

mod cli;
mod core;

use rustyline::{Helper, CompletionType, Config, EditMode, Editor};
use rustyline::hint::Hinter;
use rustyline::highlight::Highlighter;
use rustyline::completion::{Completer, FilenameCompleter};
use rustyline::error::ReadlineError;
use std::fs;

mod scheme {
    pub const HTTP: &'static str = "http://";
    pub const TCP: &'static str = "tcp://";

    pub fn is_http(url: &str) -> bool {
        url.starts_with(HTTP)
    }

    pub fn is_tcp(url: &str) -> bool {
        url.starts_with(TCP)
    }
}

// On unix platforms you can use ANSI escape sequences
#[cfg(unix)]
const PROMPT: &'static str = "\x1b[1;32m>>\x1b[0m ";

// Windows consoles typically don't support ANSI escape sequences out
// of the box
#[cfg(windows)]
const PROMPT: &'static str = ">> ";

#[derive(Default)]
struct Completes {
    file_completer: FilenameCompleter,
}

impl Helper for Completes {}

impl Hinter for Completes {
    fn hint(&self, _line: &str, _pos: usize) -> Option<String> {
        None
    }
}

impl Highlighter for Completes {}

impl Completer for Completes {
    type Candidate = String;
    fn complete(&self, line: &str, _pos: usize) -> rustyline::Result<(usize, Vec<String>)> {
        // TODO Completion for function names
        let cmds = vec![
            command::HELP,
            command::LOAD,
            command::CONNECT,
            command::FNLIST,
            command::ANALYZE,
            command::DOT,
            command::IR,
            command::DECOMPILE,
        ];
        let mut ret: Vec<String> = cmds.into_iter()
            .filter(|s| s.starts_with(line))
            .map(|s| s.to_string())
            .collect();
        match self.file_completer.complete(line, _pos) {
            Ok((n, ss)) => {
                let mut completed_lines = ss.into_iter()
                    .map(|s| format!("{}{}", &line[..n], s.display))
                    .collect();
                ret.append(&mut completed_lines);
            }
            Err(_) => {}
        }

        Ok((0, ret))
    }
}

const SEP: &'static str = "END";
const USAGE: &'static str = "
Usage:
  radeco <bin>
  radeco [--append]
  radeco (--help | --version)

  Options:
  -h, --help        Show this screen.
  -v, --version     Show current version.
  --bin             Load a program.
  --append          Append separator to the end of every output.
";

fn main() {
    let (arg, is_append_mode) = cli::parse_args(USAGE);
    let config = Config::builder()
        .auto_add_history(true)
        .history_ignore_space(true)
        .completion_type(CompletionType::List)
        .edit_mode(EditMode::Emacs)
        .build();
    let mut rl = Editor::with_config(config);
    rl.set_helper(Some(Completes::default()));
    core::PROJ.with(move |proj| {
        *proj.borrow_mut() = arg.and_then(|ref s| {
            if scheme::is_http(s) {
                core::load_proj_http(&s[scheme::HTTP.len()..])
            } else if scheme::is_tcp(s) {
                core::load_proj_tcp(&s[scheme::TCP.len()..])
            } else if is_file(s) {
                Ok(core::load_proj_by_path(s))
            } else {
                Err("Invalid argument")
            }.ok()
        });
    });

    loop {
        let readline = rl.readline(PROMPT);
        match readline {
            Ok(line) => {
                let mut terms = line.split_whitespace();
                let o1 = terms.next();
                let o2 = terms.next();
                cmd(o1, o2);
                if is_append_mode {
                    println!("{}", SEP);
                }
            }
            Err(ReadlineError::Interrupted) |
            Err(ReadlineError::Eof) => break,
            Err(err) => {
                println!("Error: {:?}", err);
                break;
            }
        }
    }
}

mod command {
    pub const HELP: &'static str = "help";
    pub const LOAD: &'static str = "load";
    pub const CONNECT: &'static str = "connect";
    pub const FNLIST: &'static str = "fn_list";
    pub const ANALYZE: &'static str = "analyze";
    pub const DOT: &'static str = "dot";
    pub const IR: &'static str = "ir";
    pub const DECOMPILE: &'static str = "decompile";

    pub fn help() {
        let width = 30;
        println!("{:width$}    Show this help", HELP, width = width);
        println!(
            "{:width$}    Load binary",
            format!("{} path", LOAD),
            width = width
        );
        println!(
            "{:width$}    Connect to radare2 server",
            format!("{} (http|tcp)://<url>", CONNECT),
            width = width
        );
        println!(
            "{:width$}    Show function list",
            FNLIST,
            width = width
        );
        println!(
            "{:width$}    Analyze <func>",
            format!("{} <func>", ANALYZE),
            width = width
        );
        println!(
            "{:width$}    Emit IR of <func>",
            format!("{} <func>", IR),
            width = width
        );
        println!(
            "{:width$}    Emit graph of the IR in Graphviz dot",
            format!("{} <func>", DOT),
            width = width
        );
        println!(
            "{:width$}    Decompile <func>",
            format!("{} <func>", DECOMPILE),
            width = width
        );
    }
}

fn cmd(op1: Option<&str>, op2: Option<&str>) {
    core::PROJ.with(|proj_opt| {
        match (op1, op2) {
            (Some(command::HELP), _) => {
                command::help();
                return;
            }
            (Some(command::LOAD), Some(path)) => {
                if is_file(path) {
                    *proj_opt.borrow_mut() = Some(core::load_proj_by_path(path));
                    return;
                } else {
                    println!("{} is not found.", path);
                    return;
                }
            }
            (Some(command::CONNECT), Some(url)) => {
                let p_opt = if scheme::is_http(&url) {
                    core::load_proj_http(&url[scheme::HTTP.len()..])
                } else if scheme::is_tcp(&url) {
                    core::load_proj_tcp(&url[scheme::TCP.len()..])
                } else {
                    Err("Invalid url")
                };
                match p_opt {
                    Ok(p) => *proj_opt.borrow_mut() = Some(p),
                    Err(msg) => println!("{}", msg),
                }
                return;
            }
            _ => {}
        };
        if proj_opt.borrow().is_none() {
            println!("Load a project first");
            return;
        }
        let mut proj_ = proj_opt.borrow_mut();
        let proj = proj_.as_mut().unwrap();
        match (op1, op2) {
            (Some(command::ANALYZE), Some("*")) => {
                let rfns = proj.iter_mut().map(|i| i.module).flat_map(|rmod| {
                    rmod.functions.values_mut()
                });
                for rfn in rfns {
                    core::analyze(rfn);
                }
            }
            (Some(command::FNLIST), _) => {
                let funcs = core::fn_list(&proj);
                println!("{}", funcs.join("\n"));
            }
            // TODO Show list of dependency information of analyses
            // TODO Add command for individual analyses
            (Some(command::ANALYZE), Some(f)) => {
                if let Some(rfn) = core::get_function_mut(f, proj) {
                    core::analyze(rfn);
                } else {
                    println!("{} is not found", f);
                }
            }
            (Some(command::DOT), Some(f)) => {
                if let Some(rfn) = core::get_function(f, &proj) {
                    println!("{}", core::emit_dot(rfn.ssa()));
                } else {
                    println!("{} is not found", f);
                }
            }
            (Some(command::IR), Some(f)) => {
                if let Some(rfn) = core::get_function(f, &proj) {
                    println!("{}", core::emit_ir(rfn));
                } else {
                    println!("{} is not found", f);
                }
            }
            (Some(command::DECOMPILE), Some(f)) => {
                if let Some(rfn) = core::get_function(f, &proj) {
                    let rmod = proj.iter().map(|i| i.module).next().unwrap();
                    let func_name_map = core::func_names(&rmod);
                    let strings = core::strings(&rmod);
                    match core::decompile(rfn, &func_name_map, &strings) {
                        Ok(res) => println!("{}", res),
                        Err(err) => println!("{}", err),
                    }
                } else {
                    println!("{} is not found", f);
                }
            }
            _ => {
                println!(
                    "Invalid command {} {}",
                    op1.unwrap_or(""),
                    op2.unwrap_or("")
                );
            }
        }
    });
}

fn is_file(path: &str) -> bool {
    fs::metadata(path).map(|f| f.is_file()).unwrap_or(false)
}
<commit_msg>[CLI] Usability fixes<commit_after>#![feature(box_patterns)]
#[cfg(feature = "trace_log")]
extern crate env_logger;

extern crate radeco_lib;
extern crate r2pipe;
extern crate r2api;
extern crate base64;
extern crate rustyline;

mod cli;
mod core;

use rustyline::{Helper, CompletionType, Config, EditMode, Editor};
use rustyline::hint::Hinter;
use rustyline::highlight::Highlighter;
use rustyline::completion::{Completer, FilenameCompleter};
use rustyline::error::ReadlineError;
use std::fs;

mod scheme {
    pub const HTTP: &'static str = "http://";
    pub const TCP: &'static str = "tcp://";

    pub fn is_http(url: &str) -> bool {
        url.starts_with(HTTP)
    }

    pub fn is_tcp(url: &str) -> bool {
        url.starts_with(TCP)
    }
}

// On unix platforms you can use ANSI escape sequences
#[cfg(unix)]
const PROMPT: &'static str = "\x1b[1;32m>>\x1b[0m ";
// File seperator for *nix based platforms
#[cfg(unix)]
const FILE_SP: char = '/';

// Windows consoles typically don't support ANSI escape sequences out
// of the box
#[cfg(windows)]
const PROMPT: &'static str = ">> ";
#[cfg(windows)]
// File seperator for Windows
const FILE_SP: char = '\\';

#[derive(Default)]
struct Completes {
    file_completer: FilenameCompleter,
}

impl Helper for Completes {}

impl Hinter for Completes {
    fn hint(&self, _line: &str, _pos: usize) -> Option<String> {
        None
    }
}

impl Highlighter for Completes {}

impl Completer for Completes {
    type Candidate = String;
    fn complete(&self, line: &str, _pos: usize) -> rustyline::Result<(usize, Vec<String>)> {
        // TODO Completion for function names
        let cmds = vec![
            command::HELP,
            command::LOAD,
            command::CONNECT,
            command::FNLIST,
            command::ANALYZE,
            command::DOT,
            command::IR,
            command::DECOMPILE,
        ];
        let mut ret: Vec<String> = cmds.into_iter()
            .filter(|s| s.starts_with(line))
            .map(|s| s.to_string())
            .collect();
        match self.file_completer.complete(line, _pos) {
            Ok((n, ss)) => {
                let mut completed_lines = ss.into_iter()
                    .map(|s| {
                        if let Some (sep_loc) = line.rfind (FILE_SP) {
                            format! ("{}{}", &line[..sep_loc + 1], s.display)
                        } else {
                            format! ("{}{}", &line[..n], s.display)
                        }
                    })
                    .collect();
                ret.append(&mut completed_lines);
            }
            Err(_) => {}
        }

        Ok((0, ret))
    }
}

const SEP: &'static str = "END";
const USAGE: &'static str = "
Usage:
  radeco <bin>
  radeco [--append]
  radeco (--help | --version)

  Options:
  -h, --help        Show this screen.
  -v, --version     Show current version.
  --bin             Load a program.
  --append          Append separator to the end of every output.
";

fn main() {
    let (arg, is_append_mode) = cli::parse_args(USAGE);
    let config = Config::builder()
        .auto_add_history(true)
        .history_ignore_space(true)
        .completion_type(CompletionType::List)
        .edit_mode(EditMode::Emacs)
        .build();
    let mut rl = Editor::with_config(config);
    rl.set_helper(Some(Completes::default()));
    core::PROJ.with(move |proj| {
        *proj.borrow_mut() = arg.and_then(|ref s| {
            if scheme::is_http(s) {
                core::load_proj_http(&s[scheme::HTTP.len()..])
            } else if scheme::is_tcp(s) {
                core::load_proj_tcp(&s[scheme::TCP.len()..])
            } else if is_file(s) {
                Ok(core::load_proj_by_path(s))
            } else {
                Err("Invalid argument")
            }.ok()
        });
    });

    loop {
        let readline = rl.readline(PROMPT);
        match readline {
            Ok(line) => {
                if !line.is_empty () {
                    let mut terms = line.split_whitespace();
                    let o1 = terms.next();
                    let o2 = terms.next();
                    cmd(o1, o2);
                    if is_append_mode {
                        println!("{}", SEP);
                    }
                }
            }
            Err(ReadlineError::Interrupted) => {},
            Err(ReadlineError::Eof) => break,
            Err(err) => {
                println!("Error: {:?}", err);
                break;
            }
        }
    }
}

mod command {
    pub const HELP: &'static str = "help";
    pub const LOAD: &'static str = "load";
    pub const CONNECT: &'static str = "connect";
    pub const FNLIST: &'static str = "fn_list";
    pub const ANALYZE: &'static str = "analyze";
    pub const DOT: &'static str = "dot";
    pub const IR: &'static str = "ir";
    pub const DECOMPILE: &'static str = "decompile";

    pub fn help() {
        let width = 30;
        println!("{:width$}    Show this help", HELP, width = width);
        println!(
            "{:width$}    Load binary",
            format!("{} path", LOAD),
            width = width
        );
        println!(
            "{:width$}    Connect to radare2 server",
            format!("{} (http|tcp)://<url>", CONNECT),
            width = width
        );
        println!(
            "{:width$}    Show function list",
            FNLIST,
            width = width
        );
        println!(
            "{:width$}    Analyze <func>",
            format!("{} <func>", ANALYZE),
            width = width
        );
        println!(
            "{:width$}    Emit IR of <func>",
            format!("{} <func>", IR),
            width = width
        );
        println!(
            "{:width$}    Emit graph of the IR in Graphviz dot",
            format!("{} <func>", DOT),
            width = width
        );
        println!(
            "{:width$}    Decompile <func>",
            format!("{} <func>", DECOMPILE),
            width = width
        );
    }
}

fn cmd(op1: Option<&str>, op2: Option<&str>) {
    core::PROJ.with(|proj_opt| {
        match (op1, op2) {
            (Some(command::HELP), _) => {
                command::help();
                return;
            }
            (Some(command::LOAD), Some(path)) => {
                if is_file(path) {
                    *proj_opt.borrow_mut() = Some(core::load_proj_by_path(path));
                    return;
                } else {
                    println!("{} is not found.", path);
                    return;
                }
            }
            (Some(command::CONNECT), Some(url)) => {
                let p_opt = if scheme::is_http(&url) {
                    core::load_proj_http(&url[scheme::HTTP.len()..])
                } else if scheme::is_tcp(&url) {
                    core::load_proj_tcp(&url[scheme::TCP.len()..])
                } else {
                    Err("Invalid url")
                };
                match p_opt {
                    Ok(p) => *proj_opt.borrow_mut() = Some(p),
                    Err(msg) => println!("{}", msg),
                }
                return;
            }
            _ => {}
        };
        if proj_opt.borrow().is_none() {
            println!("Load a project first");
            return;
        }
        let mut proj_ = proj_opt.borrow_mut();
        let proj = proj_.as_mut().unwrap();
        match (op1, op2) {
            (Some(command::ANALYZE), Some("*")) => {
                let rfns = proj.iter_mut().map(|i| i.module).flat_map(|rmod| {
                    rmod.functions.values_mut()
                });
                for rfn in rfns {
                    core::analyze(rfn);
                }
            }
            (Some(command::FNLIST), _) => {
                let funcs = core::fn_list(&proj);
                println!("{}", funcs.join("\n"));
            }
            // TODO Show list of dependency information of analyses
            // TODO Add command for individual analyses
            (Some(command::ANALYZE), Some(f)) => {
                if let Some(rfn) = core::get_function_mut(f, proj) {
                    core::analyze(rfn);
                } else {
                    println!("{} is not found", f);
                }
            }
            (Some(command::DOT), Some(f)) => {
                if let Some(rfn) = core::get_function(f, &proj) {
                    println!("{}", core::emit_dot(rfn.ssa()));
                } else {
                    println!("{} is not found", f);
                }
            }
            (Some(command::IR), Some(f)) => {
                if let Some(rfn) = core::get_function(f, &proj) {
                    println!("{}", core::emit_ir(rfn));
                } else {
                    println!("{} is not found", f);
                }
            }
            (Some(command::DECOMPILE), Some(f)) => {
                if let Some(rfn) = core::get_function(f, &proj) {
                    let rmod = proj.iter().map(|i| i.module).next().unwrap();
                    let func_name_map = core::func_names(&rmod);
                    let strings = core::strings(&rmod);
                    match core::decompile(rfn, &func_name_map, &strings) {
                        Ok(res) => println!("{}", res),
                        Err(err) => println!("{}", err),
                    }
                } else {
                    println!("{} is not found", f);
                }
            }
            _ => {
                println!(
                    "Invalid command {} {}",
                    op1.unwrap_or(""),
                    op2.unwrap_or("")
                );
            }
        }
    });
}

fn is_file(path: &str) -> bool {
    fs::metadata(path).map(|f| f.is_file()).unwrap_or(false)
}
<|endoftext|>


<commit_before>use std::sync::atomic::{self, AtomicPtr};

pub struct Stack<T> {
    head: AtomicPtr<Node<T>>,
}

struct Node<T> {
    data: T,
    next: *mut Node<T>,
}

impl<T> Stack<T> {
    fn new() -> Stack<T> {
        Stack {
            head: AtomicPtr::default,
        }
    }

    fn push(&self, t: T) {
        // Allocate the node, and immediately turn it into a `*mut` pointer.
        let n = Box::into_raw(Box::new(Node {
            data: t,
            next: null_mut(),
        }));

        loop {
            // Snapshot current head.
            let head = self.head.load(atomic::Ordering::Relaxed);

            // Update `next` pointer with snapshot.
            unsafe { (*n).next = head; }

            // If snapshot is still good, link in new node.
            if self.head.compare_and_swap(head, n, atomic::Ordering::Release) == head {
                break
            }
        }
    }

    fn take_each(&self, f: F)
    where F: Fn(T) {
        // Replace the old head with a null pointer.
        let mut node = self.head.swap(AtomicPtr::default(), atomic::Ordering::Acquire);

        // We traverse every node until the pointer is null.
        while !node.is_null() {
            // Read the node into an owned box.
            let bx = unsafe { Box::from_raw(head) };
            // Apply the provided closure.
            f(bx.data);
            // Go to the next link.
            node = bx.next;
        }
    }
}

struct RawReader {
    active: *const AtomicBool,
    ptr: *const T,
}

impl RawReader {
    unsafe fn destroy(self) {
        // Drop the atomic boolean stored on the heap.
        mem::drop_in_place(self.active);
    }
}

struct Reader<'a, T> {
    raw: RawReader,
    _marker: PhantomData<'a>,
}

impl<'a, T> Reader<'a, T> {
    fn drop(&mut self) {
        self.raw.active.store(true);
    }
}

struct Atomic<T> {
    inner: AtomicPtr<T>,
    snapshots: Stack<Box<T>>,
    readers: Stack<RawReader>,
    flags: AtomicUsize,
}

impl<T> Atomic<T> {
    fn new(inner: T) -> Atomic<T> {
        Atomic {
            inner: AtomicPtr::new(Box::into_raw(Box::new())),
            snapshots: Stack::new(),
            readers: Stack::new(),
            // Initially no one is accessing the readers stack nor garbage collecting, so the flags
            // are set to zero.
            flags: AtomicUsize::new(0),
        }
    }

    fn gc(&self) {
        // Spin until no thread is currently modifying the stacks. This prevents premature frees in
        // the thread which is currently pushing to `self.readers`.
        loop {
            // Read the flags, and if no readers or garbage collectors, activate garbage
            // collection.
            let flags = self.flags.compare_and_swap(0, 1, atomic::Ordering::Relaxed);
            if flags == 0 {
                // Currently, no one accesses the readers stack and the CAS above means that the
                // lowest bitflag have been set, indicating that a garbage collection is now
                // active.
                break;
            } else if flags & 1 == 1 {
                // Another thread is currently garbage collection. No need for this thread doing
                // the same.
                return;
            }
        }

        // We've now set the lowest bit in `self.active` namely the flag defining if we are
        // collecting.

        // Initially, every snapshot is marked unused.
        let mut unused = self.snapshots.collect();

        // Traverse the readers and update the reference counts.
        self.readers.take_each(|reader| {
            if reader.active.load() {
                // The reader is not released yet, and is thus considered active.

                // Remove the reader from the unused set and insert it back into the log (if it
                // exists in the unused set), as the snapshot is active.
                unused.remove(reader.ptr).map(|x| self.snapshots.insert(x));
                // Put the reader back in the structure.
                self.readers.insert(reader);
            } else {
                // The reader was released. Destroy it.
                reader.destroy();
            }
        });

        // Unset the bitflag defining if currently garbage collecting.
        self.flags.fetch_and(!1, atomic::Ordering::Relaxed);
    }

    fn get(&self) -> Reader {
        // Increment the number of threads currently pushing to the readers stack. We add two to
        // account for the LSB being a separate bitflag. This ensures that the read snapshot isn't
        // freed while registering it in the reader stack.
        self.flags.fetch_add(2, atomic::Ordering::Relaxed);

        // Construct the raw reader.
        let reader = RawReader {
            // Load a snapshot of the pointer.
            ptr: self.inner.load(atomic::Ordering::Relaxed),
            // We allocate the atomic boolean on the heap as it is shared between the returned RAII
            // guard and the reader stack.
            released: Box::into_raw(Box::new(AtomicBool::new(false))),
        };

        // Register the reader through the reader stack, ensuring that it is not freed before the
        // RAII guard drops (`reader.release` is set to `true`).
        self.readers.push(reader);

        // Revert the original increment.
        self.flags.fetch_sub(2, atomic::Ordering::Relaxed);

        Reader {
            raw: reader,
            _marker: PhantomData,
        }
    }

    fn set(&self, new: Box<T>) {
        // Replace the inner by the new value.
        let old = self.inner.swap(Box::into_raw(new), atomic::Ordering::Relaxed);
        // Push the old pointer to the snapshot stack.
        self.snapshots.push(Box::from_raw(old));
    }
}
<commit_msg>Factor out the state part of the reclamation to its own type.<commit_after>use std::sync::atomic::{self, AtomicPtr};

pub struct Stack<T> {
    head: AtomicPtr<Node<T>>,
}

struct Node<T> {
    data: T,
    next: *mut Node<T>,
}

impl<T> Stack<T> {
    fn new() -> Stack<T> {
        Stack {
            head: AtomicPtr::default,
        }
    }

    fn push(&self, t: T) {
        // Allocate the node, and immediately turn it into a `*mut` pointer.
        let n = Box::into_raw(Box::new(Node {
            data: t,
            next: null_mut(),
        }));

        loop {
            // Snapshot current head.
            let head = self.head.load(atomic::Ordering::Relaxed);

            // Update `next` pointer with snapshot.
            unsafe { (*n).next = head; }

            // If snapshot is still good, link in new node.
            if self.head.compare_and_swap(head, n, atomic::Ordering::Release) == head {
                break
            }
        }
    }

    fn take_each(&self, f: F)
    where F: Fn(T) {
        // Replace the old head with a null pointer.
        let mut node = self.head.swap(AtomicPtr::default(), atomic::Ordering::Acquire);

        // We traverse every node until the pointer is null.
        while !node.is_null() {
            // Read the node into an owned box.
            let bx = unsafe { Box::from_raw(head) };
            // Apply the provided closure.
            f(bx.data);
            // Go to the next link.
            node = bx.next;
        }
    }
}

struct RawReader {
    active: *const AtomicBool,
    ptr: *const T,
}

impl RawReader {
    unsafe fn destroy(self) {
        // Drop the atomic boolean stored on the heap.
        mem::drop_in_place(self.active);
    }
}

struct Reader<'a, T> {
    raw: RawReader,
    _marker: PhantomData<'a>,
}

impl<'a, T> Reader<'a, T> {
    fn drop(&mut self) {
        self.raw.active.store(true);
    }
}

#[derive(Default)]
struct State {
    flags: AtomicUsize,
}

impl State {
    fn start_gc(&self) -> false {
        // Mark that a garbage collection is pending.
        if self.flags.fetch_or(1, atomic::Ordering::Relaxed) & 1 != 0 {
            // Another thread is pending to or currently garbage collecting, so we won't do the
            // same.
            return false;
        }

        // Spin until no thread is currently modifying the stacks. This prevents premature frees in
        // the thread which is currently pushing to `self.readers`.
        loop {
            // Read the flags, and if no readers or garbage collectors, activate garbage
            // collection.
            let flags = self.flags.compare_and_swap(1, 0b11, atomic::Ordering::Relaxed);
            if flags == 1 {
                // Currently, no one accesses the readers stack and the CAS above means that the
                // lowest bitflag have been set, indicating that a garbage collection is now
                // active.
                return true;
            }
        }
    }

    fn end_gc(&self) {
        self.flags.fetch_sub(1, atomic::Ordering::Relaxed);
    }

    fn start_read(&self) {
        // Increment the number of threads currently pushing to the readers stack. We add two to
        // account for the LSB being a separate bitflag.
        self.flags.fetch_add(2, atomic::Ordering::Relaxed);
    }

    fn end_read(&self) {
        self.flags.fetch_sub(2, atomic::Ordering::Relaxed);
    }
}

struct Atomic<T> {
    inner: AtomicPtr<T>,
    snapshots: Stack<Box<T>>,
    readers: Stack<RawReader>,
    state: State,
}

impl<T> Atomic<T> {
    fn new(inner: T) -> Atomic<T> {
        Atomic {
            inner: AtomicPtr::new(Box::into_raw(Box::new(inner))),
            snapshots: Stack::new(),
            readers: Stack::new(),
            flags: State::default(),
        }
    }

    fn gc(&self) {
        // Start the garbage collection.
        if !self.state.start_gc() {
            // Another thread is garbage collecting, so we short-circuit.
            return;
        }

        // Initially, every snapshot is marked unused.
        let mut unused = self.snapshots.collect();

        // Traverse the readers and update the reference counts.
        self.readers.take_each(|reader| {
            if reader.active.load() {
                // The reader is not released yet, and is thus considered active.

                // Remove the reader from the unused set and insert it back into the log (if it
                // exists in the unused set), as the snapshot is active.
                unused.remove(reader.ptr).map(|x| self.snapshots.insert(x));
                // Put the reader back in the structure.
                self.readers.insert(reader);
            } else {
                // The reader was released. Destroy it.
                reader.destroy();
            }
        });

        // End the garbage collection cycle.
        self.state.end_gc();
    }

    fn get(&self) -> Reader {
        // To avoid another thread freeing between reading and inserting into the readers stack, we
        // change the state to block garbage collecting for awhile.
        self.state.start_read();

        // Construct the raw reader.
        let reader = RawReader {
            // Load a snapshot of the pointer.
            ptr: self.inner.load(atomic::Ordering::Relaxed),
            // We allocate the atomic boolean on the heap as it is shared between the returned RAII
            // guard and the reader stack.
            released: Box::into_raw(Box::new(AtomicBool::new(false))),
        };

        // Register the reader through the reader stack, ensuring that it is not freed before the
        // RAII guard drops (`reader.release` is set to `true`).
        self.readers.push(reader);

        // Revert the original increment.
        self.state.end_read();

        Reader {
            raw: reader,
            _marker: PhantomData,
        }
    }

    fn set(&self, new: Box<T>) {
        // Replace the inner by the new value.
        let old = self.inner.swap(Box::into_raw(new), atomic::Ordering::Relaxed);
        // Push the old pointer to the snapshot stack.
        self.snapshots.push(Box::from_raw(old));
    }
}
<|endoftext|>


<commit_before>use xlib::XExposeEvent;
use xlib::bindgen::{XOpenDisplay, XDefaultScreen, XRootWindow};
use xlib::bindgen::{XBlackPixel, XCreateSimpleWindow, XStoreName};
use xlib::bindgen::{XSelectInput, XCloseDisplay, XNextEvent, XDefaultVisual};
use xlib::bindgen::{XMapWindow};
use bindgen::{AzCreateColorPattern, AzDrawTargetFillRect, AzReleaseColorPattern};
use bindgen::{AzSanityCheck, AzCreateDrawTargetForCairoSurface, AzReleaseDrawTarget};
use bindgen::{AzDrawTargetGetSize};
use cairo::{CAIRO_FORMAT_RGB24};
use cairo::bindgen::{cairo_image_surface_create, cairo_surface_destroy};
use cairo_xlib::bindgen::{cairo_xlib_surface_create};

#[test]
fn sanity_check() {
    AzSanityCheck();
}

const SIZEX: c_uint = 400 as c_uint;
const SIZEY: c_uint = 400 as c_uint;

const ExposureMask: c_long = (1 << 15) as c_long;
const ButtonPressMask: c_long = (1 << 2) as c_long;

const Expose: c_int = 12 as c_int;
const ButtonPress: c_int = 4 as c_int;

type XEventStub = {
    type_: c_int,
    padding: (
        int, int, int, int, int, int, int, int,
        int, int, int, int, int, int, int, int,
        int, int, int, int, int, int, int, int,
        int, int, int, int, int, int, int, int,
        int, int, int, int, int, int, int, int,
        int, int, int, int, int, int, int, int,
        int, int, int, int, int, int, int, int,
        int, int, int, int, int, int, int, int
    )
};

fn xexpose(event: *XEventStub) -> *XExposeEvent unsafe {
    unsafe::reinterpret_cast(&ptr::addr_of((*event).padding))
}

#[test]
#[ignore]
fn cairo_it_up() {
    let dpy = XOpenDisplay(ptr::null());
    assert(ptr::is_not_null(dpy));
    let scr = XDefaultScreen(dpy);
    let rootwin = XRootWindow(dpy, scr);
    let win = XCreateSimpleWindow(
        dpy, rootwin, 1 as c_int, 1 as c_int, SIZEX, SIZEY, 0 as c_uint,
        XBlackPixel(dpy, scr), XBlackPixel(dpy, scr));
    str::as_c_str(~"test", |cstr| XStoreName(dpy, win, cstr));
    XSelectInput(dpy, win, ExposureMask | ButtonPressMask);
    XMapWindow(dpy, win);

    let cs = cairo_xlib_surface_create(
        dpy, win, XDefaultVisual(dpy, 0 as c_int),
        SIZEX as c_int, SIZEY as c_int);

    let e = {
        type_: 0 as c_int,
        padding: (
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
        )
    };
    let ep = ptr::addr_of(e);

    let dt = AzCreateDrawTargetForCairoSurface(cs);

    loop {
        unsafe {
            XNextEvent(dpy, unsafe::reinterpret_cast(&ep));
            log(error, *ep);
            log(error, *xexpose(ep));
            if e.type_ == Expose && (*xexpose(ep)).count < 1 as c_int {
                paint(dt);
            } else if e.type_ == ButtonPress {
                break;
            } else {
                paint(dt);
            }
        }
    }

    AzReleaseDrawTarget(dt);
    cairo_surface_destroy(cs);
    XCloseDisplay(dpy);
}

fn paint(dt: AzDrawTargetRef) {
    log(error, "painting");
    let rect = {
        x: 200f as AzFloat,
        y: 200f as AzFloat,
        width: 100f as AzFloat,
        height: 100f as AzFloat
    };
    let color = {
        r: 0f as AzFloat,
        g: 1f as AzFloat,
        b: 0f as AzFloat,
        a: 1f as AzFloat
    };
    let pattern = AzCreateColorPattern(ptr::addr_of(color));
    AzDrawTargetFillRect(
        dt,
        ptr::addr_of(rect),
        unsafe { unsafe::reinterpret_cast(&pattern) });
    AzReleaseColorPattern(pattern);
}

#[test]
#[ignore(reason="busted on linux 32 bit")]
fn test_draw_target_get_size() {
    let surface = cairo_image_surface_create(
        CAIRO_FORMAT_RGB24, 100 as c_int, 200 as c_int);
    assert surface.is_not_null();
    let dt = AzCreateDrawTargetForCairoSurface(surface);
    assert dt.is_not_null();
    let size = AzDrawTargetGetSize(dt);
    assert size.width == 100 as int32_t;
    assert size.height == 200 as int32_t;
    AzReleaseDrawTarget(dt);
    cairo_surface_destroy(surface);
}

#[test]
#[ignore]
fn fonts() {
    use cairo::*;
    use cairo::bindgen::*;

    let dpy = XOpenDisplay(ptr::null());
    assert(ptr::is_not_null(dpy));
    let scr = XDefaultScreen(dpy);
    let rootwin = XRootWindow(dpy, scr);
    let win = XCreateSimpleWindow(
        dpy, rootwin, 1 as c_int, 1 as c_int, SIZEX, SIZEY, 0 as c_uint,
        XBlackPixel(dpy, scr), XBlackPixel(dpy, scr));
    str::as_c_str(~"test", |cstr| XStoreName(dpy, win, cstr));
    XSelectInput(dpy, win, ExposureMask | ButtonPressMask);
    XMapWindow(dpy, win);

    let cs = cairo_xlib_surface_create(
        dpy, win, XDefaultVisual(dpy, 0 as c_int),
        SIZEX as c_int, SIZEY as c_int);

    let e = {
        type_: 0 as c_int,
        padding: (
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
        )
    };
    let ep = ptr::addr_of(e);

    loop {
        unsafe {
            XNextEvent(dpy, unsafe::reinterpret_cast(&ep));
            log(error, *ep);
            log(error, *xexpose(ep));
            if e.type_ == Expose && (*xexpose(ep)).count < 1 as c_int {
                paint(cs);
            } else if e.type_ == ButtonPress {
                break;
            } else {
                paint(cs);
            }
        }
    }

    cairo_surface_destroy(cs);
    XCloseDisplay(dpy);

    fn paint(surf: *cairo_surface_t) {
        use libc::c_double;
        use cairo::*;
        use cairo::bindgen::*;

        let cr: *cairo_t = cairo_create(surf);
        assert cr.is_not_null();

        cairo_set_source_rgb(cr, 0.5 as c_double, 0.1 as c_double, 0.1 as c_double);
        cairo_paint(cr);

        let te: cairo_text_extents_t = {
            x_bearing: 0 as c_double,
            y_bearing: 0 as c_double,
            width: 0 as c_double,
            height: 0 as c_double,
            x_advance: 0 as c_double,
            y_advance: 0 as c_double
        };
        cairo_set_source_rgb(cr, 0.9 as c_double, 0.5 as c_double, 0.5 as c_double);
        str::as_c_str(~"Georgia", |fontname| {
            cairo_select_font_face(cr, fontname, CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD);
        });
        cairo_set_font_size(cr, 20.2 as c_double);
        str::as_c_str(~"a", |text| {
            cairo_text_extents(cr, text, ptr::addr_of(te));
            cairo_move_to(
                cr,
                (100.0 + 0.5) as c_double -
                te.width / (2.0 as c_double) - te.x_bearing,
                (100.0 + 0.5) as c_double -
                te.height / (2.0 as c_double) - te.y_bearing);
            cairo_show_text(cr, text);
        });

        cairo_destroy(cr);
    }
}

<commit_msg>Update linux tests for language changes.<commit_after>use xlib::XExposeEvent;
use xlib::bindgen::{XOpenDisplay, XDefaultScreen, XRootWindow};
use xlib::bindgen::{XBlackPixel, XCreateSimpleWindow, XStoreName};
use xlib::bindgen::{XSelectInput, XCloseDisplay, XNextEvent, XDefaultVisual};
use xlib::bindgen::{XMapWindow};
use bindgen::{AzCreateColorPattern, AzDrawTargetFillRect, AzReleaseColorPattern};
use bindgen::{AzSanityCheck, AzCreateDrawTargetForCairoSurface, AzReleaseDrawTarget};
use bindgen::{AzDrawTargetGetSize};
use cairo::{CAIRO_FORMAT_RGB24};
use cairo::bindgen::{cairo_image_surface_create, cairo_surface_destroy};
use cairo_xlib::bindgen::{cairo_xlib_surface_create};

#[test]
fn sanity_check() {
    AzSanityCheck();
}

const SIZEX: c_uint = 400 as c_uint;
const SIZEY: c_uint = 400 as c_uint;

const ExposureMask: c_long = (1 << 15) as c_long;
const ButtonPressMask: c_long = (1 << 2) as c_long;

const Expose: c_int = 12 as c_int;
const ButtonPress: c_int = 4 as c_int;

type XEventStub = {
    type_: c_int,
    padding: (
        int, int, int, int, int, int, int, int,
        int, int, int, int, int, int, int, int,
        int, int, int, int, int, int, int, int,
        int, int, int, int, int, int, int, int,
        int, int, int, int, int, int, int, int,
        int, int, int, int, int, int, int, int,
        int, int, int, int, int, int, int, int,
        int, int, int, int, int, int, int, int
    )
};

fn xexpose(event: *XEventStub) -> *XExposeEvent unsafe {
    cast::reinterpret_cast(&ptr::addr_of((*event).padding))
}

#[test]
#[ignore]
fn cairo_it_up() {
    let dpy = XOpenDisplay(ptr::null());
    assert(ptr::is_not_null(dpy));
    let scr = XDefaultScreen(dpy);
    let rootwin = XRootWindow(dpy, scr);
    let win = XCreateSimpleWindow(
        dpy, rootwin, 1 as c_int, 1 as c_int, SIZEX, SIZEY, 0 as c_uint,
        XBlackPixel(dpy, scr), XBlackPixel(dpy, scr));
    str::as_c_str(~"test", |cstr| XStoreName(dpy, win, cstr));
    XSelectInput(dpy, win, ExposureMask | ButtonPressMask);
    XMapWindow(dpy, win);

    let cs = cairo_xlib_surface_create(
        dpy, win, XDefaultVisual(dpy, 0 as c_int),
        SIZEX as c_int, SIZEY as c_int);

    let e = {
        type_: 0 as c_int,
        padding: (
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
        )
    };
    let ep = ptr::addr_of(e);

    let dt = AzCreateDrawTargetForCairoSurface(cs);

    loop {
        unsafe {
            XNextEvent(dpy, cast::reinterpret_cast(&ep));
            log(error, *ep);
            log(error, *xexpose(ep));
            if e.type_ == Expose && (*xexpose(ep)).count < 1 as c_int {
                paint(dt);
            } else if e.type_ == ButtonPress {
                break;
            } else {
                paint(dt);
            }
        }
    }

    AzReleaseDrawTarget(dt);
    cairo_surface_destroy(cs);
    XCloseDisplay(dpy);
}

fn paint(dt: AzDrawTargetRef) {
    log(error, "painting");
    let rect = {
        x: 200f as AzFloat,
        y: 200f as AzFloat,
        width: 100f as AzFloat,
        height: 100f as AzFloat
    };
    let color = {
        r: 0f as AzFloat,
        g: 1f as AzFloat,
        b: 0f as AzFloat,
        a: 1f as AzFloat
    };
    let pattern = AzCreateColorPattern(ptr::addr_of(color));
    AzDrawTargetFillRect(
        dt,
        ptr::addr_of(rect),
        unsafe { cast::reinterpret_cast(&pattern) });
    AzReleaseColorPattern(pattern);
}

#[test]
#[ignore(reason="busted on linux 32 bit")]
fn test_draw_target_get_size() {
    let surface = cairo_image_surface_create(
        CAIRO_FORMAT_RGB24, 100 as c_int, 200 as c_int);
    assert surface.is_not_null();
    let dt = AzCreateDrawTargetForCairoSurface(surface);
    assert dt.is_not_null();
    let size = AzDrawTargetGetSize(dt);
    assert size.width == 100 as int32_t;
    assert size.height == 200 as int32_t;
    AzReleaseDrawTarget(dt);
    cairo_surface_destroy(surface);
}

#[test]
#[ignore]
fn fonts() {
    use cairo::*;
    use cairo::bindgen::*;

    let dpy = XOpenDisplay(ptr::null());
    assert(ptr::is_not_null(dpy));
    let scr = XDefaultScreen(dpy);
    let rootwin = XRootWindow(dpy, scr);
    let win = XCreateSimpleWindow(
        dpy, rootwin, 1 as c_int, 1 as c_int, SIZEX, SIZEY, 0 as c_uint,
        XBlackPixel(dpy, scr), XBlackPixel(dpy, scr));
    str::as_c_str(~"test", |cstr| XStoreName(dpy, win, cstr));
    XSelectInput(dpy, win, ExposureMask | ButtonPressMask);
    XMapWindow(dpy, win);

    let cs = cairo_xlib_surface_create(
        dpy, win, XDefaultVisual(dpy, 0 as c_int),
        SIZEX as c_int, SIZEY as c_int);

    let e = {
        type_: 0 as c_int,
        padding: (
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
        )
    };
    let ep = ptr::addr_of(e);

    loop {
        unsafe {
            XNextEvent(dpy, cast::reinterpret_cast(&ep));
            log(error, *ep);
            log(error, *xexpose(ep));
            if e.type_ == Expose && (*xexpose(ep)).count < 1 as c_int {
                paint(cs);
            } else if e.type_ == ButtonPress {
                break;
            } else {
                paint(cs);
            }
        }
    }

    cairo_surface_destroy(cs);
    XCloseDisplay(dpy);

    fn paint(surf: *cairo_surface_t) {
        use libc::c_double;
        use cairo::*;
        use cairo::bindgen::*;

        let cr: *cairo_t = cairo_create(surf);
        assert cr.is_not_null();

        cairo_set_source_rgb(cr, 0.5 as c_double, 0.1 as c_double, 0.1 as c_double);
        cairo_paint(cr);

        let te: cairo_text_extents_t = {
            x_bearing: 0 as c_double,
            y_bearing: 0 as c_double,
            width: 0 as c_double,
            height: 0 as c_double,
            x_advance: 0 as c_double,
            y_advance: 0 as c_double
        };
        cairo_set_source_rgb(cr, 0.9 as c_double, 0.5 as c_double, 0.5 as c_double);
        str::as_c_str(~"Georgia", |fontname| {
            cairo_select_font_face(cr, fontname, CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD);
        });
        cairo_set_font_size(cr, 20.2 as c_double);
        str::as_c_str(~"a", |text| {
            cairo_text_extents(cr, text, ptr::addr_of(te));
            cairo_move_to(
                cr,
                (100.0 + 0.5) as c_double -
                te.width / (2.0 as c_double) - te.x_bearing,
                (100.0 + 0.5) as c_double -
                te.height / (2.0 as c_double) - te.y_bearing);
            cairo_show_text(cr, text);
        });

        cairo_destroy(cr);
    }
}

<|endoftext|>


<commit_before>#[feature(phase)];

#[phase(syntax, link)]
extern mod mphf;

use mphf::MphfMap;

#[test]
fn test_trailing_comma() {
    #[allow(dead_code)]
    static _m: MphfMap<int> = mphf_map!(
        "foo" => 10,
    );
}

#[test]
fn test_no_trailing_comma() {
    #[allow(dead_code)]
    static _m: MphfMap<int> = mphf_map!(
        "foo" => 10
    );
}

#[test]
fn test_empty() {
    let map: MphfMap<int> = mphf_map!();
    assert!(map.is_empty());
}

#[test]
fn test_two() {
    static map: MphfMap<int> = mphf_map!(
        "foo" => 10,
        "bar" => 11,
    );
    assert!(Some(&10) == map.find(& &"foo"));
    assert!(Some(&11) == map.find(& &"bar"));
    assert_eq!(None, map.find(& &"asdf"));
    assert_eq!(2, map.len());
}
<commit_msg>Move compile assert tests out of test fns<commit_after>#[feature(phase)];

#[phase(syntax, link)]
extern mod mphf;

use mphf::MphfMap;

#[allow(dead_code)]
static TRAILING_COMMA: MphfMap<int> = mphf_map!(
    "foo" => 10,
);

#[allow(dead_code)]
static NO_TRAILING_COMMA: MphfMap<int> = mphf_map!(
    "foo" => 10
);

#[test]
fn test_empty() {
    let map: MphfMap<int> = mphf_map!();
    assert!(map.is_empty());
}

#[test]
fn test_two() {
    static map: MphfMap<int> = mphf_map!(
        "foo" => 10,
        "bar" => 11,
    );
    assert!(Some(&10) == map.find(& &"foo"));
    assert!(Some(&11) == map.find(& &"bar"));
    assert_eq!(None, map.find(& &"asdf"));
    assert_eq!(2, map.len());
}
<|endoftext|>


<commit_before>/// A TOML [1] configuration file parser
///
/// [1]: https://github.com/mojombo/toml

use std::io::mem::MemReader;
use std::io::File;
use std::hashmap::HashMap;

fn read_char(rd: &mut MemReader) -> char {
  match rd.read_byte() {
    Some(b) => b as char,
    None => fail!()
  }
}

fn read_char_opt(rd: &mut MemReader) -> Option<char> {
  match rd.read_byte() {
    Some(b) => Some(b as char),
    None => None
  }
}

#[deriving(ToStr)]
enum Value {
    True,
    False,
    Unsigned(u64),
    Integer(i64),
    Float(f64),
    String(~str),
    Array(~[Value]),
    Datetime, // XXX
    Map(HashMap<~str, Value>) // XXX: This is no value
}

// parse values recursivly
fn parse_value(rd: &mut MemReader) -> Value {
    enum State {
        st_value,
        st_number,
        st_got_number
    }

    let mut state: State = st_value;
    let mut val: ~str = ~"";

    loop {
        match state {
            st_value => {
                if rd.eof() { fail!() }
                let ch = read_char(rd);
                match ch {
                    '\r' | '\n' | ' ' | '\t' => { }
                    '0' .. '9' => {
                        val.push_char(ch);
                        state = st_number;
                    }
                    't' => {
                        assert!(read_char(rd) == 'r');
                        assert!(read_char(rd) == 'u');
                        assert!(read_char(rd) == 'e');
                        return True
                    }
                    'f' => {
                        assert!(read_char(rd) == 'a');
                        assert!(read_char(rd) == 'l');
                        assert!(read_char(rd) == 's');
                        assert!(read_char(rd) == 'e');
                        return False
                    }
                    '"' => {
                        loop {
                            let ch = read_char(rd);
                            if ch == '"' {
                                break;
                            }
                            val.push_char(ch);
                        }
                        return String(val)
                    }
                    _ => { fail!() }
                }
            }
            st_got_number => {
                return Unsigned(from_str(val).unwrap())
            }
            st_number => {
                if rd.eof() {
                    state = st_got_number;
                } else {
                    let ch = read_char(rd);
                    match ch {
                        '\r' | '\n' | ' ' | '\t' => { state = st_got_number }
                        '#' => { skip_comment(rd); state = st_got_number }
                        '0' .. '9' => {
                            val.push_char(ch);
                        }
                        _ => { fail!() }
                    }
                }
            }
        }
    }
}

// We must be already within the '#"
fn skip_comment(rd: &mut MemReader) {
    loop {
        if rd.eof() { return }
        match read_char(rd) {
            '\n' => { return }
            _ => { }
        }
    }
}

trait Visitor {
    fn section(&mut self, name: ~str, is_array: bool) -> bool;
    fn pair(&mut self, key: ~str, val: Value) -> bool;
}

struct TOMLVisitor {
    root: HashMap<~str, Value>,
    current_section: ~str,
    section_is_array: bool
}

impl TOMLVisitor {
    fn new() -> TOMLVisitor {
        TOMLVisitor { root: HashMap::new(), current_section: ~"", section_is_array: false }
    }
    fn get_root<'a>(&'a self) -> &'a HashMap<~str, Value> {
        return &self.root;
    }
}

impl Visitor for TOMLVisitor {
    fn section(&mut self, name: ~str, is_array: bool) -> bool {
        debug!("Section: {} (is_array={})", name, is_array);
        self.section_is_array = is_array;
        self.current_section = name;
        return true
    }
    fn pair(&mut self, key: ~str, val: Value) -> bool {
        debug!("Pair: {} {:s}", key, val.to_str());
        let mut m = self.root.find_or_insert(self.current_section.clone(), Map(HashMap::new())); // XXX: remove clone
        match *m {
            Map(ref mut map) => {
                let ok = map.insert(key, val);
                return ok
            }
            _ => { return false }
        }
    }
}

fn read_token(rd: &mut MemReader, current_char: Option<char>, f: |char| -> bool) -> (~str, Option<char>) {
    let mut current_char = current_char;
    let mut token = ~"";
    loop {
        match current_char {
            Some(ch) => {
                if f(ch) { token.push_char(ch) }
                else { break }
            }
            None => { break }
        }
        current_char = read_char_opt(rd); // advance
    }

    return (token, current_char);
}

fn parse_section_identifier(rd: &mut MemReader, current_char: Option<char>) -> (~str, Option<char>) {
    read_token(rd, current_char, |ch| {
        match ch {
            'a' .. 'z' | 'A' .. 'Z' | '0' .. '9' | '.' | '_' => true,
            _ => false
        }
    })
}

fn skip_whitespaces(rd: &mut MemReader, current_char: Option<char>) -> Option<char> {
    let mut current_char = current_char;
    loop {
        match current_char {
            Some(' ') | Some('\t') | Some('\r') | Some('\n') => {
                current_char = read_char_opt(rd); // advance
            }
            _ => { break }
        }
    }
    return current_char;
}

fn parse<V: Visitor>(rd: &mut MemReader, visitor: &mut V) -> bool {
    let mut current_char: Option<char> = read_char_opt(rd);

    loop {
        if current_char.is_none() { return true }
        let ch = current_char.unwrap();
        match ch {
            // ignore whitespace
            '\r' | '\n' | ' ' | '\t' => { }

            // comment
            '#' => {
                // skip to end of line
                loop {
                    current_char = read_char_opt(rd);
                    match current_char {
                        Some('\n') => { break }
                        None => { return true }
                        _ => { /* skip */ }
                    }
                }
            }

            // section
            '[' => {
                current_char = read_char_opt(rd); // advance
                let mut double_section = false;
                match current_char {
                    Some('[') => {
                        double_section = true;
                        current_char = read_char_opt(rd); // advance
                    }
                    _ => {}
                }

                let (section_name, ch) = parse_section_identifier(rd, current_char);
                current_char = ch;

                match current_char {
                    Some(']') => { /* ok */ }
                    _ => { return false }
                }

                if double_section {
                    current_char = read_char_opt(rd); // advance
                    if current_char != Some(']') { return false }
                }

                visitor.section(section_name, double_section);
            }

            // identifier
            'a' .. 'z' | 'A' .. 'Z' | '_' => {

                let (ident, ch) = read_token(rd, current_char, |ch| {
                    match ch {
                        'a' .. 'z' | 'A' .. 'Z' | '0' .. '9' | '_' => true,
                        _ => false
                    }
                });

                current_char = ch;
                current_char = skip_whitespaces(rd, current_char);

                // assign wanted
                if current_char != Some('=') { return false }

                // XXX: current_char = read_char_opt(rd); // advance
                visitor.pair(ident, parse_value(rd)); // XXX: parse_value is not using peeking yet
            }

            _ => { return false }
        } /* end match */
        current_char = read_char_opt(rd); // advance
    }

    assert!(false);
}

fn main() {
  let contents = File::open(&Path::new(std::os::args()[1])).read_to_end();
  let mut rd = MemReader::new(contents);
  let mut visitor = TOMLVisitor::new();
  parse(&mut rd, &mut visitor);
  println!("{:s}", visitor.get_root().to_str());
}
<commit_msg>Use lookahead for parse_value()<commit_after>/// A TOML [1] configuration file parser
///
/// [1]: https://github.com/mojombo/toml

use std::io::mem::MemReader;
use std::io::File;
use std::hashmap::HashMap;

fn read_char(rd: &mut MemReader) -> char {
  match rd.read_byte() {
    Some(b) => b as char,
    None => fail!()
  }
}

fn read_char_opt(rd: &mut MemReader) -> Option<char> {
  match rd.read_byte() {
    Some(b) => Some(b as char),
    None => None
  }
}

#[deriving(ToStr)]
enum Value {
    True,
    False,
    Unsigned(u64),
    Integer(i64),
    Float(f64),
    String(~str),
    Array(~[Value]),
    Datetime, // XXX
    Map(HashMap<~str, Value>) // XXX: This is no value
}

trait Visitor {
    fn section(&mut self, name: ~str, is_array: bool) -> bool;
    fn pair(&mut self, key: ~str, val: Value) -> bool;
}

struct TOMLVisitor {
    root: HashMap<~str, Value>,
    current_section: ~str,
    section_is_array: bool
}

impl TOMLVisitor {
    fn new() -> TOMLVisitor {
        TOMLVisitor { root: HashMap::new(), current_section: ~"", section_is_array: false }
    }
    fn get_root<'a>(&'a self) -> &'a HashMap<~str, Value> {
        return &self.root;
    }
}

impl Visitor for TOMLVisitor {
    fn section(&mut self, name: ~str, is_array: bool) -> bool {
        debug!("Section: {} (is_array={})", name, is_array);
        self.section_is_array = is_array;
        self.current_section = name;
        return true
    }
    fn pair(&mut self, key: ~str, val: Value) -> bool {
        debug!("Pair: {} {:s}", key, val.to_str());
        let mut m = self.root.find_or_insert(self.current_section.clone(), Map(HashMap::new())); // XXX: remove clone
        match *m {
            Map(ref mut map) => {
                let ok = map.insert(key, val);
                return ok
            }
            _ => { return false }
        }
    }
}

// parse values recursivly
fn parse_value(rd: &mut MemReader, current_char: Option<char>) -> (Option<Value>, Option<char>) {
    let mut current_char = skip_whitespaces(rd, current_char);

    if current_char.is_none() { return (None, current_char) }
    let ch = current_char.unwrap();
    match ch {
        '0' .. '9' => {
            let (num, ch) = read_token(rd, current_char, |ch| {
                match ch {
                    '0' .. '9' => true,
                    _ => false
                }
            });
            match from_str(num) {
              Some(n) => return (Some(Unsigned(n)), ch),
              None => return (None, ch)
            }
        }
        't' => {
            current_char = read_char_opt(rd);
            if current_char != Some('r') { return (None, current_char) }
            current_char = read_char_opt(rd);
            if current_char != Some('u') { return (None, current_char) }
            current_char = read_char_opt(rd);
            if current_char != Some('e') { return (None, current_char) }
            current_char = read_char_opt(rd);

            return (Some(True), current_char)
        }
        'f' => {
            current_char = read_char_opt(rd);
            if current_char != Some('a') { return (None, current_char) }
            current_char = read_char_opt(rd);
            if current_char != Some('l') { return (None, current_char) }
            current_char = read_char_opt(rd);
            if current_char != Some('s') { return (None, current_char) }
            current_char = read_char_opt(rd);
            if current_char != Some('e') { return (None, current_char) }
            current_char = read_char_opt(rd);

            return (Some(False), current_char)
        }
        '"' => {
            current_char = read_char_opt(rd);
            let (str, ch) = read_token(rd, current_char, |ch| {
                match ch {
                    '"' => false,
                    _ => true
                }
            });
            current_char = ch;

            if current_char != Some('"') { return (None, current_char) }
            current_char = read_char_opt(rd);
            return (Some(String(str)), current_char)
        }
        _ => { return (None, current_char) }
    }
}

// We must be already within the '#"
fn skip_comment(rd: &mut MemReader) {
    loop {
        if rd.eof() { return }
        match read_char(rd) {
            '\n' => { return }
            _ => { }
        }
    }
}

fn read_token(rd: &mut MemReader, current_char: Option<char>, f: |char| -> bool) -> (~str, Option<char>) {
    let mut current_char = current_char;
    let mut token = ~"";
    loop {
        match current_char {
            Some(ch) => {
                if f(ch) { token.push_char(ch) }
                else { break }
            }
            None => { break }
        }
        current_char = read_char_opt(rd); // advance
    }

    return (token, current_char);
}

fn parse_section_identifier(rd: &mut MemReader, current_char: Option<char>) -> (~str, Option<char>) {
    read_token(rd, current_char, |ch| {
        match ch {
            'a' .. 'z' | 'A' .. 'Z' | '0' .. '9' | '.' | '_' => true,
            _ => false
        }
    })
}

fn skip_whitespaces(rd: &mut MemReader, current_char: Option<char>) -> Option<char> {
    let mut current_char = current_char;
    loop {
        match current_char {
            Some(' ') | Some('\t') | Some('\r') | Some('\n') => {
                current_char = read_char_opt(rd); // advance
            }
            _ => { break }
        }
    }
    return current_char;
}

fn parse<V: Visitor>(rd: &mut MemReader, visitor: &mut V) -> bool {
    let mut current_char: Option<char> = read_char_opt(rd);

    loop {
        if current_char.is_none() { return true }
        let ch = current_char.unwrap();
        match ch {
            // ignore whitespace
            '\r' | '\n' | ' ' | '\t' => { }

            // comment
            '#' => {
                // skip to end of line
                loop {
                    current_char = read_char_opt(rd);
                    match current_char {
                        Some('\n') => { break }
                        None => { return true }
                        _ => { /* skip */ }
                    }
                }
            }

            // section
            '[' => {
                current_char = read_char_opt(rd); // advance
                let mut double_section = false;
                match current_char {
                    Some('[') => {
                        double_section = true;
                        current_char = read_char_opt(rd); // advance
                    }
                    _ => {}
                }

                let (section_name, ch) = parse_section_identifier(rd, current_char);
                current_char = ch;

                match current_char {
                    Some(']') => { /* ok */ }
                    _ => { return false }
                }

                if double_section {
                    current_char = read_char_opt(rd); // advance
                    if current_char != Some(']') { return false }
                }

                visitor.section(section_name, double_section);
            }

            // identifier
            'a' .. 'z' | 'A' .. 'Z' | '_' => {

                let (ident, ch) = read_token(rd, current_char, |ch| {
                    match ch {
                        'a' .. 'z' | 'A' .. 'Z' | '0' .. '9' | '_' => true,
                        _ => false
                    }
                });

                current_char = ch;
                current_char = skip_whitespaces(rd, current_char);

                // assign wanted
                if current_char != Some('=') { return false }

                current_char = read_char_opt(rd); // advance
                let (val, ch) = parse_value(rd, current_char);
                current_char = ch;
                match val {
                  Some(v) => { visitor.pair(ident, v); }
                  None => { return false; }
                }
                continue; // do not advance!
            }

            _ => { return false }
        } /* end match */
        current_char = read_char_opt(rd); // advance
    }

    assert!(false);
}

fn main() {
  let contents = File::open(&Path::new(std::os::args()[1])).read_to_end();
  let mut rd = MemReader::new(contents);
  let mut visitor = TOMLVisitor::new();
  parse(&mut rd, &mut visitor);
  println!("{:s}", visitor.get_root().to_str());
}
<|endoftext|>


<commit_before>// Copyright (c) 2017-2021, The rav1e contributors. All rights reserved
//
// This source code is subject to the terms of the BSD 2 Clause License and
// the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
// was not distributed with this source code in the LICENSE file, you can
// obtain it at www.aomedia.org/license/software. If the Alliance for Open
// Media Patent License 1.0 was not distributed with this source code in the
// PATENTS file, you can obtain it at www.aomedia.org/license/patent.

#![allow(clippy::print_literal)]
#![allow(clippy::unused_io_amount)]

use rustc_version::{version, Version};
#[allow(unused_imports)]
use std::env;
use std::fs;
use std::path::{Path, PathBuf};
use std::process::exit;

#[allow(dead_code)]
fn rerun_dir<P: AsRef<Path>>(dir: P) {
  for entry in fs::read_dir(dir).unwrap() {
    let entry = entry.unwrap();
    let path = entry.path();
    println!("cargo:rerun-if-changed={}", path.to_string_lossy());

    if path.is_dir() {
      rerun_dir(path);
    }
  }
}

#[allow(dead_code)]
fn hash_changed(
  files: &[&str], out_dir: &str, config: &Path,
) -> Option<([u8; 8], PathBuf)> {
  use std::collections::hash_map::DefaultHasher;
  use std::hash::Hasher;
  use std::io::Read;

  let mut hasher = DefaultHasher::new();

  let paths = files
    .iter()
    .map(Path::new)
    .chain(std::iter::once(config))
    .chain(std::iter::once(Path::new("build.rs")));

  for path in paths {
    if let Ok(mut f) = std::fs::File::open(path) {
      let mut buf = Vec::new();
      f.read_to_end(&mut buf).unwrap();

      hasher.write(&buf);
    } else {
      panic!("Cannot open {}", path.display());
    }
  }

  let strip = env::var("STRIP").unwrap_or_else(|_| "strip".to_string());

  hasher.write(strip.as_bytes());

  let hash = hasher.finish().to_be_bytes();

  let hash_path = Path::new(&out_dir).join("asm.hash");

  if let Ok(old_hash) = std::fs::read(&hash_path) {
    if old_hash == hash {
      return None;
    }
  }

  Some((hash, hash_path))
}

#[cfg(feature = "asm")]
fn build_nasm_files() {
  use std::fs::File;
  use std::io::Write;
  let out_dir = env::var("OUT_DIR").unwrap();

  let dest_path = Path::new(&out_dir).join("config.asm");
  let mut config_file = File::create(&dest_path).unwrap();
  config_file.write(b"	%define private_prefix rav1e\n").unwrap();
  config_file.write(b"	%define ARCH_X86_32 0\n").unwrap();
  config_file.write(b" %define ARCH_X86_64 1\n").unwrap();
  config_file.write(b"	%define PIC 1\n").unwrap();
  config_file.write(b" %define STACK_ALIGNMENT 16\n").unwrap();
  config_file.write(b" %define HAVE_AVX512ICL 1\n").unwrap();
  if env::var("CARGO_CFG_TARGET_VENDOR").unwrap() == "apple" {
    config_file.write(b" %define PREFIX 1\n").unwrap();
  }

  let asm_files = &[
    "src/x86/ipred_avx2.asm",
    "src/x86/ipred_sse.asm",
    "src/x86/ipred16_avx2.asm",
    "src/x86/itx_avx2.asm",
    "src/x86/itx_sse.asm",
    "src/x86/itx16_avx2.asm",
    "src/x86/itx16_sse.asm",
    "src/x86/looprestoration_avx2.asm",
    "src/x86/looprestoration16_avx2.asm",
    "src/x86/mc_avx2.asm",
    "src/x86/mc16_avx2.asm",
    "src/x86/mc_avx512.asm",
    "src/x86/mc_sse.asm",
    "src/x86/mc16_sse.asm",
    "src/x86/me.asm",
    "src/x86/sad_sse2.asm",
    "src/x86/sad_avx.asm",
    "src/x86/satd.asm",
    "src/x86/sse.asm",
    "src/x86/cdef_rav1e.asm",
    "src/x86/cdef_sse.asm",
    "src/x86/cdef16_avx2.asm",
    "src/x86/cdef16_sse.asm",
    "src/x86/tables.asm",
  ];

  if let Some((hash, hash_path)) =
    hash_changed(asm_files, &out_dir, &dest_path)
  {
    let mut config_include_arg = String::from("-I");
    config_include_arg.push_str(&out_dir);
    config_include_arg.push('/');
    let mut nasm = nasm_rs::Build::new();
    for file in asm_files {
      nasm.file(file);
    }
    nasm.flag(&config_include_arg);
    nasm.flag("-Isrc/");
    let obj = nasm.compile_objects().expect(
      "NASM build failed. Make sure you have nasm installed. https://nasm.us",
    );
    // cc is better at finding the correct archiver
    let mut cc = cc::Build::new();
    for o in obj {
      cc.object(o);
    }
    cc.compile("rav1easm");

    // Strip local symbols from the asm library since they
    // confuse the debugger.
    fn strip<P: AsRef<Path>>(obj: P) {
      let strip = env::var("STRIP").unwrap_or_else(|_| "strip".to_string());

      let mut cmd = std::process::Command::new(strip);

      cmd.arg("-x").arg(obj.as_ref());

      let _ = cmd.output();
    }

    strip(Path::new(&out_dir).join("librav1easm.a"));

    std::fs::write(hash_path, &hash[..]).unwrap();
  } else {
    println!("cargo:rustc-link-search={}", out_dir);
  }
  println!("cargo:rustc-link-lib=static=rav1easm");
  rerun_dir("src/x86");
  rerun_dir("src/ext/x86");
}

#[cfg(feature = "asm")]
fn build_asm_files() {
  use std::fs::File;
  use std::io::Write;
  let out_dir = env::var("OUT_DIR").unwrap();

  let dest_path = Path::new(&out_dir).join("config.h");
  let mut config_file = File::create(&dest_path).unwrap();
  if env::var("CARGO_CFG_TARGET_VENDOR").unwrap() == "apple" {
    config_file.write(b" #define PREFIX 1\n").unwrap();
  }
  config_file.write(b" #define PRIVATE_PREFIX rav1e_\n").unwrap();
  config_file.write(b" #define ARCH_AARCH64 1\n").unwrap();
  config_file.write(b" #define ARCH_ARM 0\n").unwrap();
  config_file.write(b" #define CONFIG_LOG 1 \n").unwrap();
  config_file.write(b" #define HAVE_ASM 1\n").unwrap();
  config_file.sync_all().unwrap();

  let asm_files = &[
    "src/arm/64/cdef.S",
    "src/arm/64/cdef16.S",
    "src/arm/64/mc.S",
    "src/arm/64/mc16.S",
    "src/arm/64/itx.S",
    "src/arm/64/itx16.S",
    "src/arm/64/ipred.S",
    "src/arm/64/ipred16.S",
    "src/arm/64/sad.S",
    "src/arm/tables.S",
  ];

  if let Some((hash, hash_path)) =
    hash_changed(asm_files, &out_dir, &dest_path)
  {
    cc::Build::new()
      .files(asm_files)
      .include(".")
      .include(&out_dir)
      .compile("rav1e-aarch64");

    std::fs::write(hash_path, &hash[..]).unwrap();
  } else {
    println!("cargo:rustc-link-search={}", out_dir);
    println!("cargo:rustc-link-lib=static=rav1e-aarch64");
  }
  rerun_dir("src/arm");
}

fn rustc_version_check() {
  // This should match the version in the CI
  // Make sure to updated README.md when this changes.
  const REQUIRED_VERSION: &str = "1.51.0";
  if version().unwrap() < Version::parse(REQUIRED_VERSION).unwrap() {
    eprintln!("rav1e requires rustc >= {}.", REQUIRED_VERSION);
    exit(1);
  }
}

#[cfg(feature = "asm")]
fn is_nasm_new_enough(
  min_version: Option<(u8, u8, u8)>, nasm_path: &Path,
) -> Result<(), String> {
  match get_output(std::process::Command::new(nasm_path).arg("-v")) {
    Ok(version) => {
      if version.contains("NASM version 0.") {
        Err(version)
      } else if let Some((major, minor, micro)) = min_version {
        let ver = parse_nasm_version(&version)?;
        eprintln!("{:?} {:?} {:?}", min_version, version, ver);
        if major > ver.0
          || (major == ver.0 && minor > ver.1)
          || (major == ver.0 && minor == ver.1 && micro > ver.2)
        {
          Err(version)
        } else {
          Ok(())
        }
      } else {
        Ok(())
      }
    }
    Err(err) => Err(err),
  }
}

#[cfg(feature = "asm")]
fn parse_nasm_version(version_string: &str) -> Result<(u8, u8, u8), String> {
  // Rust regex lib doesn't support empty expressions in alterations,
  // so workaround the other way
  let regex1 =
    regex::Regex::new(r"(?:NASM version )?(\d+)\.(\d+)\.(\d+)").unwrap();
  if let Some(captures) = regex1.captures(version_string) {
    Ok((
      captures[1].parse().expect("Invalid version component"),
      captures[2].parse().expect("Invalid version component"),
      captures[3].parse().expect("Invalid version component"),
    ))
  } else {
    let regex2 = regex::Regex::new(r"(?:NASM version )?(\d+)\.(\d+)").unwrap();
    let captures = regex2
      .captures(version_string)
      .ok_or_else(|| "Unable to parse NASM version string".to_string())?;
    Ok((
      captures[1].parse().expect("Invalid version component"),
      captures[2].parse().expect("Invalid version component"),
      0,
    ))
  }
}

#[test]
fn test_parse_nasm_version() {
  let ver_str = "NASM version 2.14.02 compiled on Jan 22 2019";
  assert_eq!((2, 14, 2), parse_nasm_version(ver_str));
  let ver_str = "NASM version 2.14 compiled on Jan 22 2019";
  assert_eq!((2, 14, 0), parse_nasm_version(ver_str));
}

#[cfg(feature = "asm")]
fn nasm_version_check() -> std::path::PathBuf {
  let nasm_path = std::path::PathBuf::from("nasm");
  match is_nasm_new_enough(Some((2, 14, 0)), &nasm_path) {
    Ok(_) => nasm_path,
    Err(version) => {
      panic!("This version of NASM is too old: {}", version);
    }
  }
}

#[cfg(feature = "asm")]
fn get_output(cmd: &mut std::process::Command) -> Result<String, String> {
  let out = cmd.output().map_err(|e| e.to_string())?;
  if out.status.success() {
    Ok(String::from_utf8_lossy(&out.stdout).to_string())
  } else {
    Err(String::from_utf8_lossy(&out.stderr).to_string())
  }
}

#[allow(unused_variables)]
fn main() {
  rustc_version_check();

  let arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap();
  let os = env::var("CARGO_CFG_TARGET_OS").unwrap();
  // let env = env::var("CARGO_CFG_TARGET_ENV").unwrap();

  #[cfg(feature = "asm")]
  {
    if arch == "x86_64" {
      println!("cargo:rustc-cfg={}", "nasm_x86_64");
      nasm_version_check();
      build_nasm_files()
    }
    if arch == "aarch64" {
      println!("cargo:rustc-cfg={}", "asm_neon");
      build_asm_files()
    }
  }

  if os == "windows" && cfg!(feature = "decode_test") {
    panic!("Unsupported feature on this platform!");
  }

  vergen::generate_cargo_keys(vergen::ConstantsFlags::all())
    .expect("Unable to generate the cargo keys!");

  println!("cargo:rustc-env=PROFILE={}", env::var("PROFILE").unwrap());
}
<commit_msg>Add a configuration attribute to use nightly features<commit_after>// Copyright (c) 2017-2021, The rav1e contributors. All rights reserved
//
// This source code is subject to the terms of the BSD 2 Clause License and
// the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
// was not distributed with this source code in the LICENSE file, you can
// obtain it at www.aomedia.org/license/software. If the Alliance for Open
// Media Patent License 1.0 was not distributed with this source code in the
// PATENTS file, you can obtain it at www.aomedia.org/license/patent.

#![allow(clippy::print_literal)]
#![allow(clippy::unused_io_amount)]

use rustc_version::{version, version_meta, Channel, Version};
#[allow(unused_imports)]
use std::env;
use std::fs;
use std::path::{Path, PathBuf};
use std::process::exit;

#[allow(dead_code)]
fn rerun_dir<P: AsRef<Path>>(dir: P) {
  for entry in fs::read_dir(dir).unwrap() {
    let entry = entry.unwrap();
    let path = entry.path();
    println!("cargo:rerun-if-changed={}", path.to_string_lossy());

    if path.is_dir() {
      rerun_dir(path);
    }
  }
}

#[allow(dead_code)]
fn hash_changed(
  files: &[&str], out_dir: &str, config: &Path,
) -> Option<([u8; 8], PathBuf)> {
  use std::collections::hash_map::DefaultHasher;
  use std::hash::Hasher;
  use std::io::Read;

  let mut hasher = DefaultHasher::new();

  let paths = files
    .iter()
    .map(Path::new)
    .chain(std::iter::once(config))
    .chain(std::iter::once(Path::new("build.rs")));

  for path in paths {
    if let Ok(mut f) = std::fs::File::open(path) {
      let mut buf = Vec::new();
      f.read_to_end(&mut buf).unwrap();

      hasher.write(&buf);
    } else {
      panic!("Cannot open {}", path.display());
    }
  }

  let strip = env::var("STRIP").unwrap_or_else(|_| "strip".to_string());

  hasher.write(strip.as_bytes());

  let hash = hasher.finish().to_be_bytes();

  let hash_path = Path::new(&out_dir).join("asm.hash");

  if let Ok(old_hash) = std::fs::read(&hash_path) {
    if old_hash == hash {
      return None;
    }
  }

  Some((hash, hash_path))
}

#[cfg(feature = "asm")]
fn build_nasm_files() {
  use std::fs::File;
  use std::io::Write;
  let out_dir = env::var("OUT_DIR").unwrap();

  let dest_path = Path::new(&out_dir).join("config.asm");
  let mut config_file = File::create(&dest_path).unwrap();
  config_file.write(b"	%define private_prefix rav1e\n").unwrap();
  config_file.write(b"	%define ARCH_X86_32 0\n").unwrap();
  config_file.write(b" %define ARCH_X86_64 1\n").unwrap();
  config_file.write(b"	%define PIC 1\n").unwrap();
  config_file.write(b" %define STACK_ALIGNMENT 16\n").unwrap();
  config_file.write(b" %define HAVE_AVX512ICL 1\n").unwrap();
  if env::var("CARGO_CFG_TARGET_VENDOR").unwrap() == "apple" {
    config_file.write(b" %define PREFIX 1\n").unwrap();
  }

  let asm_files = &[
    "src/x86/ipred_avx2.asm",
    "src/x86/ipred_sse.asm",
    "src/x86/ipred16_avx2.asm",
    "src/x86/itx_avx2.asm",
    "src/x86/itx_sse.asm",
    "src/x86/itx16_avx2.asm",
    "src/x86/itx16_sse.asm",
    "src/x86/looprestoration_avx2.asm",
    "src/x86/looprestoration16_avx2.asm",
    "src/x86/mc_avx2.asm",
    "src/x86/mc16_avx2.asm",
    "src/x86/mc_avx512.asm",
    "src/x86/mc_sse.asm",
    "src/x86/mc16_sse.asm",
    "src/x86/me.asm",
    "src/x86/sad_sse2.asm",
    "src/x86/sad_avx.asm",
    "src/x86/satd.asm",
    "src/x86/sse.asm",
    "src/x86/cdef_rav1e.asm",
    "src/x86/cdef_sse.asm",
    "src/x86/cdef16_avx2.asm",
    "src/x86/cdef16_sse.asm",
    "src/x86/tables.asm",
  ];

  if let Some((hash, hash_path)) =
    hash_changed(asm_files, &out_dir, &dest_path)
  {
    let mut config_include_arg = String::from("-I");
    config_include_arg.push_str(&out_dir);
    config_include_arg.push('/');
    let mut nasm = nasm_rs::Build::new();
    for file in asm_files {
      nasm.file(file);
    }
    nasm.flag(&config_include_arg);
    nasm.flag("-Isrc/");
    let obj = nasm.compile_objects().expect(
      "NASM build failed. Make sure you have nasm installed. https://nasm.us",
    );
    // cc is better at finding the correct archiver
    let mut cc = cc::Build::new();
    for o in obj {
      cc.object(o);
    }
    cc.compile("rav1easm");

    // Strip local symbols from the asm library since they
    // confuse the debugger.
    fn strip<P: AsRef<Path>>(obj: P) {
      let strip = env::var("STRIP").unwrap_or_else(|_| "strip".to_string());

      let mut cmd = std::process::Command::new(strip);

      cmd.arg("-x").arg(obj.as_ref());

      let _ = cmd.output();
    }

    strip(Path::new(&out_dir).join("librav1easm.a"));

    std::fs::write(hash_path, &hash[..]).unwrap();
  } else {
    println!("cargo:rustc-link-search={}", out_dir);
  }
  println!("cargo:rustc-link-lib=static=rav1easm");
  rerun_dir("src/x86");
  rerun_dir("src/ext/x86");
}

#[cfg(feature = "asm")]
fn build_asm_files() {
  use std::fs::File;
  use std::io::Write;
  let out_dir = env::var("OUT_DIR").unwrap();

  let dest_path = Path::new(&out_dir).join("config.h");
  let mut config_file = File::create(&dest_path).unwrap();
  if env::var("CARGO_CFG_TARGET_VENDOR").unwrap() == "apple" {
    config_file.write(b" #define PREFIX 1\n").unwrap();
  }
  config_file.write(b" #define PRIVATE_PREFIX rav1e_\n").unwrap();
  config_file.write(b" #define ARCH_AARCH64 1\n").unwrap();
  config_file.write(b" #define ARCH_ARM 0\n").unwrap();
  config_file.write(b" #define CONFIG_LOG 1 \n").unwrap();
  config_file.write(b" #define HAVE_ASM 1\n").unwrap();
  config_file.sync_all().unwrap();

  let asm_files = &[
    "src/arm/64/cdef.S",
    "src/arm/64/cdef16.S",
    "src/arm/64/mc.S",
    "src/arm/64/mc16.S",
    "src/arm/64/itx.S",
    "src/arm/64/itx16.S",
    "src/arm/64/ipred.S",
    "src/arm/64/ipred16.S",
    "src/arm/64/sad.S",
    "src/arm/tables.S",
  ];

  if let Some((hash, hash_path)) =
    hash_changed(asm_files, &out_dir, &dest_path)
  {
    cc::Build::new()
      .files(asm_files)
      .include(".")
      .include(&out_dir)
      .compile("rav1e-aarch64");

    std::fs::write(hash_path, &hash[..]).unwrap();
  } else {
    println!("cargo:rustc-link-search={}", out_dir);
    println!("cargo:rustc-link-lib=static=rav1e-aarch64");
  }
  rerun_dir("src/arm");
}

fn rustc_version_check() {
  // This should match the version in the CI
  // Make sure to updated README.md when this changes.
  const REQUIRED_VERSION: &str = "1.51.0";
  if version().unwrap() < Version::parse(REQUIRED_VERSION).unwrap() {
    eprintln!("rav1e requires rustc >= {}.", REQUIRED_VERSION);
    exit(1);
  }

  if version_meta().unwrap().channel == Channel::Nightly {
    println!("cargo:rustc-cfg=nightly_rustc");
  }
}

#[cfg(feature = "asm")]
fn is_nasm_new_enough(
  min_version: Option<(u8, u8, u8)>, nasm_path: &Path,
) -> Result<(), String> {
  match get_output(std::process::Command::new(nasm_path).arg("-v")) {
    Ok(version) => {
      if version.contains("NASM version 0.") {
        Err(version)
      } else if let Some((major, minor, micro)) = min_version {
        let ver = parse_nasm_version(&version)?;
        eprintln!("{:?} {:?} {:?}", min_version, version, ver);
        if major > ver.0
          || (major == ver.0 && minor > ver.1)
          || (major == ver.0 && minor == ver.1 && micro > ver.2)
        {
          Err(version)
        } else {
          Ok(())
        }
      } else {
        Ok(())
      }
    }
    Err(err) => Err(err),
  }
}

#[cfg(feature = "asm")]
fn parse_nasm_version(version_string: &str) -> Result<(u8, u8, u8), String> {
  // Rust regex lib doesn't support empty expressions in alterations,
  // so workaround the other way
  let regex1 =
    regex::Regex::new(r"(?:NASM version )?(\d+)\.(\d+)\.(\d+)").unwrap();
  if let Some(captures) = regex1.captures(version_string) {
    Ok((
      captures[1].parse().expect("Invalid version component"),
      captures[2].parse().expect("Invalid version component"),
      captures[3].parse().expect("Invalid version component"),
    ))
  } else {
    let regex2 = regex::Regex::new(r"(?:NASM version )?(\d+)\.(\d+)").unwrap();
    let captures = regex2
      .captures(version_string)
      .ok_or_else(|| "Unable to parse NASM version string".to_string())?;
    Ok((
      captures[1].parse().expect("Invalid version component"),
      captures[2].parse().expect("Invalid version component"),
      0,
    ))
  }
}

#[test]
fn test_parse_nasm_version() {
  let ver_str = "NASM version 2.14.02 compiled on Jan 22 2019";
  assert_eq!((2, 14, 2), parse_nasm_version(ver_str));
  let ver_str = "NASM version 2.14 compiled on Jan 22 2019";
  assert_eq!((2, 14, 0), parse_nasm_version(ver_str));
}

#[cfg(feature = "asm")]
fn nasm_version_check() -> std::path::PathBuf {
  let nasm_path = std::path::PathBuf::from("nasm");
  match is_nasm_new_enough(Some((2, 14, 0)), &nasm_path) {
    Ok(_) => nasm_path,
    Err(version) => {
      panic!("This version of NASM is too old: {}", version);
    }
  }
}

#[cfg(feature = "asm")]
fn get_output(cmd: &mut std::process::Command) -> Result<String, String> {
  let out = cmd.output().map_err(|e| e.to_string())?;
  if out.status.success() {
    Ok(String::from_utf8_lossy(&out.stdout).to_string())
  } else {
    Err(String::from_utf8_lossy(&out.stderr).to_string())
  }
}

#[allow(unused_variables)]
fn main() {
  rustc_version_check();

  let arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap();
  let os = env::var("CARGO_CFG_TARGET_OS").unwrap();
  // let env = env::var("CARGO_CFG_TARGET_ENV").unwrap();

  #[cfg(feature = "asm")]
  {
    if arch == "x86_64" {
      println!("cargo:rustc-cfg={}", "nasm_x86_64");
      nasm_version_check();
      build_nasm_files()
    }
    if arch == "aarch64" {
      println!("cargo:rustc-cfg={}", "asm_neon");
      build_asm_files()
    }
  }

  if os == "windows" && cfg!(feature = "decode_test") {
    panic!("Unsupported feature on this platform!");
  }

  vergen::generate_cargo_keys(vergen::ConstantsFlags::all())
    .expect("Unable to generate the cargo keys!");

  println!("cargo:rustc-env=PROFILE={}", env::var("PROFILE").unwrap());
}
<|endoftext|>


<commit_before>extern crate rumblebars;
extern crate rustc_serialize;

use std::env;
use std::path::Path;
use std::fs::{self, File};
use std::io::Read;
use std::collections::BTreeMap;

use rumblebars::{Template, EvalContext};
use rustc_serialize::json::Json;

static UNITS_DIR: &'static str = "units";

fn main() {
    let out_dir = env::var("OUT_DIR").unwrap();
    let output = Path::new(&*out_dir);

    let data = Json::Object(build_render_data());
    let ctx = EvalContext::new();

    for entry in fs::read_dir(UNITS_DIR).unwrap() {
        let entry = entry.unwrap();
        let name = entry.file_name().into_string().unwrap();
        let target = output.join(&name[..name.len()-3]);
        let tmpl = File::open(entry.path()).and_then(|mut file| {
            let mut buf = String::new();
            file.read_to_string(&mut buf).map(|_| Template::new(&*buf).unwrap())
        }).unwrap();

        println!("generating unit: {:?} -> {:?}...", entry.path(), target);
        tmpl.eval(&data, &mut File::create(target).unwrap(), &ctx).unwrap();
    }
}

fn build_render_data() -> BTreeMap<String, Json> {
    let mut ctx = BTreeMap::new();

    let prefix = env::var("PREFIX").unwrap_or_else(|_| "/usr/local".to_owned());
    let package = "systemd-cron";

    ctx.insert("package".to_owned(), Json::String(package.to_owned()));

    ctx.insert("bindir".to_owned(), Json::String(env::var("BIN_DIR").unwrap_or_else(|_| prefix.clone() + "/bin")));
    ctx.insert("confdir".to_owned(), Json::String(env::var("CONF_DIR").unwrap_or_else(|_| prefix.clone() + "/etc")));

    let datadir = env::var("DATA_DIR").unwrap_or_else(|_| prefix.clone() + "/share");
    let libdir = env::var("LIB_DIR").unwrap_or_else(|_| prefix.clone() + "/lib");

    ctx.insert("mandir".to_owned(), Json::String(env::var("MAN_DIR").unwrap_or_else(|_| datadir.clone() + "/man")));
    ctx.insert("docdir".to_owned(), Json::String(env::var("DOC_DIR").unwrap_or_else(|_| datadir.clone() + "/doc/" + package)));
    ctx.insert("unitdir".to_owned(), Json::String(env::var("UNIT_DIR").unwrap_or_else(|_| libdir.clone() + "/systemd/system")));

    ctx.insert("stale_stamps".to_owned(), Json::String(env::var("STALE_STAMPS").unwrap_or_else(|_| libdir.clone() + package + "remove-stale-stamps")));

    ctx.insert("libdir".to_owned(), Json::String(libdir));
    ctx.insert("datadir".to_owned(), Json::String(datadir));
    ctx.insert("prefix".to_owned(), Json::String(prefix));

    ctx.insert("statedir".to_owned(), Json::String(env::var("STATE_DIR").unwrap_or_else(|_| "/var/spool/cron".to_owned())));

    ctx.insert("runparts".to_owned(), Json::String(env::var("RUN_PARTS").unwrap_or_else(|_| "/usr/bin/run-parts".to_owned())));

    let mut schedules = Vec::new();
    if env::var("CARGO_FEATURE_SCHED_BOOT").is_ok() {
        schedules.push(Json::String("boot".to_owned()));
    }
    if env::var("CARGO_FEATURE_SCHED_HOURLY").is_ok() {
        schedules.push(Json::String("hourly".to_owned()));
    }
    if env::var("CARGO_FEATURE_SCHED_DAILY").is_ok() {
        schedules.push(Json::String("daily".to_owned()));
    }
    if env::var("CARGO_FEATURE_SCHED_WEEKLY").is_ok() {
        schedules.push(Json::String("weekly".to_owned()));
    }
    if env::var("CARGO_FEATURE_SCHED_MONTHLY").is_ok() {
        schedules.push(Json::String("monthly".to_owned()));
    }
    if env::var("CARGO_FEATURE_SCHED_YEARLY").is_ok() {
        schedules.push(Json::String("yearly".to_owned()));
    }
    if env::var("CARGO_FEATURE_SCHED_MINUTELY").is_ok() {
        schedules.push(Json::String("minutely".to_owned()));
    }
    if env::var("CARGO_FEATURE_SCHED_QUARTERLY").is_ok() {
        schedules.push(Json::String("quarterly".to_owned()));
    }
    if env::var("CARGO_FEATURE_SCHED_SEMI_ANNUALLY").is_ok() {
        schedules.push(Json::String("semi-annually".to_owned()));
    }

    ctx.insert("schedules".to_owned(), Json::Array(schedules));

    ctx.insert("persistent".to_owned(), Json::Boolean(env::var("CARGO_FEATURE_PERSISTENT").is_ok()));

    ctx
}
<commit_msg>config: WIP<commit_after>extern crate rumblebars;
extern crate rustc_serialize;

use std::env;
use std::path::Path;
use std::fs::{self, File};
use std::io::Read;
use std::collections::BTreeMap;

use rumblebars::{Template, EvalContext};
use rustc_serialize::json::Json;

static UNITS_DIR: &'static str = "units";

fn main() {
    let out_dir = env::var("OUT_DIR").unwrap();
    let output = Path::new(&*out_dir);

    let data = build_render_data();
    let ctx = EvalContext::new();

    let mut consts = File::create(output + "/consts.rs").unwrap();
    writeln!(consts, "static CRONTAB_DIR: &'static str = {:?};", data["statedir"]).unwrap();
    writeln!(consts, "static USERS_CRONTAB_DIR: &'static str = {:?};", data["statedir"]).unwrap();

    let data = Json::Object(data);

    for entry in fs::read_dir(UNITS_DIR).unwrap() {
        let entry = entry.unwrap();
        let name = entry.file_name().into_string().unwrap();
        let target = output.join(&name[..name.len()-3]);
        let tmpl = File::open(entry.path()).and_then(|mut file| {
            let mut buf = String::new();
            file.read_to_string(&mut buf).map(|_| Template::new(&*buf).unwrap())
        }).unwrap();

        println!("generating unit: {:?} -> {:?}...", entry.path(), target);
        tmpl.eval(&data, &mut File::create(target).unwrap(), &ctx).unwrap();
    }
}

fn build_render_data() -> BTreeMap<String, Json> {
    let mut ctx = BTreeMap::new();

    let prefix = env::var("PREFIX").unwrap_or_else(|_| "/usr/local".to_owned());
    let package = "systemd-cron";

    ctx.insert("package".to_owned(), Json::String(package.to_owned()));

    ctx.insert("bindir".to_owned(), Json::String(env::var("BIN_DIR").unwrap_or_else(|_| prefix.clone() + "/bin")));
    ctx.insert("confdir".to_owned(), Json::String(env::var("CONF_DIR").unwrap_or_else(|_| prefix.clone() + "/etc")));

    let datadir = env::var("DATA_DIR").unwrap_or_else(|_| prefix.clone() + "/share");
    let libdir = env::var("LIB_DIR").unwrap_or_else(|_| prefix.clone() + "/lib");

    ctx.insert("mandir".to_owned(), Json::String(env::var("MAN_DIR").unwrap_or_else(|_| datadir.clone() + "/man")));
    ctx.insert("docdir".to_owned(), Json::String(env::var("DOC_DIR").unwrap_or_else(|_| datadir.clone() + "/doc/" + package)));
    ctx.insert("unitdir".to_owned(), Json::String(env::var("UNIT_DIR").unwrap_or_else(|_| libdir.clone() + "/systemd/system")));

    ctx.insert("stale_stamps".to_owned(), Json::String(env::var("STALE_STAMPS").unwrap_or_else(|_| libdir.clone() + package + "remove-stale-stamps")));

    ctx.insert("libdir".to_owned(), Json::String(libdir));
    ctx.insert("datadir".to_owned(), Json::String(datadir));
    ctx.insert("prefix".to_owned(), Json::String(prefix));

    ctx.insert("statedir".to_owned(), Json::String(env::var("STATE_DIR").unwrap_or_else(|_| "/var/spool/cron".to_owned())));

    ctx.insert("runparts".to_owned(), Json::String(env::var("RUN_PARTS").unwrap_or_else(|_| "/usr/bin/run-parts".to_owned())));

    let mut schedules = Vec::new();
    if env::var("CARGO_FEATURE_SCHED_BOOT").is_ok() {
        schedules.push(Json::String("boot".to_owned()));
    }
    if env::var("CARGO_FEATURE_SCHED_HOURLY").is_ok() {
        schedules.push(Json::String("hourly".to_owned()));
    }
    if env::var("CARGO_FEATURE_SCHED_DAILY").is_ok() {
        schedules.push(Json::String("daily".to_owned()));
    }
    if env::var("CARGO_FEATURE_SCHED_WEEKLY").is_ok() {
        schedules.push(Json::String("weekly".to_owned()));
    }
    if env::var("CARGO_FEATURE_SCHED_MONTHLY").is_ok() {
        schedules.push(Json::String("monthly".to_owned()));
    }
    if env::var("CARGO_FEATURE_SCHED_YEARLY").is_ok() {
        schedules.push(Json::String("yearly".to_owned()));
    }
    if env::var("CARGO_FEATURE_SCHED_MINUTELY").is_ok() {
        schedules.push(Json::String("minutely".to_owned()));
    }
    if env::var("CARGO_FEATURE_SCHED_QUARTERLY").is_ok() {
        schedules.push(Json::String("quarterly".to_owned()));
    }
    if env::var("CARGO_FEATURE_SCHED_SEMI_ANNUALLY").is_ok() {
        schedules.push(Json::String("semi-annually".to_owned()));
    }

    ctx.insert("schedules".to_owned(), Json::Array(schedules));

    ctx.insert("persistent".to_owned(), Json::Boolean(env::var("CARGO_FEATURE_PERSISTENT").is_ok()));

    ctx
}
<|endoftext|>


<commit_before>extern crate bindgen;

use std::env;
use std::path::PathBuf;

fn main() {
    // Tell cargo to tell rustc to link the system pam
    // shared library.
    println!("cargo:rustc-link-lib=pam");

    // pam_misc is only supported on Linux afaik
    if cfg!(target_os = "linux") {
        println!("cargo:rustc-link-lib=pam_misc");
    }

    // Tell cargo to invalidate the built crate whenever the wrapper changes
    println!("cargo:rerun-if-changed=wrapper.h");

    // Prepare bindgen builder
    let mut builder = bindgen::Builder::default()
        // Our header
        .header("wrapper.h")
        // Import libc so our signatures are slightly nicer
        .raw_line("use libc::{uid_t, gid_t, group, passwd};")
        .ctypes_prefix("libc")
        // Set macro constants to signed int, as all functions that accept
        // these constants use signed int as the parameter type
        .default_macro_constant_type(bindgen::MacroTypeVariation::Signed)
        // pam_handle is opaque, don't implement Copy
        .no_copy("pam_handle")
        // Blacklist varargs functions and related types for now
        // TODO: find a nice solution for this
        .blacklist_type("va_list")
        .blacklist_type("__builtin_va_list")
        .blacklist_type("__va_list_tag")
        .blacklist_function("pam_v.*")
        .blacklist_function("pam_syslog")
        .blacklist_function("pam_prompt")
        // Blacklist types we use from libc
        .blacklist_type(".*gid_t")
        .blacklist_type(".*uid_t")
        .blacklist_type("group")
        .blacklist_type("passwd")
        // Whitelist all PAM constants
        .whitelist_var("PAM_.*")
        // Whitelist all PAM functions..
        .whitelist_function("pam_.*")
        // ..except module related functions (pam_sm_*)
        .blacklist_function("pam_sm_.*");

    // Platform-specific adaptions
    if cfg!(target_os = "linux") {
        // On Linux we also use spwd from libc
        builder = builder
            .raw_line("use libc::spwd;")
            .blacklist_type("spwd");
    } else if cfg!(target_os = "freebsd") {
        // XXX: this should include all OS that use openPAM
        builder = builder
            .raw_line("pub const PAM_SILENT: libc::c_uint = 0x8000_0000;")
            .blacklist_type("PAM_SILENT");
    }

    let bindings = builder
        // Finish the builder and generate the bindings.
        .generate()
        // Unwrap the Result and panic on failure.
        .expect("Unable to generate bindings");

    // Write the bindings to the $OUT_DIR/bindings.rs file.
    let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
    bindings
        .write_to_file(out_path.join("bindings.rs"))
        .expect("Couldn't write bindings!");
}
<commit_msg>build.rs: fmt<commit_after>extern crate bindgen;

use std::env;
use std::path::PathBuf;

fn main() {
    // Tell cargo to tell rustc to link the system pam
    // shared library.
    println!("cargo:rustc-link-lib=pam");

    // pam_misc is only supported on Linux afaik
    if cfg!(target_os = "linux") {
        println!("cargo:rustc-link-lib=pam_misc");
    }

    // Tell cargo to invalidate the built crate whenever the wrapper changes
    println!("cargo:rerun-if-changed=wrapper.h");

    // Prepare bindgen builder
    let mut builder = bindgen::Builder::default()
        // Our header
        .header("wrapper.h")
        // Import libc so our signatures are slightly nicer
        .raw_line("use libc::{uid_t, gid_t, group, passwd};")
        .ctypes_prefix("libc")
        // Set macro constants to signed int, as all functions that accept
        // these constants use signed int as the parameter type
        .default_macro_constant_type(bindgen::MacroTypeVariation::Signed)
        // pam_handle is opaque, don't implement Copy
        .no_copy("pam_handle")
        // Blacklist varargs functions and related types for now
        // TODO: find a nice solution for this
        .blacklist_type("va_list")
        .blacklist_type("__builtin_va_list")
        .blacklist_type("__va_list_tag")
        .blacklist_function("pam_v.*")
        .blacklist_function("pam_syslog")
        .blacklist_function("pam_prompt")
        // Blacklist types we use from libc
        .blacklist_type(".*gid_t")
        .blacklist_type(".*uid_t")
        .blacklist_type("group")
        .blacklist_type("passwd")
        // Whitelist all PAM constants
        .whitelist_var("PAM_.*")
        // Whitelist all PAM functions..
        .whitelist_function("pam_.*")
        // ..except module related functions (pam_sm_*)
        .blacklist_function("pam_sm_.*");

    // Platform-specific adaptions
    if cfg!(target_os = "linux") {
        // On Linux we also use spwd from libc
        builder = builder.raw_line("use libc::spwd;").blacklist_type("spwd");
    } else if cfg!(target_os = "freebsd") {
        // XXX: this should include all OS that use openPAM
        builder = builder
            .raw_line("pub const PAM_SILENT: libc::c_uint = 0x8000_0000;")
            .blacklist_type("PAM_SILENT");
    }

    let bindings = builder
        // Finish the builder and generate the bindings.
        .generate()
        // Unwrap the Result and panic on failure.
        .expect("Unable to generate bindings");

    // Write the bindings to the $OUT_DIR/bindings.rs file.
    let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
    bindings
        .write_to_file(out_path.join("bindings.rs"))
        .expect("Couldn't write bindings!");
}
<|endoftext|>