<commit_before>// build.rs

#[cfg(all(target_arch = "x86_64", feature = "nasm"))]
extern crate nasm_rs;

#[allow(unused_imports)]
use std::env;
use std::fs;
use std::path::Path;

#[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);
        }
    }
}

fn main() {
    #[cfg(all(target_arch = "x86_64", feature = "nasm"))] {
        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();
            if cfg!(target_os="macos") {
              config_file.write(b" %define PREFIX 1\n").unwrap();
            }
        }
        let mut config_include_arg = String::from("-I");
        config_include_arg.push_str(&out_dir);
        config_include_arg.push('/');
        nasm_rs::compile_library_args(
            "rav1easm",
            &[
                "src/x86/data.asm",
                "src/x86/ipred.asm",
                "src/x86/itx.asm",
                "src/x86/mc.asm",
                "src/x86/me.asm",
                "src/x86/sad_sse2.asm",
                "src/x86/sad_avx.asm"
            ],
            &[&config_include_arg, "-Isrc/"]
        );
        println!("cargo:rustc-link-lib=static=rav1easm");
        rerun_dir("src/x86");
        rerun_dir("src/ext/x86");
    }

    if cfg!(windows) && cfg!(feature = "decode_test") {
        panic!("Unsupported feature on this platform!");
    }
}
<commit_msg>Support cross compiling<commit_after>// build.rs

#[allow(unused_imports)]
use std::env;
use std::fs;
use std::path::Path;

#[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);
        }
    }
}

#[cfg(feature = "nasm")]
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();
      if cfg!(target_os="macos") {
        config_file.write(b" %define PREFIX 1\n").unwrap();
      }
  }
  let mut config_include_arg = String::from("-I");
  config_include_arg.push_str(&out_dir);
  config_include_arg.push('/');
  nasm_rs::compile_library_args(
      "rav1easm",
      &[
          "src/x86/data.asm",
          "src/x86/ipred.asm",
          "src/x86/itx.asm",
          "src/x86/mc.asm",
          "src/x86/me.asm",
          "src/x86/sad_sse2.asm",
          "src/x86/sad_avx.asm"
      ],
      &[&config_include_arg, "-Isrc/"]
  );
  println!("cargo:rustc-link-lib=static=rav1easm");
  rerun_dir("src/x86");
  rerun_dir("src/ext/x86");
}

#[allow(unused_variables)]
fn main() {
    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 = "nasm")] {
      if arch == "x86_64" {
        build_nasm_files()
      }
    }

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


<commit_before>#[cfg(feature = "with-syntex")]
mod inner {
    extern crate serde_codegen;

    use std::env;
    use std::path::Path;

    pub fn main() {
        let out_dir = env::var_os("OUT_DIR").unwrap();

        for &(src, dst) in &[
            ("src/config.in.rs", "config.rs"),
            ("src/command/tags.in.rs", "tags.rs"),
            ("src/command/xkcd.in.rs", "xkcd.rs"),
        ] {
            let src = Path::new(src);
            let dst = Path::new(&out_dir).join(dst);

            serde_codegen::expand(&src, &dst).unwrap();
        }
    }
}

#[cfg(feature = "nightly")]
mod inner {
    pub fn main() {}
}

fn main() {
    inner::main();
}
<commit_msg>fix: update `tag.rs` filename in `build.rs`<commit_after>#[cfg(feature = "with-syntex")]
mod inner {
    extern crate serde_codegen;

    use std::env;
    use std::path::Path;

    pub fn main() {
        let out_dir = env::var_os("OUT_DIR").unwrap();

        for &(src, dst) in &[
            ("src/config.in.rs", "config.rs"),
            ("src/command/tag.in.rs", "tag.rs"),
            ("src/command/xkcd.in.rs", "xkcd.rs"),
        ] {
            let src = Path::new(src);
            let dst = Path::new(&out_dir).join(dst);

            serde_codegen::expand(&src, &dst).unwrap();
        }
    }
}

#[cfg(feature = "nightly")]
mod inner {
    pub fn main() {}
}

fn main() {
    inner::main();
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use std::fs;
use std::path::Path;
use std::process::Command;

fn main() {
    build_mmtabbarview();
    build_nibs();
}

fn build_mmtabbarview() {
    Command::new("xcodebuild")
        .args(&["-project", "./src/platform/cocoa/MMTabBarView/MMTabBarView/MMTabBarView.xcodeproj"])
        .args(&["-configuration", "Release"])
        .args(&["SYMROOT=../../../../../target/MMTabBarView/"])
        .status()
        .expect("xcodebuild failed");
    println!("cargo:rustc-link-search=framework=target/MMTabBarView/Release/");
}

fn build_nibs() {
    fn ibtool(src: &str, out_dir: &Path) {
        let out = out_dir.to_str().unwrap();
        let filename = Path::new(src).file_name().unwrap();
        let out_file = filename.to_str().unwrap().replace("xib", "nib");
        Command::new("ibtool")
            .arg(src)
            .arg("--compile")
            .arg(&format!("{}/{}", out, out_file))
            .status()
            .ok()
            .expect("ibtool failed");
    }
    let nibs_dir = Path::new("target/nibs");
    fs::create_dir_all(&nibs_dir).unwrap();
    ibtool("src/platform/cocoa/xib/App.xib", nibs_dir);
    ibtool("src/platform/cocoa/xib/Window.xib", nibs_dir);
}
<commit_msg>only build cocoa stuff for non glutin builds<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use std::fs;
use std::path::Path;
use std::process::Command;

fn main() {
    build_mmtabbarview();
    build_nibs();
}

fn build_mmtabbarview() {
    if cfg!(all(not(feature = "force-glutin"), target_os = "macos")) {
        Command::new("xcodebuild")
            .args(&["-project", "./src/platform/cocoa/MMTabBarView/MMTabBarView/MMTabBarView.xcodeproj"])
            .args(&["-configuration", "Release"])
            .args(&["SYMROOT=../../../../../target/MMTabBarView/"])
            .status()
            .expect("xcodebuild failed");
        println!("cargo:rustc-link-search=framework=target/MMTabBarView/Release/");
    }
}

fn build_nibs() {
    fn ibtool(src: &str, out_dir: &Path) {
        let out = out_dir.to_str().unwrap();
        let filename = Path::new(src).file_name().unwrap();
        let out_file = filename.to_str().unwrap().replace("xib", "nib");
        Command::new("ibtool")
            .arg(src)
            .arg("--compile")
            .arg(&format!("{}/{}", out, out_file))
            .status()
            .ok()
            .expect("ibtool failed");
    }
    let nibs_dir = Path::new("target/nibs");
    fs::create_dir_all(&nibs_dir).unwrap();
    ibtool("src/platform/cocoa/xib/App.xib", nibs_dir);
    ibtool("src/platform/cocoa/xib/Window.xib", nibs_dir);
}
<|endoftext|>


<commit_before>extern crate gcc;
extern crate bindgen;
extern crate regex;

use std::path::*;
use std::env;
use std::process::Command;
use std::fs::*;
use std::io::*;
use regex::Regex;

#[derive(Default)]
struct State {
    project_path: Option<PathBuf>,
    dpdk_path: Option<PathBuf>,
    dpdk_headers: Vec<PathBuf>,
    dpdk_links: Vec<PathBuf>,
    dpdk_config: Option<PathBuf>,
}

fn find_dpdk(state: &mut State) {
    if let Ok(path_string) = env::var("RTE_SDK") {
        state.dpdk_path = Some(PathBuf::from(&path_string));
    } else {
        // Automatic download
        let dir_path = Path::new("3rdparty");
        if !dir_path.exists() {
            create_dir(dir_path).ok();
        }
        assert!(dir_path.exists());
        let git_path = dir_path.join("dpdk");
        if !git_path.exists() {
            Command::new("git")
                .args(&["clone",
                        "-b",
                        "anlab",
                        "https://github.com/ANLAB-KAIST/dpdk",
                        git_path.to_str().unwrap()])
                .output()
                .expect("failed to run git command");
        }
        Command::new("make")
            .args(&["-C", git_path.to_str().unwrap(), "config", "T=anlab_shared"])
            .output()
            .expect("failed to run make command");
        Command::new("make")
            .args(&["-C", git_path.to_str().unwrap(), "-j", "T=anlab_shared"])
            .output()
            .expect("failed to run make command");

        state.dpdk_path = Some(git_path.join("build"));
    }
    assert!(state.dpdk_path
                .clone()
                .unwrap()
                .exists());
    let config_header = state.dpdk_path
        .clone()
        .unwrap()
        .join("include")
        .join("rte_config.h");
    assert!(config_header.exists());
    state.dpdk_config = Some(config_header);
}

fn find_link_libs(state: &mut State) {
    let dpdk_path = state.dpdk_path.clone().unwrap();
    let lib_dir = dpdk_path.join("lib");

    let mut libs = vec![];
    for entry in lib_dir.read_dir().expect("read_dir failed") {
        if let Ok(entry) = entry {
            let path = entry.path();
            if !path.is_file() {
                continue;
            }

            if let Some(ext) = path.extension() {
                if ext != "a" && ext != "so" {
                    continue;
                }
            } else {
                continue;
            }

            if let Some(file_name) = path.file_name() {
                let string = file_name.to_str().unwrap();
                if !string.starts_with("librte_") {
                    continue;
                }
                libs.push(PathBuf::from(string));
            } else {
                continue;
            }
        } else {
            continue;
        }
    }
    libs.sort();
    state.dpdk_links = libs;
}

fn check_direct_include(path: &Path) -> bool {
    if !path.is_file() {
        return false;
    }
    let file = File::open(path).unwrap();
    let reader = BufReader::new(&file);
    for (_, line) in reader.lines().enumerate() {
        let line_str = line.ok()
            .unwrap()
            .trim()
            .to_lowercase();
        if line_str.starts_with("#error") {
            if line_str.find("do not").is_some() && line_str.find("include").is_some() &&
               line_str.find("directly").is_some() {
                return false;
            }
        }
    }
    return true;
}

fn make_all_in_one_header(state: &mut State) {
    let include_dir = state.dpdk_path
        .clone()
        .unwrap()
        .join("include");
    let dpdk_config = state.dpdk_config.clone().unwrap();
    let mut headers = vec![];
    for entry in include_dir.read_dir().expect("read_dir failed") {
        if let Ok(entry) = entry {

            let path = entry.path();

            if !path.is_file() {
                continue;
            }

            if let Some(ext) = path.extension() {
                if ext != "h" {
                    continue;
                }
            } else {
                continue;
            }
            if path == dpdk_config {
                continue;
            }
            if !check_direct_include(&path) {
                continue;
            }
            headers.push(path);
        } else {
            continue;
        }
    }
    headers.sort();
    state.dpdk_headers = headers;
    let template_path = state.project_path
        .clone()
        .unwrap()
        .join("dpdk.h.template");
    let target_path = state.project_path
        .clone()
        .unwrap()
        .join("dpdk.h");
    let mut template = File::open(template_path).unwrap();
    let mut target = File::create(target_path).unwrap();

    let mut template_string = String::new();
    template.read_to_string(&mut template_string).ok();

    let mut headers_string = String::new();
    for header in &state.dpdk_headers {
        headers_string += &format!("#include <{}>\n",
                                   header.clone()
                                       .file_name()
                                       .unwrap()
                                       .to_str()
                                       .unwrap());
    }
    let formatted_string = template_string.replace("%header_list%", &headers_string);

    target.write_fmt(format_args!("{}", formatted_string)).ok();
}

fn generate_rust_def(state: &mut State) {
    let dpdk_include_path = state.dpdk_path
        .clone()
        .unwrap()
        .join("include");
    let c_include_path = state.project_path
        .clone()
        .unwrap()
        .join("c_header");
    let dpdk_config_path = state.dpdk_config.clone().unwrap();

    let header_path = state.project_path
        .clone()
        .unwrap()
        .join("dpdk.h");
    let target_path = state.project_path
        .clone()
        .unwrap()
        .join("src")
        .join("dpdk.rs");
    bindgen::builder()
        .header(header_path.to_str().unwrap())
        .no_unstable_rust()
        .clang_arg(format!("-I{}", dpdk_include_path.to_str().unwrap()))
        .clang_arg(format!("-I{}", c_include_path.to_str().unwrap()))
        .clang_arg("-imacros")
        .clang_arg(dpdk_config_path.to_str().unwrap())
        .clang_arg("-march=native")
        .generate()
        .unwrap()
        .write_to_file(target_path)
        .ok();
}

fn generate_lib_rs(state: &mut State) {
    let template_path = state.project_path
        .clone()
        .unwrap()
        .join("lib.rs.template");
    let target_path = state.project_path
        .clone()
        .unwrap()
        .join("src")
        .join("lib.rs");

    let format = Regex::new(r"rte_pmd_(\w+)").unwrap();

    let mut pmds = vec![];
    for link in &state.dpdk_links {
        let link_name = link.file_name()
            .unwrap()
            .to_str()
            .unwrap();
        if let Some(capture) = format.captures(link_name) {
            pmds.push(String::from(&capture[1]));
        }
    }

    let mut pmds_string = String::new();
    for pmd in pmds {
        pmds_string += &format!("\n\"{}\",", pmd);
    }

    let mut template = File::open(template_path).unwrap();
    let mut template_string = String::new();
    template.read_to_string(&mut template_string).ok();

    let formatted_string = template_string.replace("%pmd_list%", &pmds_string);
    let mut target = File::create(target_path).unwrap();
    target.write_fmt(format_args!("{}", formatted_string)).ok();
}

fn compile(state: &mut State) {

    let dpdk_path = state.dpdk_path.clone().unwrap();
    let dpdk_config = state.dpdk_config.clone().unwrap();
    let project_path = state.project_path.clone().unwrap();
    let lib_path = dpdk_path.join("lib");
    println!("cargo:rustc-link-search=native={}",
             lib_path.to_str().unwrap());
    for link in &state.dpdk_links {
        println!("cargo:rustc-link-lib={}", link.to_str().unwrap());
    }
    let dpdk_include_path = dpdk_path.join("include");
    let c_include_path = project_path.join("c_header");
    let c_source_path = project_path.join("c_source");
    gcc::Config::new()
        .file(c_source_path.join("inline_wrapper.c"))
        .include(&dpdk_include_path)
        .include(&c_include_path)
        .include(&project_path)
        .flag("-march=native")
        .flag("-imacros")
        .flag(dpdk_config.to_str().unwrap())
        .compile("lib_c_inline_wrapper.a");

    gcc::Config::new()
        .file(c_source_path.join("macro_wrapper.c"))
        .include(&c_include_path)
        .flag("-march=native")
        .compile("lib_c_macro_wrapper.a");
}
fn main() {
    let mut state: State = Default::default();
    state.project_path = Some(PathBuf::from("."));
    find_dpdk(&mut state);
    find_link_libs(&mut state);
    make_all_in_one_header(&mut state);
    generate_rust_def(&mut state);
    generate_lib_rs(&mut state);
    compile(&mut state);
}
<commit_msg>Fix path issues<commit_after>extern crate gcc;
extern crate bindgen;
extern crate regex;

use std::path::*;
use std::env;
use std::process::Command;
use std::fs::*;
use std::io::*;
use regex::Regex;

#[derive(Default)]
struct State {
    project_path: Option<PathBuf>,
    dpdk_path: Option<PathBuf>,
    dpdk_headers: Vec<PathBuf>,
    dpdk_links: Vec<PathBuf>,
    dpdk_config: Option<PathBuf>,
}

fn find_dpdk(state: &mut State) {
    if let Ok(path_string) = env::var("RTE_SDK") {
        state.dpdk_path = Some(PathBuf::from(&path_string));
    } else {
        // Automatic download
        let dir_path = Path::new("3rdparty");
        if !dir_path.exists() {
            create_dir(dir_path).ok();
        }
        assert!(dir_path.exists());
        let git_path = dir_path.join("dpdk");
        if !git_path.exists() {
            Command::new("git")
                .args(&["clone",
                        "-b",
                        "anlab",
                        "https://github.com/ANLAB-KAIST/dpdk",
                        git_path.to_str().unwrap()])
                .output()
                .expect("failed to run git command");
        }
        Command::new("make")
            .args(&["-C", git_path.to_str().unwrap(), "config", "T=anlab_shared"])
            .output()
            .expect("failed to run make command");
        Command::new("make")
            .args(&["-C", git_path.to_str().unwrap(), "-j", "T=anlab_shared"])
            .output()
            .expect("failed to run make command");

        state.dpdk_path = Some(git_path.join("build"));
    }
    assert!(state.dpdk_path
                .clone()
                .unwrap()
                .exists());
    let config_header = state.dpdk_path
        .clone()
        .unwrap()
        .join("include")
        .join("rte_config.h");
    assert!(config_header.exists());
    state.dpdk_config = Some(config_header);
}

fn find_link_libs(state: &mut State) {
    let dpdk_path = state.dpdk_path.clone().unwrap();
    let lib_dir = dpdk_path.join("lib");

    let mut libs = vec![];
    for entry in lib_dir.read_dir().expect("read_dir failed") {
        if let Ok(entry) = entry {
            let path = entry.path();
            if !path.is_file() {
                continue;
            }

            if let Some(ext) = path.extension() {
                if ext != "a" && ext != "so" {
                    continue;
                }
            } else {
                continue;
            }

            if let Some(file_stem) = path.file_stem() {
                let string = file_stem.to_str().unwrap();
                if !string.starts_with("librte_") {
                    continue;
                }
                libs.push(path.clone());
            } else {
                continue;
            }
        } else {
            continue;
        }
    }
    libs.sort();
    state.dpdk_links = libs;
}

fn check_direct_include(path: &Path) -> bool {
    if !path.is_file() {
        return false;
    }
    let file = File::open(path).unwrap();
    let reader = BufReader::new(&file);
    for (_, line) in reader.lines().enumerate() {
        let line_str = line.ok()
            .unwrap()
            .trim()
            .to_lowercase();
        if line_str.starts_with("#error") {
            if line_str.find("do not").is_some() && line_str.find("include").is_some() &&
               line_str.find("directly").is_some() {
                return false;
            }
        }
    }
    return true;
}

fn make_all_in_one_header(state: &mut State) {
    let include_dir = state.dpdk_path
        .clone()
        .unwrap()
        .join("include");
    let dpdk_config = state.dpdk_config.clone().unwrap();
    let mut headers = vec![];
    for entry in include_dir.read_dir().expect("read_dir failed") {
        if let Ok(entry) = entry {

            let path = entry.path();

            if !path.is_file() {
                continue;
            }

            if let Some(ext) = path.extension() {
                if ext != "h" {
                    continue;
                }
            } else {
                continue;
            }
            if path == dpdk_config {
                continue;
            }
            if !check_direct_include(&path) {
                continue;
            }
            headers.push(path);
        } else {
            continue;
        }
    }
    headers.sort();
    state.dpdk_headers = headers;
    let template_path = state.project_path
        .clone()
        .unwrap()
        .join("dpdk.h.template");
    let target_path = state.project_path
        .clone()
        .unwrap()
        .join("dpdk.h");
    let mut template = File::open(template_path).unwrap();
    let mut target = File::create(target_path).unwrap();

    let mut template_string = String::new();
    template.read_to_string(&mut template_string).ok();

    let mut headers_string = String::new();
    for header in &state.dpdk_headers {
        headers_string += &format!("#include <{}>\n",
                                   header.clone()
                                       .file_name()
                                       .unwrap()
                                       .to_str()
                                       .unwrap());
    }
    let formatted_string = template_string.replace("%header_list%", &headers_string);

    target.write_fmt(format_args!("{}", formatted_string)).ok();
}

fn generate_rust_def(state: &mut State) {
    let dpdk_include_path = state.dpdk_path
        .clone()
        .unwrap()
        .join("include");
    let c_include_path = state.project_path
        .clone()
        .unwrap()
        .join("c_header");
    let dpdk_config_path = state.dpdk_config.clone().unwrap();

    let header_path = state.project_path
        .clone()
        .unwrap()
        .join("dpdk.h");
    let target_path = state.project_path
        .clone()
        .unwrap()
        .join("src")
        .join("dpdk.rs");
    bindgen::builder()
        .header(header_path.to_str().unwrap())
        .no_unstable_rust()
        .clang_arg(format!("-I{}", dpdk_include_path.to_str().unwrap()))
        .clang_arg(format!("-I{}", c_include_path.to_str().unwrap()))
        .clang_arg("-imacros")
        .clang_arg(dpdk_config_path.to_str().unwrap())
        .clang_arg("-march=native")
        .generate()
        .unwrap()
        .write_to_file(target_path)
        .ok();
}

fn generate_lib_rs(state: &mut State) {
    let template_path = state.project_path
        .clone()
        .unwrap()
        .join("lib.rs.template");
    let target_path = state.project_path
        .clone()
        .unwrap()
        .join("src")
        .join("lib.rs");

    let format = Regex::new(r"rte_pmd_(\w+)").unwrap();

    let mut pmds = vec![];
    for link in &state.dpdk_links {
        let link_name = link.file_stem()
            .unwrap()
            .to_str()
            .unwrap();
        if let Some(capture) = format.captures(link_name) {
            pmds.push(String::from(&capture[1]));
        }
    }

    let mut pmds_string = String::new();
    for pmd in pmds {
        pmds_string += &format!("\n\"{}\",", pmd);
    }

    let mut template = File::open(template_path).unwrap();
    let mut template_string = String::new();
    template.read_to_string(&mut template_string).ok();

    let formatted_string = template_string.replace("%pmd_list%", &pmds_string);
    let mut target = File::create(target_path).unwrap();
    target.write_fmt(format_args!("{}", formatted_string)).ok();
}

fn compile(state: &mut State) {

    let dpdk_path = state.dpdk_path.clone().unwrap();
    let dpdk_config = state.dpdk_config.clone().unwrap();
    let project_path = state.project_path.clone().unwrap();
    let lib_path = dpdk_path.join("lib");
    println!("cargo:rustc-link-search=native={}",
             lib_path.to_str().unwrap());
    for link in &state.dpdk_links {
        println!("cargo:rustc-link-lib={}",
                 link.file_stem()
                     .unwrap()
                     .to_str()
                     .unwrap());
    }
    let dpdk_include_path = dpdk_path.join("include");
    let c_include_path = project_path.join("c_header");
    let c_source_path = project_path.join("c_source");
    gcc::Config::new()
        .file(c_source_path.join("inline_wrapper.c"))
        .include(&dpdk_include_path)
        .include(&c_include_path)
        .include(&project_path)
        .flag("-march=native")
        .flag("-imacros")
        .flag(dpdk_config.to_str().unwrap())
        .compile("lib_c_inline_wrapper.a");

    gcc::Config::new()
        .file(c_source_path.join("macro_wrapper.c"))
        .include(&c_include_path)
        .flag("-march=native")
        .compile("lib_c_macro_wrapper.a");
}
fn main() {
    let mut state: State = Default::default();
    state.project_path = Some(PathBuf::from("."));
    find_dpdk(&mut state);
    find_link_libs(&mut state);
    make_all_in_one_header(&mut state);
    generate_rust_def(&mut state);
    generate_lib_rs(&mut state);
    compile(&mut state);
}
<|endoftext|>


<commit_before>extern crate rlua;

use self::rlua::prelude::*;
use antidote::RwLock;
use irc::client::prelude::*;
use std::fmt;
use std::str::FromStr;

use chrono::NaiveDateTime;
use time;

use plugin::*;
pub mod database;
use self::database::Database;

mod utils;
use self::utils::*;
use utils::Url;

use self::error::*;
use error::ErrorKind as FrippyErrorKind;
use error::FrippyError;
use failure::ResultExt;

static LUA_SANDBOX: &'static str = include_str!("sandbox.lua");

#[derive(PluginName)]
pub struct Factoids<T: Database> {
    factoids: RwLock<T>,
}

impl<T: Database> Factoids<T> {
    pub fn new(db: T) -> Factoids<T> {
        Factoids {
            factoids: RwLock::new(db),
        }
    }

    fn create_factoid(
        &self,
        name: &str,
        content: &str,
        author: &str,
    ) -> Result<&str, FactoidsError> {
        let count = self.factoids.read().count_factoids(name)?;
        let tm = time::now().to_timespec();

        let factoid = database::NewFactoid {
            name: name,
            idx: count,
            content: content,
            author: author,
            created: NaiveDateTime::from_timestamp(tm.sec, 0u32),
        };

        Ok(self.factoids
            .write()
            .insert_factoid(&factoid)
            .map(|()| "Successfully added!")?)
    }

    fn add(&self, command: &mut PluginCommand) -> Result<&str, FactoidsError> {
        if command.tokens.len() < 2 {
            Err(ErrorKind::InvalidCommand)?;
        }

        let name = command.tokens.remove(0);
        let content = command.tokens.join(" ");

        Ok(self.create_factoid(&name, &content, &command.source)?)
    }

    fn add_from_url(&self, command: &mut PluginCommand) -> Result<&str, FactoidsError> {
        if command.tokens.len() < 2 {
            Err(ErrorKind::InvalidCommand)?;
        }

        let name = command.tokens.remove(0);
        let url = &command.tokens[0];
        let content = Url::from(url.as_ref())
            .max_kib(1024)
            .request()
            .context(ErrorKind::Download)?;

        Ok(self.create_factoid(&name, &content, &command.source)?)
    }

    fn remove(&self, command: &mut PluginCommand) -> Result<&str, FactoidsError> {
        if command.tokens.len() < 1 {
            Err(ErrorKind::InvalidCommand)?;
        }

        let name = command.tokens.remove(0);
        let count = self.factoids.read().count_factoids(&name)?;

        match self.factoids.write().delete_factoid(&name, count - 1) {
            Ok(()) => Ok("Successfully removed"),
            Err(e) => Err(e)?,
        }
    }

    fn get(&self, command: &PluginCommand) -> Result<String, FactoidsError> {
        let (name, idx) = match command.tokens.len() {
            0 => Err(ErrorKind::InvalidCommand)?,
            1 => {
                let name = &command.tokens[0];
                let count = self.factoids.read().count_factoids(name)?;

                if count < 1 {
                    Err(ErrorKind::NotFound)?;
                }

                (name, count - 1)
            }
            _ => {
                let name = &command.tokens[0];
                let idx = match i32::from_str(&command.tokens[1]) {
                    Ok(i) => i,
                    Err(_) => Err(ErrorKind::InvalidCommand)?,
                };

                (name, idx)
            }
        };

        let factoid = self.factoids
            .read()
            .get_factoid(name, idx)
            .context(ErrorKind::NotFound)?;

        let message = factoid.content.replace("\n", "|").replace("\r", "");

        Ok(format!("{}: {}", factoid.name, message))
    }

    fn info(&self, command: &PluginCommand) -> Result<String, FactoidsError> {
        match command.tokens.len() {
            0 => Err(ErrorKind::InvalidCommand)?,
            1 => {
                let name = &command.tokens[0];
                let count = self.factoids.read().count_factoids(name)?;

                Ok(match count {
                    0 => Err(ErrorKind::NotFound)?,
                    1 => format!("There is 1 version of {}", name),
                    _ => format!("There are {} versions of {}", count, name),
                })
            }
            _ => {
                let name = &command.tokens[0];
                let idx = i32::from_str(&command.tokens[1]).context(ErrorKind::InvalidIndex)?;
                let factoid = self.factoids.read().get_factoid(name, idx)?;

                Ok(format!(
                    "{}: Added by {} at {} UTC",
                    name, factoid.author, factoid.created
                ))
            }
        }
    }

    fn exec(&self, mut command: PluginCommand) -> Result<String, FactoidsError> {
        if command.tokens.len() < 1 {
            Err(ErrorKind::InvalidIndex)?
        } else {
            let name = command.tokens.remove(0);
            let count = self.factoids.read().count_factoids(&name)?;
            let factoid = self.factoids.read().get_factoid(&name, count - 1)?;

            let content = factoid.content;
            let value = if content.starts_with('>') {
                let content = String::from(&content[1..]);

                if content.starts_with('>') {
                    content
                } else {
                    match self.run_lua(&name, &content, &command) {
                        Ok(v) => v,
                        Err(e) => match e {
                            LuaError::CallbackError { cause, .. } => cause.to_string(),
                            _ => e.to_string(),
                        },
                    }
                }
            } else {
                content
            };

            Ok(value.replace("\n", "|").replace("\r", ""))
        }
    }

    fn run_lua(&self, name: &str, code: &str, command: &PluginCommand) -> Result<String, LuaError> {
        let args = command
            .tokens
            .iter()
            .filter(|x| !x.is_empty())
            .map(ToOwned::to_owned)
            .collect::<Vec<String>>();

        let lua = unsafe { Lua::new_with_debug() };
        let globals = lua.globals();

        globals.set("factoid", code)?;
        globals.set("download", lua.create_function(download)?)?;
        globals.set("sleep", lua.create_function(sleep)?)?;
        globals.set("args", args)?;
        globals.set("input", command.tokens.join(" "))?;
        globals.set("user", command.source.clone())?;
        globals.set("channel", command.target.clone())?;
        globals.set("output", lua.create_table()?)?;

        lua.exec::<()>(LUA_SANDBOX, Some(name))?;
        let output: Vec<String> = globals.get::<_, Vec<String>>("output")?;

        Ok(output.join("|"))
    }
}

impl<T: Database> Plugin for Factoids<T> {
    fn execute(&self, _: &IrcClient, message: &Message) -> ExecutionStatus {
        match message.command {
            Command::PRIVMSG(_, ref content) => if content.starts_with('!') {
                ExecutionStatus::RequiresThread
            } else {
                ExecutionStatus::Done
            },
            _ => ExecutionStatus::Done,
        }
    }

    fn execute_threaded(&self, client: &IrcClient, message: &Message) -> Result<(), FrippyError> {
        if let Command::PRIVMSG(_, mut content) = message.command.clone() {
            content.remove(0);

            let t: Vec<String> = content.split(' ').map(ToOwned::to_owned).collect();

            let c = PluginCommand {
                source: message.source_nickname().unwrap().to_owned(),
                target: message.response_target().unwrap().to_owned(),
                tokens: t,
            };

            Ok(match self.exec(c) {
                Ok(f) => client
                    .send_privmsg(&message.response_target().unwrap(), &f)
                    .context(FrippyErrorKind::Connection)?,
                Err(_) => (),
            })
        } else {
            Ok(())
        }
    }

    fn command(&self, client: &IrcClient, mut command: PluginCommand) -> Result<(), FrippyError> {
        if command.tokens.is_empty() {
            return Ok(client
                .send_notice(&command.target, "Invalid command")
                .context(FrippyErrorKind::Connection)?);
        }

        let target = command.target.clone();
        let source = command.source.clone();

        let sub_command = command.tokens.remove(0);
        let result = match sub_command.as_ref() {
            "add" => self.add(&mut command).map(|s| s.to_owned()),
            "fromurl" => self.add_from_url(&mut command).map(|s| s.to_owned()),
            "remove" => self.remove(&mut command).map(|s| s.to_owned()),
            "get" => self.get(&command),
            "info" => self.info(&command),
            "exec" => self.exec(command),
            _ => Err(ErrorKind::InvalidCommand.into()),
        };

        Ok(match result {
            Ok(v) => client
                .send_privmsg(&target, &v)
                .context(FrippyErrorKind::Connection)?,
            Err(e) => {
                let message = e.to_string();
                client
                    .send_notice(&source, &message)
                    .context(FrippyErrorKind::Connection)?;
                Err(e).context(FrippyErrorKind::Factoids)?
            }
        })
    }

    fn evaluate(&self, _: &IrcClient, _: PluginCommand) -> Result<String, String> {
        Err(String::from(
            "Evaluation of commands is not implemented for Factoids at this time",
        ))
    }
}

impl<T: Database> fmt::Debug for Factoids<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Factoids {{ ... }}")
    }
}

pub mod error {
    #[derive(Copy, Clone, Eq, PartialEq, Debug, Fail, Error)]
    #[error = "FactoidsError"]
    pub enum ErrorKind {
        /// Invalid command error
        #[fail(display = "Invalid Command")]
        InvalidCommand,

        /// Invalid index error
        #[fail(display = "Invalid index")]
        InvalidIndex,

        /// Download  error
        #[fail(display = "Download failed")]
        Download,

        /// Duplicate error
        #[fail(display = "Entry already exists")]
        Duplicate,

        /// Not found error
        #[fail(display = "Factoid was not found")]
        NotFound,

        /// MySQL error
        #[cfg(feature = "mysql")]
        #[fail(display = "Failed to execute MySQL Query")]
        MysqlError,

        /// No connection error
        #[cfg(feature = "mysql")]
        #[fail(display = "No connection to the database")]
        NoConnection,
    }
}
<commit_msg>Factoids: Add help and make some responses notices<commit_after>extern crate rlua;

use self::rlua::prelude::*;
use antidote::RwLock;
use irc::client::prelude::*;
use std::fmt;
use std::str::FromStr;

use chrono::NaiveDateTime;
use time;

use plugin::*;
pub mod database;
use self::database::Database;

mod utils;
use self::utils::*;
use utils::Url;

use self::error::*;
use error::ErrorKind as FrippyErrorKind;
use error::FrippyError;
use failure::ResultExt;

static LUA_SANDBOX: &'static str = include_str!("sandbox.lua");

enum FactoidResponse {
    Public(String),
    Private(String),
}

#[derive(PluginName)]
pub struct Factoids<T: Database> {
    factoids: RwLock<T>,
}

impl<T: Database> Factoids<T> {
    pub fn new(db: T) -> Factoids<T> {
        Factoids {
            factoids: RwLock::new(db),
        }
    }

    fn create_factoid(
        &self,
        name: &str,
        content: &str,
        author: &str,
    ) -> Result<&str, FactoidsError> {
        let count = self.factoids.read().count_factoids(name)?;
        let tm = time::now().to_timespec();

        let factoid = database::NewFactoid {
            name: name,
            idx: count,
            content: content,
            author: author,
            created: NaiveDateTime::from_timestamp(tm.sec, 0u32),
        };

        Ok(self.factoids
            .write()
            .insert_factoid(&factoid)
            .map(|()| "Successfully added!")?)
    }

    fn add(&self, command: &mut PluginCommand) -> Result<&str, FactoidsError> {
        if command.tokens.len() < 2 {
            Err(ErrorKind::InvalidCommand)?;
        }

        let name = command.tokens.remove(0);
        let content = command.tokens.join(" ");

        Ok(self.create_factoid(&name, &content, &command.source)?)
    }

    fn add_from_url(&self, command: &mut PluginCommand) -> Result<&str, FactoidsError> {
        if command.tokens.len() < 2 {
            Err(ErrorKind::InvalidCommand)?;
        }

        let name = command.tokens.remove(0);
        let url = &command.tokens[0];
        let content = Url::from(url.as_ref())
            .max_kib(1024)
            .request()
            .context(ErrorKind::Download)?;

        Ok(self.create_factoid(&name, &content, &command.source)?)
    }

    fn remove(&self, command: &mut PluginCommand) -> Result<&str, FactoidsError> {
        if command.tokens.len() < 1 {
            Err(ErrorKind::InvalidCommand)?;
        }

        let name = command.tokens.remove(0);
        let count = self.factoids.read().count_factoids(&name)?;

        match self.factoids.write().delete_factoid(&name, count - 1) {
            Ok(()) => Ok("Successfully removed"),
            Err(e) => Err(e)?,
        }
    }

    fn get(&self, command: &PluginCommand) -> Result<String, FactoidsError> {
        let (name, idx) = match command.tokens.len() {
            0 => Err(ErrorKind::InvalidCommand)?,
            1 => {
                let name = &command.tokens[0];
                let count = self.factoids.read().count_factoids(name)?;

                if count < 1 {
                    Err(ErrorKind::NotFound)?;
                }

                (name, count - 1)
            }
            _ => {
                let name = &command.tokens[0];
                let idx = match i32::from_str(&command.tokens[1]) {
                    Ok(i) => i,
                    Err(_) => Err(ErrorKind::InvalidCommand)?,
                };

                (name, idx)
            }
        };

        let factoid = self.factoids
            .read()
            .get_factoid(name, idx)
            .context(ErrorKind::NotFound)?;

        let message = factoid.content.replace("\n", "|").replace("\r", "");

        Ok(format!("{}: {}", factoid.name, message))
    }

    fn info(&self, command: &PluginCommand) -> Result<String, FactoidsError> {
        match command.tokens.len() {
            0 => Err(ErrorKind::InvalidCommand)?,
            1 => {
                let name = &command.tokens[0];
                let count = self.factoids.read().count_factoids(name)?;

                Ok(match count {
                    0 => Err(ErrorKind::NotFound)?,
                    1 => format!("There is 1 version of {}", name),
                    _ => format!("There are {} versions of {}", count, name),
                })
            }
            _ => {
                let name = &command.tokens[0];
                let idx = i32::from_str(&command.tokens[1]).context(ErrorKind::InvalidIndex)?;
                let factoid = self.factoids.read().get_factoid(name, idx)?;

                Ok(format!(
                    "{}: Added by {} at {} UTC",
                    name, factoid.author, factoid.created
                ))
            }
        }
    }

    fn exec(&self, mut command: PluginCommand) -> Result<String, FactoidsError> {
        if command.tokens.len() < 1 {
            Err(ErrorKind::InvalidIndex)?
        } else {
            let name = command.tokens.remove(0);
            let count = self.factoids.read().count_factoids(&name)?;
            let factoid = self.factoids.read().get_factoid(&name, count - 1)?;

            let content = factoid.content;
            let value = if content.starts_with('>') {
                let content = String::from(&content[1..]);

                if content.starts_with('>') {
                    content
                } else {
                    match self.run_lua(&name, &content, &command) {
                        Ok(v) => v,
                        Err(e) => match e {
                            LuaError::CallbackError { cause, .. } => cause.to_string(),
                            _ => e.to_string(),
                        },
                    }
                }
            } else {
                content
            };

            Ok(value.replace("\n", "|").replace("\r", ""))
        }
    }

    fn run_lua(&self, name: &str, code: &str, command: &PluginCommand) -> Result<String, LuaError> {
        let args = command
            .tokens
            .iter()
            .filter(|x| !x.is_empty())
            .map(ToOwned::to_owned)
            .collect::<Vec<String>>();

        let lua = unsafe { Lua::new_with_debug() };
        let globals = lua.globals();

        globals.set("factoid", code)?;
        globals.set("download", lua.create_function(download)?)?;
        globals.set("sleep", lua.create_function(sleep)?)?;
        globals.set("args", args)?;
        globals.set("input", command.tokens.join(" "))?;
        globals.set("user", command.source.clone())?;
        globals.set("channel", command.target.clone())?;
        globals.set("output", lua.create_table()?)?;

        lua.exec::<()>(LUA_SANDBOX, Some(name))?;
        let output: Vec<String> = globals.get::<_, Vec<String>>("output")?;

        Ok(output.join("|"))
    }

    fn help(&self) -> &str {
        "usage: factoids <subcommand>\r\n\
         subcommands: add, fromurl, remove, info, get, exec, help"
    }
}

impl<T: Database> Plugin for Factoids<T> {
    fn execute(&self, _: &IrcClient, message: &Message) -> ExecutionStatus {
        match message.command {
            Command::PRIVMSG(_, ref content) => if content.starts_with('!') {
                ExecutionStatus::RequiresThread
            } else {
                ExecutionStatus::Done
            },
            _ => ExecutionStatus::Done,
        }
    }

    fn execute_threaded(&self, client: &IrcClient, message: &Message) -> Result<(), FrippyError> {
        if let Command::PRIVMSG(_, mut content) = message.command.clone() {
            content.remove(0);

            let t: Vec<String> = content.split(' ').map(ToOwned::to_owned).collect();

            let c = PluginCommand {
                source: message.source_nickname().unwrap().to_owned(),
                target: message.response_target().unwrap().to_owned(),
                tokens: t,
            };

            Ok(match self.exec(c) {
                Ok(f) => client
                    .send_privmsg(&message.response_target().unwrap(), &f)
                    .context(FrippyErrorKind::Connection)?,
                Err(_) => (),
            })
        } else {
            Ok(())
        }
    }

    fn command(&self, client: &IrcClient, mut command: PluginCommand) -> Result<(), FrippyError> {
        use self::FactoidResponse::{Private, Public};

        if command.tokens.is_empty() {
            return Ok(client
                .send_notice(&command.target, "Invalid command")
                .context(FrippyErrorKind::Connection)?);
        }

        let target = command.target.clone();
        let source = command.source.clone();

        let sub_command = command.tokens.remove(0);
        let result = match sub_command.as_ref() {
            "add" => self.add(&mut command).map(|s| Private(s.to_owned())),
            "fromurl" => self.add_from_url(&mut command)
                .map(|s| Private(s.to_owned())),
            "remove" => self.remove(&mut command).map(|s| Private(s.to_owned())),
            "get" => self.get(&command).map(|s| Public(s)),
            "info" => self.info(&command).map(|s| Public(s)),
            "exec" => self.exec(command).map(|s| Public(s)),
            "help" => Ok(Private(self.help().to_owned())),
            _ => Err(ErrorKind::InvalidCommand.into()),
        };

        Ok(match result {
            Ok(v) => match v {
                Public(m) => client
                    .send_privmsg(&target, &m)
                    .context(FrippyErrorKind::Connection)?,
                Private(m) => client
                    .send_notice(&source, &m)
                    .context(FrippyErrorKind::Connection)?,
            },
            Err(e) => {
                let message = e.to_string();
                client
                    .send_notice(&source, &message)
                    .context(FrippyErrorKind::Connection)?;
                Err(e).context(FrippyErrorKind::Factoids)?
            }
        })
    }

    fn evaluate(&self, _: &IrcClient, _: PluginCommand) -> Result<String, String> {
        Err(String::from(
            "Evaluation of commands is not implemented for Factoids at this time",
        ))
    }
}

impl<T: Database> fmt::Debug for Factoids<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Factoids {{ ... }}")
    }
}

pub mod error {
    #[derive(Copy, Clone, Eq, PartialEq, Debug, Fail, Error)]
    #[error = "FactoidsError"]
    pub enum ErrorKind {
        /// Invalid command error
        #[fail(display = "Invalid Command")]
        InvalidCommand,

        /// Invalid index error
        #[fail(display = "Invalid index")]
        InvalidIndex,

        /// Download  error
        #[fail(display = "Download failed")]
        Download,

        /// Duplicate error
        #[fail(display = "Entry already exists")]
        Duplicate,

        /// Not found error
        #[fail(display = "Factoid was not found")]
        NotFound,

        /// MySQL error
        #[cfg(feature = "mysql")]
        #[fail(display = "Failed to execute MySQL Query")]
        MysqlError,

        /// No connection error
        #[cfg(feature = "mysql")]
        #[fail(display = "No connection to the database")]
        NoConnection,
    }
}
<|endoftext|>


<commit_before>use std::slice::Iter;
use std::iter::FromIterator;
use geometry::{BBox, PartialBoundingBox, Prim};
use raytracer::Ray;
use vec3::Vec3;

pub struct Octree<T> {
    prims: Vec<T>,
    infinites: Vec<T>, // for infinite prims (planes)
    root: OctreeNode,
}

pub struct OctreeNode {
    bbox: BBox,
    depth: i32,
    children: Vec<OctreeNode>,
    leaf_data: Vec<OctreeData>,
}

#[derive(Clone, Copy)]
struct OctreeData {
    pub bbox: BBox,
    pub index: usize
}

impl OctreeData {
    pub fn intersects(&self, ray: &Ray) -> bool {
        self.bbox.intersects(ray)
    }
}

impl OctreeNode {
    #[allow(dead_code)]
    pub fn new(bbox: BBox, depth: i32) -> OctreeNode {
        OctreeNode {
            bbox: bbox,
            depth: depth,
            children: Vec::new(),
            leaf_data: Vec::new(),
        }
    }

    fn subdivide(&mut self) {
        for x in 0u32..2 {
            for y in 0u32..2 {
                for z in 0u32..2 {
                    let len = self.bbox.len();

                    let child_bbox = BBox {
                        min: Vec3 {
                            x: self.bbox.min.x + x as f64 * len.x / 2.0,
                            y: self.bbox.min.y + y as f64 * len.y / 2.0,
                            z: self.bbox.min.z + z as f64 * len.z / 2.0
                        },
                        max: Vec3 {
                            x: self.bbox.max.x - (1 - x) as f64 * len.x / 2.0,
                            y: self.bbox.max.y - (1 - y) as f64 * len.y / 2.0,
                            z: self.bbox.max.z - (1 - z) as f64 * len.z / 2.0,
                        }
                    };

                    self.children.push(OctreeNode::new(child_bbox, self.depth - 1));
                }
            }
        }
    }

    #[allow(dead_code)]
    pub fn insert(&mut self, index: usize, object_bbox: BBox) -> () {
        // Max depth
        if self.depth <= 0 {
            self.leaf_data.push(OctreeData { index: index, bbox: object_bbox });
            return;
        }

        // Empty leaf node
        if self.is_leaf() && self.leaf_data.len() == 0 {
            self.leaf_data.push(OctreeData { index: index, bbox: object_bbox });
            return;
        }

        // Occupied leaf node and not max depth: subdivide node
        if self.is_leaf() && self.leaf_data.len() == 1 {
            self.subdivide();
            let old = self.leaf_data.remove(0);
            // Reinsert old node and then fall through to insert current object
            self.insert(old.index, old.bbox);
        }

        // Interior node (has children)
        for child in self.children.iter_mut() {
            if child.bbox.overlaps(&object_bbox) {
                child.insert(index, object_bbox);
            }
        }
    }

    fn is_leaf(&self) -> bool {
        self.children.len() == 0
    }
}

impl<T> FromIterator<T> for Octree<T> where T: PartialBoundingBox {
    fn from_iter<I>(iterator: I) -> Self where I: IntoIterator<Item=T> {
        let iterator = iterator.into_iter();

        let (finites, infinites): (Vec<T>, Vec<T>) =
            iterator.partition(|item| item.partial_bounding_box().is_some());

        // TODO(sell): why do we need to map here? &T isn't PartialBoundingBox,
        //             but we need to find out how to make it so.
        let bounds = BBox::from_union(finites.iter().map(|i| i.partial_bounding_box()))
            .unwrap_or(BBox::zero());

        // pbrt recommended max depth for a k-d tree (though, we're using an octree)
        // For a k-d tree: 8 + 1.3 * log2(N)
        let depth = (1.2 * (finites.len() as f64).log(8.0)).round() as i32;

        println!("Octree maximum depth {}", depth);
        let mut root_node = OctreeNode::new(bounds, depth);
        for (i, prim) in finites.iter().enumerate() {
            root_node.insert(i, prim.partial_bounding_box().unwrap());
        }

        Octree {
            prims: finites,
            infinites: infinites,
            root: root_node,
        }
    }
}

impl Octree<Box<Prim+Send+Sync>> {
    pub fn get_intersected_objects<'a>(&'a self, ray: &'a Ray) -> OctreeIterator<'a, Box<Prim+Send+Sync>> {
        OctreeIterator::new(self, ray)
    }
}

struct OctreeIterator<'a, T:'a> {
    prims: &'a [T],
    stack: Vec<&'a OctreeNode>,
    cur_iter: Option<Iter<'a, OctreeData>>,
    ray: &'a Ray,
    infinites: Iter<'a, T>,
    just_infinites: bool
}

impl<'a> OctreeIterator<'a, Box<Prim+Send+Sync>> {
    fn new<'b>(octree: &'b Octree<Box<Prim+Send+Sync>>, ray: &'b Ray) -> OctreeIterator<'b, Box<Prim+Send+Sync>> {
            OctreeIterator {
            prims: &octree.prims[..],
            stack: vec![&octree.root],
            cur_iter: None,
            ray: ray,
            infinites: octree.infinites.iter(),
            just_infinites: false
        }
    }
}

impl<'a> Iterator for OctreeIterator<'a, Box<Prim+Send+Sync>> {
    type Item = &'a Box<Prim+Send+Sync>;

    fn next(&mut self) -> Option<&'a Box<Prim+Send+Sync>> {
        if self.just_infinites {
            return self.infinites.next();
        }
        loop {
            let (new_cur_iter, val) = match self.cur_iter.take() {
                Some(mut cur_iter) => match cur_iter.next() {
                    Some(val) => (Some(cur_iter), Some(val)),
                    None => (None, None)
                },
                None => match self.stack.pop() {
                    Some(node) => {
                        for child in node.children.iter() {
                            if child.bbox.intersects(self.ray) {
                                self.stack.push(child);
                            }
                        }
                        (Some(node.leaf_data.iter()), None)
                    },
                    None => break  // Empty stack and no iterator
                }
            };
            self.cur_iter = new_cur_iter;
            match val {
                Some(val) => {
                    if val.intersects(self.ray) {
                        return Some(&self.prims[val.index]);
                    }
                },
                None => (),
            }
        }
        self.just_infinites = true;
        self.infinites.next()
    }
}
<commit_msg>refactor(octree): significant improvement in Iterator loop<commit_after>use std::slice::Iter;
use std::iter::FromIterator;
use geometry::{BBox, PartialBoundingBox, Prim};
use raytracer::Ray;
use vec3::Vec3;

pub struct Octree<T> {
    prims: Vec<T>,
    infinites: Vec<T>, // for infinite prims (planes)
    root: OctreeNode,
}

pub struct OctreeNode {
    bbox: BBox,
    depth: i32,
    children: Vec<OctreeNode>,
    leaf_data: Vec<OctreeData>,
}

#[derive(Clone, Copy)]
struct OctreeData {
    pub bbox: BBox,
    pub index: usize
}

impl OctreeNode {
    #[allow(dead_code)]
    pub fn new(bbox: BBox, depth: i32) -> OctreeNode {
        OctreeNode {
            bbox: bbox,
            depth: depth,
            children: Vec::new(),
            leaf_data: Vec::new(),
        }
    }

    fn subdivide(&mut self) {
        for x in 0u32..2 {
            for y in 0u32..2 {
                for z in 0u32..2 {
                    let len = self.bbox.len();

                    let child_bbox = BBox {
                        min: Vec3 {
                            x: self.bbox.min.x + x as f64 * len.x / 2.0,
                            y: self.bbox.min.y + y as f64 * len.y / 2.0,
                            z: self.bbox.min.z + z as f64 * len.z / 2.0
                        },
                        max: Vec3 {
                            x: self.bbox.max.x - (1 - x) as f64 * len.x / 2.0,
                            y: self.bbox.max.y - (1 - y) as f64 * len.y / 2.0,
                            z: self.bbox.max.z - (1 - z) as f64 * len.z / 2.0,
                        }
                    };

                    self.children.push(OctreeNode::new(child_bbox, self.depth - 1));
                }
            }
        }
    }

    #[allow(dead_code)]
    pub fn insert(&mut self, index: usize, object_bbox: BBox) -> () {
        // Max depth
        if self.depth <= 0 {
            self.leaf_data.push(OctreeData { index: index, bbox: object_bbox });
            return;
        }

        // Empty leaf node
        if self.is_leaf() && self.leaf_data.len() == 0 {
            self.leaf_data.push(OctreeData { index: index, bbox: object_bbox });
            return;
        }

        // Occupied leaf node and not max depth: subdivide node
        if self.is_leaf() && self.leaf_data.len() == 1 {
            self.subdivide();
            let old = self.leaf_data.remove(0);
            // Reinsert old node and then fall through to insert current object
            self.insert(old.index, old.bbox);
        }

        // Interior node (has children)
        for child in self.children.iter_mut() {
            if child.bbox.overlaps(&object_bbox) {
                child.insert(index, object_bbox);
            }
        }
    }

    fn is_leaf(&self) -> bool {
        self.children.len() == 0
    }
}

impl<T> FromIterator<T> for Octree<T> where T: PartialBoundingBox {
    fn from_iter<I>(iterator: I) -> Self where I: IntoIterator<Item=T> {
        let iterator = iterator.into_iter();

        let (finites, infinites): (Vec<T>, Vec<T>) =
            iterator.partition(|item| item.partial_bounding_box().is_some());

        // TODO(sell): why do we need to map here? &T isn't PartialBoundingBox,
        //             but we need to find out how to make it so.
        let bounds = BBox::from_union(finites.iter().map(|i| i.partial_bounding_box()))
            .unwrap_or(BBox::zero());

        // pbrt recommended max depth for a k-d tree (though, we're using an octree)
        // For a k-d tree: 8 + 1.3 * log2(N)
        let depth = (1.2 * (finites.len() as f64).log(8.0)).round() as i32;

        println!("Octree maximum depth {}", depth);
        let mut root_node = OctreeNode::new(bounds, depth);
        for (i, prim) in finites.iter().enumerate() {
            root_node.insert(i, prim.partial_bounding_box().unwrap());
        }

        Octree {
            prims: finites,
            infinites: infinites,
            root: root_node,
        }
    }
}

impl Octree<Box<Prim+Send+Sync>> {
    pub fn get_intersected_objects<'a>(&'a self, ray: &'a Ray) -> OctreeIterator<'a, Box<Prim+Send+Sync>> {
        OctreeIterator::new(self, ray)
    }
}

struct OctreeIterator<'a, T:'a> {
    prims: &'a [T],
    stack: Vec<&'a OctreeNode>,
    leaf_iter: Option<Iter<'a, OctreeData>>,
    ray: &'a Ray,
    infinites: Iter<'a, T>,
    just_infinites: bool
}

impl<'a> OctreeIterator<'a, Box<Prim+Send+Sync>> {
    fn new<'b>(octree: &'b Octree<Box<Prim+Send+Sync>>, ray: &'b Ray) -> OctreeIterator<'b, Box<Prim+Send+Sync>> {
            OctreeIterator {
            prims: &octree.prims[..],
            stack: vec![&octree.root],
            leaf_iter: None,
            ray: ray,
            infinites: octree.infinites.iter(),
            just_infinites: false
        }
    }
}

impl<'a> Iterator for OctreeIterator<'a, Box<Prim+Send+Sync>> {
    type Item = &'a Box<Prim+Send+Sync>;

    fn next(&mut self) -> Option<&'a Box<Prim+Send+Sync>> {
        if self.just_infinites {
            return self.infinites.next();
        }

        loop {
            let ray = self.ray;
            if let Some(leaf_iter) = self.leaf_iter.as_mut() {
                if let Some(val) = leaf_iter.filter(|x| x.bbox.intersects(ray)).next() {
                    return Some(&self.prims[val.index]);
                }
                // iterator went empty, so we'll pop from the stack and
                // iterate on the next node's children now,
            }

            if let Some(node) = self.stack.pop() {
                for child in node.children.iter() {
                    if child.bbox.intersects(self.ray) {
                        self.stack.push(child);
                    }
                }
                self.leaf_iter = Some(node.leaf_data.iter());
            } else {
                self.just_infinites = true;
                return self.infinites.next()
            }
        }
    }
}
<|endoftext|>


<commit_before>use std::collections::{HashMap, HashSet};

use render::Renderer;
use resman::AudioBuffer;
use routing::{Edge, GraphWatcher, NodeData, NodeHandle};
use routing::effect::{PrimitiveEffect, EffectData};
use util::unpack_f32;

type NodeMap = HashMap<NodeHandle, Node>;

#[derive(Default, Debug)]
pub struct RefRenderer {
    nodes: NodeMap,
}

#[derive(Default, Debug)]
struct Node {
    data: Option<MyNodeData>,
    inbound: HashSet<Edge>
}

#[derive(Debug)]
enum MyNodeData {
    /// This node is a non-primitive effect.
    UserNode(NodeMap),
    /// Primitive effect (delay, constant, etc).
    Primitive(PrimitiveEffect),
    /// External audio.
    Buffer(AudioBuffer),
}

impl Renderer for RefRenderer {
    fn get_sample(&mut self, time: u64, slot: u32) -> f32 {
        // Try to find the edge that goes to -> (Null, slot==slot)
        let root_handle = NodeHandle::toplevel();
        // empty graph is 0 = silence
        self.nodes.get(&root_handle).map_or(0f32, |node| {
            // find all edges to ([Null], slot=slot)
            self.sum_input_to_slot(&self.nodes, node, time, slot, &Vec::new()) as f32
        })
    }
}
impl RefRenderer {
    /// Get the value on an edge at a particular time
    /// When backtracking from the output, we push each Node onto the context if we enter inside of
    ///   it (i.e. if it's a nested DAG) & pop when exiting.
    fn get_value(&self, nodes: &NodeMap, edge: &Edge, time: u64, context: &Vec<(&NodeMap, NodeHandle)>) -> f64 {
        let from = edge.from_full();
        if *from.node_handle() == None {
            // Reading from one of the inputs to the top of `context`
            // TODO: we can avoid cloning by reversing the pop after recursing.
            let mut new_context = context.clone();
            let (new_nodes, head) = new_context.pop().unwrap();
            // Sum the inputs to the matching slot
            self.sum_input_to_slot(new_nodes, &new_nodes[&head], time, edge.from_slot(), &new_context)
        } else {
            // Reading from another node within the DAG
            let node = &nodes[&from];
            match *node.data.as_ref().expect("Expected node to have associated data") {
                MyNodeData::UserNode(ref new_nodes) => {
                    let mut new_context = context.clone();
                    new_context.push((nodes, from));
                    // Now find the *output* of the sub dag (or 0 if the sub dag has no outputs)
                    new_nodes.get(&NodeHandle::toplevel()).map_or(0f64, |root_node| {
                        self.sum_input_to_slot(new_nodes, root_node, time, edge.from_slot(), &new_context)
                    })
                },
                MyNodeData::Primitive(prim) => match prim {
                    // Output = sum of all inputs to slot 0.
                    PrimitiveEffect::Delay => {
                        // The only nonzero output is slot=0.
                        if edge.from_slot() != 0 {
                            warn!("Attempt to read from Delay slot != 0");
                            0f64
                        } else {
                            let delay_frames = self.sum_input_to_slot(nodes, node, time, 1, context);
                            // Clamp delay value to [0, u64::max]
                            let delay_int = if delay_frames < 0f64 {
                                0u64
                            } else if delay_frames > u64::max_value() as f64 {
                                // TODO: u64::max isn't precisely representable in f64;
                                // will this cause issues?
                                u64::max_value()
                            } else {
                                // Note: this conversion is flooring.
                                delay_frames as u64
                            };
                            // t<0 -> value is 0.
                            time.checked_sub(delay_int).map_or(0f64, |origin_time| {
                                self.sum_input_to_slot(nodes, node, origin_time, 0, context)
                            })
                        }
                    },
                    PrimitiveEffect::F32Constant => {
                        // Float value is encoded via the slot.
                        let value = edge.from_slot();
                        unpack_f32(value) as f64
                    },
                    PrimitiveEffect::Multiply => {
                        // The only nonzero output is slot=0.
                        if edge.from_slot() != 0 {
                            warn!("Attempt to read from Multiply slot != 0");
                            0f64
                        } else {
                            // Sum all inputs from slot=0 and slot=2 into two separate
                            // variables, then multiply them.
                            let val_a = self.sum_input_to_slot(nodes, node, time, 0, context);
                            let val_b = self.sum_input_to_slot(nodes, node, time, 1, context);
                            val_a * val_b
                        }
                    },
                    PrimitiveEffect::Divide => {
                        // The only nonzero output is slot=0.
                        if edge.from_slot() != 0 {
                            warn!("Attempt to read from MultInv slot != 0");
                            0f64
                        } else {
                            // Sum all inputs
                            let dividend = self.sum_input_to_slot(nodes, node, time, 0, context);
                            let divisor = self.sum_input_to_slot(nodes, node, time, 1, context);
                            dividend / divisor
                        }
                    },
                    PrimitiveEffect::Minimum => {
                        // The only nonzero output is slot=0.
                        if edge.from_slot() != 0 {
                            warn!("Attempt to read from Modulo slot != 0");
                            0f64
                        } else {
                            let input_a = self.sum_input_to_slot(nodes, node, time, 0, context);
                            let input_b = self.sum_input_to_slot(nodes, node, time, 1, context);
                            input_a.min(input_b)
                        }
                    }
                    PrimitiveEffect::Modulo => {
                        // The only nonzero output is slot=0.
                        if edge.from_slot() != 0 {
                            warn!("Attempt to read from Modulo slot != 0");
                            0f64
                        } else {
                            // Sum all dividends
                            let dividend = self.sum_input_to_slot(nodes, node, time, 0, context);
                            // Sum all divisors
                            let divisor = self.sum_input_to_slot(nodes, node, time, 1, context);
                            let rem = dividend % divisor;
                            if rem < 0f64 {
                                // TODO: We may be losing precision here, if rem is small.
                                // We should find a way to do true modulus.
                                rem + divisor
                            } else {
                                rem
                            }
                        }
                    },
                },
                MyNodeData::Buffer(ref buf) => buf.get(time, edge.from_slot()) as f64,
            }
        }
    }
    /// Return the sum of all inputs into a specific slot of the given
    /// node at the given time.
    fn sum_input_to_slot(&self, nodes: &NodeMap, node: &Node, time: u64, slot: u32, context: &Vec<(&NodeMap, NodeHandle)>) -> f64 {
        let edges_in = node.inbound.iter().filter(|in_edge| {
            in_edge.to_slot() == slot
        });
        edges_in.map(|edge| self.get_value(nodes, edge, time, context)).sum()
    }

    fn make_node(&self, effect: &NodeData) -> MyNodeData {
        match *effect.data() {
            EffectData::Primitive(e) => MyNodeData::Primitive(e),
            EffectData::Buffer(ref buff) => MyNodeData::Buffer(buff.clone()),
            EffectData::RouteGraph(ref graph) => {
                let mut nodes = HashMap::new();
                nodes.entry(NodeHandle::toplevel()).or_insert_with(Default::default);

                for (node, data) in graph.iter_nodes() {
                    nodes.insert(*node, Node::new(Some(self.make_node(data))));
                }
                for edge in graph.iter_edges() {
                    nodes.get_mut(&edge.to_full()).unwrap().inbound.insert(edge.clone());
                }
                MyNodeData::UserNode(nodes)
            }
        }
    }
}

impl GraphWatcher for RefRenderer {
    fn on_add_node(&mut self, handle: &NodeHandle, data: &NodeData) {
        let my_node_data = self.make_node(data);
        self.nodes.insert(*handle, Node::new(Some(my_node_data)));
        // If the node is part of a new DAG, allocate data so that future edges
        // to null within the DAG can be held.
        self.nodes.entry(NodeHandle::toplevel()).or_insert_with(Default::default);
    }
    fn on_del_node(&mut self, handle: &NodeHandle) {
        self.nodes.remove(handle);
    }
    fn on_add_edge(&mut self, edge: &Edge) {
        self.nodes.get_mut(&edge.to_full()).unwrap().inbound.insert(edge.clone());
    }
    fn on_del_edge(&mut self, edge: &Edge) {
        self.nodes.get_mut(&edge.to_full()).unwrap().inbound.remove(edge);
    }
}

impl Node {
    fn new(data: Option<MyNodeData>) -> Self {
        Node {
            data: data,
            inbound: HashSet::new(),
        }
    }
}
<commit_msg>Move boilerplate into standard trait impls.<commit_after>use std::collections::{HashMap, HashSet};
use std::ops::{Deref, DerefMut};

use render::Renderer;
use resman::AudioBuffer;
use routing::{Edge, GraphWatcher, NodeData, NodeHandle};
use routing::effect::{PrimitiveEffect, EffectData};
use util::unpack_f32;

#[derive(Debug)]
struct NodeMap {
    nodes: HashMap<NodeHandle, Node>,
}

#[derive(Default, Debug)]
pub struct RefRenderer {
    nodes: NodeMap,
}

#[derive(Default, Debug)]
struct Node {
    data: Option<MyNodeData>,
    inbound: HashSet<Edge>
}

#[derive(Debug)]
enum MyNodeData {
    /// This node is a non-primitive effect.
    UserNode(NodeMap),
    /// Primitive effect (delay, constant, etc).
    Primitive(PrimitiveEffect),
    /// External audio.
    Buffer(AudioBuffer),
}

impl Renderer for RefRenderer {
    fn get_sample(&mut self, time: u64, slot: u32) -> f32 {
        let node = &self.nodes[&NodeHandle::toplevel()];
        // find all edges to ([Null], slot=slot)
        self.sum_input_to_slot(&self.nodes, node, time, slot, &Vec::new()) as f32
    }
}
impl RefRenderer {
    /// Get the value on an edge at a particular time
    /// When backtracking from the output, we push each Node onto the context if we enter inside of
    ///   it (i.e. if it's a nested DAG) & pop when exiting.
    fn get_value(&self, nodes: &NodeMap, edge: &Edge, time: u64, context: &Vec<(&NodeMap, NodeHandle)>) -> f64 {
        let from = edge.from_full();
        if *from.node_handle() == None {
            // Reading from one of the inputs to the top of `context`
            // TODO: we can avoid cloning by reversing the pop after recursing.
            let mut new_context = context.clone();
            let (new_nodes, head) = new_context.pop().unwrap();
            // Sum the inputs to the matching slot
            self.sum_input_to_slot(new_nodes, &new_nodes[&head], time, edge.from_slot(), &new_context)
        } else {
            // Reading from another node within the DAG
            let node = &nodes[&from];
            match *node.data.as_ref().expect("Expected node to have associated data") {
                MyNodeData::UserNode(ref new_nodes) => {
                    let mut new_context = context.clone();
                    new_context.push((nodes, from));
                    // Now find the *output* of the sub dag (or 0 if the sub dag has no outputs)
                    new_nodes.get(&NodeHandle::toplevel()).map_or(0f64, |root_node| {
                        self.sum_input_to_slot(new_nodes, root_node, time, edge.from_slot(), &new_context)
                    })
                },
                MyNodeData::Primitive(prim) => match prim {
                    // Output = sum of all inputs to slot 0.
                    PrimitiveEffect::Delay => {
                        // The only nonzero output is slot=0.
                        if edge.from_slot() != 0 {
                            warn!("Attempt to read from Delay slot != 0");
                            0f64
                        } else {
                            let delay_frames = self.sum_input_to_slot(nodes, node, time, 1, context);
                            // Clamp delay value to [0, u64::max]
                            let delay_int = if delay_frames < 0f64 {
                                0u64
                            } else if delay_frames > u64::max_value() as f64 {
                                // TODO: u64::max isn't precisely representable in f64;
                                // will this cause issues?
                                u64::max_value()
                            } else {
                                // Note: this conversion is flooring.
                                delay_frames as u64
                            };
                            // t<0 -> value is 0.
                            time.checked_sub(delay_int).map_or(0f64, |origin_time| {
                                self.sum_input_to_slot(nodes, node, origin_time, 0, context)
                            })
                        }
                    },
                    PrimitiveEffect::F32Constant => {
                        // Float value is encoded via the slot.
                        let value = edge.from_slot();
                        unpack_f32(value) as f64
                    },
                    PrimitiveEffect::Multiply => {
                        // The only nonzero output is slot=0.
                        if edge.from_slot() != 0 {
                            warn!("Attempt to read from Multiply slot != 0");
                            0f64
                        } else {
                            // Sum all inputs from slot=0 and slot=2 into two separate
                            // variables, then multiply them.
                            let val_a = self.sum_input_to_slot(nodes, node, time, 0, context);
                            let val_b = self.sum_input_to_slot(nodes, node, time, 1, context);
                            val_a * val_b
                        }
                    },
                    PrimitiveEffect::Divide => {
                        // The only nonzero output is slot=0.
                        if edge.from_slot() != 0 {
                            warn!("Attempt to read from MultInv slot != 0");
                            0f64
                        } else {
                            // Sum all inputs
                            let dividend = self.sum_input_to_slot(nodes, node, time, 0, context);
                            let divisor = self.sum_input_to_slot(nodes, node, time, 1, context);
                            dividend / divisor
                        }
                    },
                    PrimitiveEffect::Minimum => {
                        // The only nonzero output is slot=0.
                        if edge.from_slot() != 0 {
                            warn!("Attempt to read from Modulo slot != 0");
                            0f64
                        } else {
                            let input_a = self.sum_input_to_slot(nodes, node, time, 0, context);
                            let input_b = self.sum_input_to_slot(nodes, node, time, 1, context);
                            input_a.min(input_b)
                        }
                    }
                    PrimitiveEffect::Modulo => {
                        // The only nonzero output is slot=0.
                        if edge.from_slot() != 0 {
                            warn!("Attempt to read from Modulo slot != 0");
                            0f64
                        } else {
                            // Sum all dividends
                            let dividend = self.sum_input_to_slot(nodes, node, time, 0, context);
                            // Sum all divisors
                            let divisor = self.sum_input_to_slot(nodes, node, time, 1, context);
                            let rem = dividend % divisor;
                            if rem < 0f64 {
                                // TODO: We may be losing precision here, if rem is small.
                                // We should find a way to do true modulus.
                                rem + divisor
                            } else {
                                rem
                            }
                        }
                    },
                },
                MyNodeData::Buffer(ref buf) => buf.get(time, edge.from_slot()) as f64,
            }
        }
    }
    /// Return the sum of all inputs into a specific slot of the given
    /// node at the given time.
    fn sum_input_to_slot(&self, nodes: &NodeMap, node: &Node, time: u64, slot: u32, context: &Vec<(&NodeMap, NodeHandle)>) -> f64 {
        let edges_in = node.inbound.iter().filter(|in_edge| {
            in_edge.to_slot() == slot
        });
        edges_in.map(|edge| self.get_value(nodes, edge, time, context)).sum()
    }

    fn make_node(&self, effect: &NodeData) -> MyNodeData {
        match *effect.data() {
            EffectData::Primitive(e) => MyNodeData::Primitive(e),
            EffectData::Buffer(ref buff) => MyNodeData::Buffer(buff.clone()),
            EffectData::RouteGraph(ref graph) => {
                let mut nodes: NodeMap = Default::default();

                for (node, data) in graph.iter_nodes() {
                    nodes.insert(*node, Node::new(Some(self.make_node(data))));
                }
                for edge in graph.iter_edges() {
                    nodes.get_mut(&edge.to_full()).unwrap().inbound.insert(edge.clone());
                }
                MyNodeData::UserNode(nodes)
            }
        }
    }
}

impl GraphWatcher for RefRenderer {
    fn on_add_node(&mut self, handle: &NodeHandle, data: &NodeData) {
        let my_node_data = self.make_node(data);
        self.nodes.insert(*handle, Node::new(Some(my_node_data)));
    }
    fn on_del_node(&mut self, handle: &NodeHandle) {
        self.nodes.remove(handle);
    }
    fn on_add_edge(&mut self, edge: &Edge) {
        self.nodes.get_mut(&edge.to_full()).unwrap().inbound.insert(edge.clone());
    }
    fn on_del_edge(&mut self, edge: &Edge) {
        self.nodes.get_mut(&edge.to_full()).unwrap().inbound.remove(edge);
    }
}

impl Node {
    fn new(data: Option<MyNodeData>) -> Self {
        Node {
            data: data,
            inbound: HashSet::new(),
        }
    }
}

impl Default for NodeMap {
    fn default() -> Self {
        // Create a NodeMap that already has an entry for the toplevel so that it can receive
        // edges.
        let nodes = Some((NodeHandle::toplevel(), Default::default())).into_iter().collect();
        Self { nodes }
    }
}

impl Deref for NodeMap {
    type Target = HashMap<NodeHandle, Node>;
    fn deref(&self) -> &Self::Target {
        &self.nodes
    }
}

impl DerefMut for NodeMap {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.nodes
    }
}
<|endoftext|>


<commit_before>use std::thread;
use std::sync::Arc;

use glutin::{
    GlProfile,
    GlRequest,
    ContextBuilder,
    dpi::LogicalSize,
    window::WindowBuilder,
    event::{
        Event as GlutinEvent,
        WindowEvent,
        DeviceEvent,
        KeyboardInput,
        VirtualKeyCode,
        ElementState,
    },
    event_loop::{ControlFlow, EventLoop, EventLoopProxy},
};
use tokio::{
    sync::Mutex,
    runtime::{self, Runtime},
};

use crate::ipc_protocol::ServerConnection;

use super::{
    RequestRedraw,
    app::App,
    renderer::{
        Renderer,
        display_list::DisplayList,
    },
};

/// Run the renderer process in the current thread
///
/// This function must run in the main thread ONLY
pub fn main() {
    assert_main_thread();

    // The state of the drawing and the state/drawings associated with each turtle
    let app = Arc::new(App::default());
    // All of the drawing primitives in the order in which they wil be drawn
    //
    // This is managed separately from the rest of the app state because the display list is shared
    // among pretty much everything and so critical sections containing the display list need to be
    // as short as possible.
    let display_list = Arc::new(Mutex::new(DisplayList::default()));

    let event_loop = EventLoop::with_user_event();

    // Spawn the actual server thread(s) that will handle incoming IPC messages and asynchronous
    // update the shared state
    let event_loop_proxy = event_loop.create_proxy();
    spawn_async_server(app.clone(), display_list.clone(), event_loop_proxy);

    // A single-threaded runtime for driving futures on the main thread only
    let basic_rt = runtime::Builder::new()
        .basic_scheduler()
        .build()
        .expect("unable to spawn tokio runtime to run turtle server process");

    let window_builder = {
        let drawing = basic_rt.handle().block_on(app.drawing_mut());
        WindowBuilder::new()
            .with_title(&drawing.title)
            .with_inner_size(LogicalSize {width: drawing.width, height: drawing.height})
    };

    // Create an OpenGL 3.x context for Pathfinder to use
    let gl_context = ContextBuilder::new()
        .with_gl(GlRequest::Latest)
        .with_gl_profile(GlProfile::Core)
        .with_vsync(true)
        .with_double_buffer(Some(true))
        .build_windowed(window_builder, &event_loop)
        .expect("bug: either could not create window or could not build OpenGL context");

    // Load OpenGL, and make the context current
    let gl_context = unsafe { gl_context.make_current().unwrap() };
    gl::load_with(|name| gl_context.get_proc_address(name) as *const _);

    // Need to draw using the physical size in pixels, not the logical size
    let draw_size = gl_context.window().inner_size();
    let mut renderer = Renderer::new(draw_size);

    event_loop.run(move |event, _, control_flow| match event {
        // Quit if the window is closed or if Esc is pressed and then released
        GlutinEvent::WindowEvent {
            event: WindowEvent::CloseRequested,
            ..
        } | GlutinEvent::WindowEvent {
            event: WindowEvent::Destroyed,
            ..
        } | GlutinEvent::DeviceEvent {
            event: DeviceEvent::Key(KeyboardInput {
                state: ElementState::Released,
                virtual_keycode: Some(VirtualKeyCode::Escape),
                ..
            }),
            ..
        } => {
            *control_flow = ControlFlow::Exit;
        },

        GlutinEvent::WindowEvent {
            event: WindowEvent::ScaleFactorChanged {scale_factor, ..},
            ..
        } => {
            renderer.set_scale_factor(scale_factor);
            //TODO: No idea if this next line is necessary or not
            gl_context.window().request_redraw();
        },

        GlutinEvent::WindowEvent {window_id, event} => {
            //TODO: Check if event modifies state and then send event
        },
        GlutinEvent::DeviceEvent {device_id, event} => {
            //TODO: Check if event modifies state and then send event
        },

        GlutinEvent::UserEvent(RequestRedraw) => {
            gl_context.window().request_redraw();
        },

        GlutinEvent::RedrawRequested(_) => {
            let drawing = {
                // Hold the drawing lock for as little time as possible
                let drawing = basic_rt.handle().block_on(app.drawing_mut());
                drawing.clone()
            };
            let display_list = basic_rt.handle().block_on(display_list.lock());

            let draw_size = gl_context.window().inner_size();
            renderer.render(draw_size, &display_list, &drawing);
            gl_context.swap_buffers().expect("unable to swap the buffer (for double buffering)");

            *control_flow = ControlFlow::Wait;
        },

        _ => {},
    });
}

fn assert_main_thread() {
    // This check isn't foolproof. Someone can always create a thread named "main".
    if thread::current().name().unwrap_or("") != "main" {
        // In order to maintain compatibility with MacOS, we need to make sure that windows are
        // only created on the main thread. We do this check on all platforms so that no one
        // can accidentally make a change that creates the window off of the main thread.
        unreachable!("bug: windows can only be created on the main thread");
    }
}

fn spawn_async_server(
    app: Arc<App>,
    display_list: Arc<Mutex<DisplayList>>,
    event_loop: EventLoopProxy<RequestRedraw>,
) {
    thread::spawn(move || {
        let mut runtime = Runtime::new()
            .expect("unable to spawn tokio runtime to run turtle server process");

        // Spawn root task
        runtime.block_on(async {
            let conn = ServerConnection::connect_stdin().await
                .expect("unable to establish turtle server connection");
            super::serve(conn, app, display_list, event_loop).await;
        });
    });
}
<commit_msg>server process only spawns a single runtime now -- shared through a handle<commit_after>use std::thread;
use std::sync::Arc;

use glutin::{
    GlProfile,
    GlRequest,
    ContextBuilder,
    dpi::LogicalSize,
    window::WindowBuilder,
    event::{
        Event as GlutinEvent,
        WindowEvent,
        DeviceEvent,
        KeyboardInput,
        VirtualKeyCode,
        ElementState,
    },
    event_loop::{ControlFlow, EventLoop, EventLoopProxy},
};
use tokio::{
    sync::Mutex,
    runtime::{Runtime, Handle},
};

use crate::ipc_protocol::ServerConnection;

use super::{
    RequestRedraw,
    app::App,
    renderer::{
        Renderer,
        display_list::DisplayList,
    },
};

/// Run the renderer process in the current thread
///
/// This function must run in the main thread ONLY
pub fn main() {
    assert_main_thread();

    // The runtime for driving async code
    let runtime = Runtime::new()
        .expect("unable to spawn tokio runtime to run turtle server process");

    // The state of the drawing and the state/drawings associated with each turtle
    let app = Arc::new(App::default());
    // All of the drawing primitives in the order in which they wil be drawn
    //
    // This is managed separately from the rest of the app state because the display list is shared
    // among pretty much everything and so critical sections containing the display list need to be
    // as short as possible.
    let display_list = Arc::new(Mutex::new(DisplayList::default()));

    let event_loop = EventLoop::with_user_event();

    // Spawn the actual server thread(s) that will handle incoming IPC messages and asynchronous
    // update the shared state
    let event_loop_proxy = event_loop.create_proxy();
    spawn_async_server(runtime.handle().clone(), app.clone(), display_list.clone(), event_loop_proxy);

    let window_builder = {
        let drawing = runtime.handle().block_on(app.drawing_mut());
        WindowBuilder::new()
            .with_title(&drawing.title)
            .with_inner_size(LogicalSize {width: drawing.width, height: drawing.height})
    };

    // Create an OpenGL 3.x context for Pathfinder to use
    let gl_context = ContextBuilder::new()
        .with_gl(GlRequest::Latest)
        .with_gl_profile(GlProfile::Core)
        .with_vsync(true)
        .with_double_buffer(Some(true))
        .build_windowed(window_builder, &event_loop)
        .expect("bug: either could not create window or could not build OpenGL context");

    // Load OpenGL, and make the context current
    let gl_context = unsafe { gl_context.make_current().unwrap() };
    gl::load_with(|name| gl_context.get_proc_address(name) as *const _);

    // Need to draw using the physical size in pixels, not the logical size
    let draw_size = gl_context.window().inner_size();
    let mut renderer = Renderer::new(draw_size);

    event_loop.run(move |event, _, control_flow| match event {
        // Quit if the window is closed or if Esc is pressed and then released
        GlutinEvent::WindowEvent {
            event: WindowEvent::CloseRequested,
            ..
        } | GlutinEvent::WindowEvent {
            event: WindowEvent::Destroyed,
            ..
        } | GlutinEvent::DeviceEvent {
            event: DeviceEvent::Key(KeyboardInput {
                state: ElementState::Released,
                virtual_keycode: Some(VirtualKeyCode::Escape),
                ..
            }),
            ..
        } => {
            *control_flow = ControlFlow::Exit;
        },

        GlutinEvent::WindowEvent {
            event: WindowEvent::ScaleFactorChanged {scale_factor, ..},
            ..
        } => {
            renderer.set_scale_factor(scale_factor);
            //TODO: No idea if this next line is necessary or not
            gl_context.window().request_redraw();
        },

        GlutinEvent::WindowEvent {window_id, event} => {
            //TODO: Check if event modifies state and then send event
        },
        GlutinEvent::DeviceEvent {device_id, event} => {
            //TODO: Check if event modifies state and then send event
        },

        GlutinEvent::UserEvent(RequestRedraw) => {
            gl_context.window().request_redraw();
        },

        GlutinEvent::RedrawRequested(_) => {
            let drawing = {
                // Hold the drawing lock for as little time as possible
                let drawing = runtime.handle().block_on(app.drawing_mut());
                drawing.clone()
            };
            let display_list = runtime.handle().block_on(display_list.lock());

            let draw_size = gl_context.window().inner_size();
            renderer.render(draw_size, &display_list, &drawing);
            gl_context.swap_buffers().expect("unable to swap the buffer (for double buffering)");

            *control_flow = ControlFlow::Wait;
        },

        _ => {},
    });
}

fn assert_main_thread() {
    // This check isn't foolproof. Someone can always create a thread named "main".
    if thread::current().name().unwrap_or("") != "main" {
        // In order to maintain compatibility with MacOS, we need to make sure that windows are
        // only created on the main thread. We do this check on all platforms so that no one
        // can accidentally make a change that creates the window off of the main thread.
        unreachable!("bug: windows can only be created on the main thread");
    }
}

fn spawn_async_server(
    handle: Handle,
    app: Arc<App>,
    display_list: Arc<Mutex<DisplayList>>,
    event_loop: EventLoopProxy<RequestRedraw>,
) {
    thread::spawn(move || {
        // Spawn root task
        handle.spawn(async {
            let conn = ServerConnection::connect_stdin().await
                .expect("unable to establish turtle server connection");
            super::serve(conn, app, display_list, event_loop).await;
        });
    });
}
<|endoftext|>