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

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

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

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

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

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

  let mut hasher = DefaultHasher::new();

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

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

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

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

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

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

  Some((hash, hash_path))
}

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

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

  let asm_files = &[
    "src/x86/ipred.asm",
    "src/x86/ipred_ssse3.asm",
    "src/x86/itx.asm",
    "src/x86/itx_ssse3.asm",
    "src/x86/looprestoration16_avx2.asm",
    "src/x86/mc_avx2.asm",
    "src/x86/mc16_avx2.asm",
    "src/x86/mc_avx512.asm",
    "src/x86/mc_sse.asm",
    "src/x86/me.asm",
    "src/x86/sad_sse2.asm",
    "src/x86/sad_avx.asm",
    "src/x86/satd.asm",
    "src/x86/sse.asm",
    "src/x86/cdef_rav1e.asm",
    "src/x86/cdef_sse.asm",
    "src/x86/cdef16_avx2.asm",
    "src/x86/cdef16_sse.asm",
    "src/x86/tables.asm",
  ];

  if let Some((hash, hash_path)) =
    hash_changed(asm_files, &out_dir, &dest_path)
  {
    let mut config_include_arg = String::from("-I");
    config_include_arg.push_str(&out_dir);
    config_include_arg.push('/');
    nasm_rs::compile_library_args(
      "rav1easm",
      asm_files,
      &[&config_include_arg, "-Isrc/"],
    )
    .expect(
      "NASM build failed. Make sure you have nasm installed. https://nasm.us",
    );
    std::fs::write(hash_path, &hash[..]).unwrap();
  } else {
    println!("cargo:rustc-link-search={}", out_dir);
  }
  println!("cargo:rustc-link-lib=static=rav1easm");
  rerun_dir("src/x86");
  rerun_dir("src/ext/x86");
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  let mut hasher = DefaultHasher::new();

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

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

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

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

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

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

  Some((hash, hash_path))
}

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

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

  let asm_files = &[
    "src/x86/ipred.asm",
    "src/x86/ipred_ssse3.asm",
    "src/x86/itx.asm",
    "src/x86/itx_ssse3.asm",
    "src/x86/looprestoration16_avx2.asm",
    "src/x86/mc_avx2.asm",
    "src/x86/mc16_avx2.asm",
    "src/x86/mc_avx512.asm",
    "src/x86/mc_sse.asm",
    "src/x86/me.asm",
    "src/x86/sad_sse2.asm",
    "src/x86/sad_avx.asm",
    "src/x86/satd.asm",
    "src/x86/sse.asm",
    "src/x86/cdef_rav1e.asm",
    "src/x86/cdef_sse.asm",
    "src/x86/cdef16_avx2.asm",
    "src/x86/cdef16_sse.asm",
    "src/x86/tables.asm",
  ];

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


<commit_before>#![deny(warnings)]

extern crate cmake;

use std::env;

fn main() {
    let dst = cmake::Config::new("src").build();

    println!("cargo:rustc-link-search=native={}/lib\n\
              cargo:rustc-link-lib=static=brotli\n\
              cargo:rustc-link-lib=static=woff2\n\
              cargo:rustc-link-lib=static=ots",
        dst.display());
    let target = env::var("TARGET").unwrap();
    if !target.contains("msvc") {
        println!("cargo:rustc-link-lib=stdc++");
    }
}
<commit_msg>Switch to libc++ on OS X<commit_after>#![deny(warnings)]

extern crate cmake;

use std::env;

fn main() {
    let dst = cmake::Config::new("src").build();

    println!("cargo:rustc-link-search=native={}/lib\n\
              cargo:rustc-link-lib=static=brotli\n\
              cargo:rustc-link-lib=static=woff2\n\
              cargo:rustc-link-lib=static=ots",
        dst.display());
    let target = env::var("TARGET").unwrap();
    if target.contains("apple") {
        println!("cargo:rustc-link-lib=c++");
    } else if !target.contains("msvc") {
        println!("cargo:rustc-link-lib=stdc++");
    }
}
<|endoftext|>


<commit_before>use crate::context::Context;
use crate::instance::{
    siginfo_ext::SiginfoExt, FaultDetails, Instance, State, TerminationDetails, CURRENT_INSTANCE,
    HOST_CTX,
};
use crate::sysdeps::UContextPtr;
use failure::Error;
use lazy_static::lazy_static;
use libc::{c_int, c_void, siginfo_t, SIGBUS, SIGSEGV};
use lucet_module_data::TrapCode;
use nix::sys::signal::{
    pthread_sigmask, raise, sigaction, SaFlags, SigAction, SigHandler, SigSet, SigmaskHow, Signal,
};
use std::sync::Mutex;

lazy_static! {
    // TODO: work out an alternative to this that is signal-safe for `reraise_host_signal_in_handler`
    static ref LUCET_SIGNAL_STATE: Mutex<Option<SignalState>> = Mutex::new(None);
}

/// The value returned by
/// [`Instance.signal_handler`](struct.Instance.html#structfield.signal_handler) to determine the
/// outcome of a handled signal.
pub enum SignalBehavior {
    /// Use default behavior, which switches back to the host with `State::Fault` populated.
    Default,
    /// Override default behavior and cause the instance to continue.
    Continue,
    /// Override default behavior and cause the instance to terminate.
    Terminate,
}

pub type SignalHandler = dyn Fn(
    &Instance,
    &Option<TrapCode>,
    libc::c_int,
    *const siginfo_t,
    *const c_void,
) -> SignalBehavior;

pub fn signal_handler_none(
    _inst: &Instance,
    _trapcode: &Option<TrapCode>,
    _signum: libc::c_int,
    _siginfo_ptr: *const siginfo_t,
    _ucontext_ptr: *const c_void,
) -> SignalBehavior {
    SignalBehavior::Default
}

impl Instance {
    pub(crate) fn with_signals_on<F, R>(&mut self, f: F) -> Result<R, Error>
    where
        F: FnOnce(&mut Instance) -> Result<R, Error>,
    {
        // setup signal stack for this thread
        let guest_sigstack = SigStack::new(
            self.alloc.slot().sigstack,
            SigStackFlags::empty(),
            libc::SIGSTKSZ,
        );
        let saved_sigstack =
            unsafe { sigaltstack(&guest_sigstack).expect("saving sigaltstack succeeds") };

        let mut ostate = LUCET_SIGNAL_STATE.lock().unwrap();
        if let Some(ref mut state) = *ostate {
            state.counter += 1;
        } else {
            unsafe {
                setup_guest_signal_state(&mut ostate);
            }
        }
        drop(ostate);

        // run the body
        let res = f(self);

        let mut ostate = LUCET_SIGNAL_STATE.lock().unwrap();
        let counter_zero = if let Some(ref mut state) = *ostate {
            state.counter -= 1;
            if state.counter == 0 {
                unsafe {
                    // restore the host signal stack
                    sigaltstack(&saved_sigstack).expect("sigaltstack restoration succeeds");
                    restore_host_signal_state(state);
                }
                true
            } else {
                false
            }
        } else {
            panic!("signal handlers weren't installed at instance exit");
        };
        if counter_zero {
            *ostate = None;
        }

        res
    }
}

/// Signal handler installed during instance execution.
///
/// This function is only designed to handle signals that are the direct result of execution of a
/// hardware instruction from the faulting WASM thread. It thus safely assumes the signal is
/// directed specifically at this thread (i.e. not a different thread or the process as a whole).
extern "C" fn handle_signal(signum: c_int, siginfo_ptr: *mut siginfo_t, ucontext_ptr: *mut c_void) {
    let signal = Signal::from_c_int(signum).expect("signum is a valid signal");
    if !(signal == Signal::SIGBUS
        || signal == Signal::SIGSEGV
        || signal == Signal::SIGILL
        || signal == Signal::SIGFPE)
    {
        panic!("unexpected signal in guest signal handler: {:?}", signal);
    }
    assert!(!siginfo_ptr.is_null(), "siginfo must not be null");

    // Safety: when using a SA_SIGINFO sigaction, the third argument can be cast to a `ucontext_t`
    // pointer per the manpage
    assert!(!ucontext_ptr.is_null(), "ucontext_ptr must not be null");
    let ctx = UContextPtr::new(ucontext_ptr);
    let rip = ctx.get_ip();

    let switch_to_host = CURRENT_INSTANCE.with(|current_instance| {
        let mut current_instance = current_instance.borrow_mut();

        if current_instance.is_none() {
            // If there is no current instance, we've caught a signal raised by a thread that's not
            // running a lucet instance. Restore the host signal handler and reraise the signal,
            // then return if the host handler returns
            unsafe {
                reraise_host_signal_in_handler(signal, signum, siginfo_ptr, ucontext_ptr);
            }
            // don't try context-switching
            return false;
        }

        // Safety: the memory pointed to by CURRENT_INSTANCE should be a valid instance. This is not
        // a trivial property, but relies on the compiler not emitting guest programs that can
        // overwrite the instance.
        let inst = unsafe {
            current_instance
                .as_mut()
                .expect("current instance exists")
                .as_mut()
        };

        let trapcode = inst.module.lookup_trapcode(rip);

        let behavior = (inst.signal_handler)(inst, &trapcode, signum, siginfo_ptr, ucontext_ptr);
        match behavior {
            SignalBehavior::Continue => {
                // return to the guest context without making any modifications to the instance
                false
            }
            SignalBehavior::Terminate => {
                // set the state before jumping back to the host context
                inst.state = State::Terminated {
                    details: TerminationDetails::Signal,
                };
                true
            }
            SignalBehavior::Default => {
                // safety: pointer is checked for null at the top of the function, and the
                // manpage guarantees that a siginfo_t will be passed as the second argument
                let siginfo = unsafe { *siginfo_ptr };
                let rip_addr = rip as usize;
                // If the trap table lookup returned unknown, it is a fatal error
                let unknown_fault = trapcode.is_none();

                // If the trap was a segv or bus fault and the addressed memory was outside the
                // guard pages, it is also a fatal error
                let outside_guard = (siginfo.si_signo == SIGSEGV || siginfo.si_signo == SIGBUS)
                    && !inst.alloc.addr_in_heap_guard(siginfo.si_addr());

                // record the fault and jump back to the host context
                inst.state = State::Fault {
                    details: FaultDetails {
                        fatal: unknown_fault || outside_guard,
                        trapcode: trapcode,
                        rip_addr,
                        // Details set to `None` here: have to wait until `verify_trap_safety` to
                        // fill in these details, because access may not be signal safe.
                        rip_addr_details: None,
                    },
                    siginfo,
                    context: ctx.into(),
                };
                true
            }
        }
    });

    if switch_to_host {
        HOST_CTX.with(|host_ctx| unsafe {
            Context::set_from_signal(&*host_ctx.get())
                .expect("can successfully switch back to the host context");
        });
        unreachable!()
    }
}

struct SignalState {
    counter: usize,
    saved_sigbus: SigAction,
    saved_sigfpe: SigAction,
    saved_sigill: SigAction,
    saved_sigsegv: SigAction,
}

// raw pointers in the saved types
unsafe impl Send for SignalState {}

unsafe fn setup_guest_signal_state(ostate: &mut Option<SignalState>) {
    let mut masked_signals = SigSet::empty();
    masked_signals.add(Signal::SIGBUS);
    masked_signals.add(Signal::SIGFPE);
    masked_signals.add(Signal::SIGILL);
    masked_signals.add(Signal::SIGSEGV);

    // setup signal handlers
    let sa = SigAction::new(
        SigHandler::SigAction(handle_signal),
        SaFlags::SA_RESTART | SaFlags::SA_SIGINFO | SaFlags::SA_ONSTACK,
        masked_signals,
    );
    let saved_sigbus = sigaction(Signal::SIGBUS, &sa).expect("sigaction succeeds");
    let saved_sigfpe = sigaction(Signal::SIGFPE, &sa).expect("sigaction succeeds");
    let saved_sigill = sigaction(Signal::SIGILL, &sa).expect("sigaction succeeds");
    let saved_sigsegv = sigaction(Signal::SIGSEGV, &sa).expect("sigaction succeeds");

    *ostate = Some(SignalState {
        counter: 1,
        saved_sigbus,
        saved_sigfpe,
        saved_sigill,
        saved_sigsegv,
    });
}

unsafe fn restore_host_signal_state(state: &mut SignalState) {
    // restore signal handlers
    sigaction(Signal::SIGBUS, &state.saved_sigbus).expect("sigaction succeeds");
    sigaction(Signal::SIGFPE, &state.saved_sigfpe).expect("sigaction succeeds");
    sigaction(Signal::SIGILL, &state.saved_sigill).expect("sigaction succeeds");
    sigaction(Signal::SIGSEGV, &state.saved_sigsegv).expect("sigaction succeeds");
}

unsafe fn reraise_host_signal_in_handler(
    sig: Signal,
    signum: libc::c_int,
    siginfo_ptr: *mut libc::siginfo_t,
    ucontext_ptr: *mut c_void,
) {
    let saved_handler = {
        // TODO: avoid taking a mutex here, probably by having some static muts just for this
        // function
        if let Some(ref state) = *LUCET_SIGNAL_STATE.lock().unwrap() {
            match sig {
                Signal::SIGBUS => state.saved_sigbus.clone(),
                Signal::SIGFPE => state.saved_sigfpe.clone(),
                Signal::SIGILL => state.saved_sigill.clone(),
                Signal::SIGSEGV => state.saved_sigsegv.clone(),
                sig => panic!(
                    "unexpected signal in reraise_host_signal_in_handler: {:?}",
                    sig
                ),
            }
        } else {
            // this case is very fishy; it can arise when the last lucet instance spins down and
            // uninstalls the lucet handlers while a signal handler is running on this thread, but
            // before taking the mutex above. The theory is that if this has happened, the host
            // handler has been reinstalled, so we shouldn't end up back here if we reraise

            // unmask the signal to reraise; we don't have to restore it because the handler will return
            // after this. If it signals again between here and now, that's a double fault and the
            // process is going to die anyway
            let mut unmask = SigSet::empty();
            unmask.add(sig);
            pthread_sigmask(SigmaskHow::SIG_UNBLOCK, Some(&unmask), None)
                .expect("pthread_sigmask succeeds");
            // if there's no current signal state, just re-raise and hope for the best
            raise(sig).expect("raise succeeds");
            return;
        }
    };

    match saved_handler.handler() {
        SigHandler::SigDfl => {
            // reinstall default signal handler and reraise the signal; this should terminate the
            // program
            sigaction(sig, &saved_handler).expect("sigaction succeeds");
            let mut unmask = SigSet::empty();
            unmask.add(sig);
            pthread_sigmask(SigmaskHow::SIG_UNBLOCK, Some(&unmask), None)
                .expect("pthread_sigmask succeeds");
            raise(sig).expect("raise succeeds");
        }
        SigHandler::SigIgn => {
            // don't do anything; if we hit this case, whatever program is hosting us is almost
            // certainly doing something wrong, because our set of signals requires intervention to
            // proceed
            return;
        }
        SigHandler::Handler(f) => {
            // call the saved handler directly so there is no altstack confusion
            f(signum)
        }
        SigHandler::SigAction(f) => {
            // call the saved handler directly so there is no altstack confusion
            f(signum, siginfo_ptr, ucontext_ptr)
        }
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// A collection of wrappers that will be upstreamed to the `nix` crate eventually.
////////////////////////////////////////////////////////////////////////////////////////////////////

use bitflags::bitflags;

#[derive(Copy, Clone)]
pub struct SigStack {
    stack: libc::stack_t,
}

impl SigStack {
    pub fn new(sp: *mut libc::c_void, flags: SigStackFlags, size: libc::size_t) -> SigStack {
        let mut stack = unsafe { std::mem::uninitialized::<libc::stack_t>() };
        stack.ss_sp = sp;
        stack.ss_flags = flags.bits();
        stack.ss_size = size;
        SigStack { stack }
    }

    pub fn flags(&self) -> SigStackFlags {
        SigStackFlags::from_bits_truncate(self.stack.ss_flags)
    }
}

impl AsRef<libc::stack_t> for SigStack {
    fn as_ref(&self) -> &libc::stack_t {
        &self.stack
    }
}

impl AsMut<libc::stack_t> for SigStack {
    fn as_mut(&mut self) -> &mut libc::stack_t {
        &mut self.stack
    }
}

bitflags! {
    pub struct SigStackFlags: libc::c_int {
        const SS_ONSTACK = libc::SS_ONSTACK;
        const SS_DISABLE = libc::SS_DISABLE;
    }
}

pub unsafe fn sigaltstack(ss: &SigStack) -> nix::Result<SigStack> {
    let mut oldstack = std::mem::uninitialized::<libc::stack_t>();

    let res = libc::sigaltstack(
        &ss.stack as *const libc::stack_t,
        &mut oldstack as *mut libc::stack_t,
    );

    nix::errno::Errno::result(res).map(|_| SigStack { stack: oldstack })
}
<commit_msg>Improve sigaltstack(2) usage in lucet-runtime<commit_after>use crate::context::Context;
use crate::instance::{
    siginfo_ext::SiginfoExt, FaultDetails, Instance, State, TerminationDetails, CURRENT_INSTANCE,
    HOST_CTX,
};
use crate::sysdeps::UContextPtr;
use failure::Error;
use lazy_static::lazy_static;
use libc::{c_int, c_void, siginfo_t, SIGBUS, SIGSEGV};
use lucet_module_data::TrapCode;
use nix::sys::signal::{
    pthread_sigmask, raise, sigaction, SaFlags, SigAction, SigHandler, SigSet, SigmaskHow, Signal,
};
use std::sync::Mutex;

lazy_static! {
    // TODO: work out an alternative to this that is signal-safe for `reraise_host_signal_in_handler`
    static ref LUCET_SIGNAL_STATE: Mutex<Option<SignalState>> = Mutex::new(None);
}

/// The value returned by
/// [`Instance.signal_handler`](struct.Instance.html#structfield.signal_handler) to determine the
/// outcome of a handled signal.
pub enum SignalBehavior {
    /// Use default behavior, which switches back to the host with `State::Fault` populated.
    Default,
    /// Override default behavior and cause the instance to continue.
    Continue,
    /// Override default behavior and cause the instance to terminate.
    Terminate,
}

pub type SignalHandler = dyn Fn(
    &Instance,
    &Option<TrapCode>,
    libc::c_int,
    *const siginfo_t,
    *const c_void,
) -> SignalBehavior;

pub fn signal_handler_none(
    _inst: &Instance,
    _trapcode: &Option<TrapCode>,
    _signum: libc::c_int,
    _siginfo_ptr: *const siginfo_t,
    _ucontext_ptr: *const c_void,
) -> SignalBehavior {
    SignalBehavior::Default
}

impl Instance {
    pub(crate) fn with_signals_on<F, R>(&mut self, f: F) -> Result<R, Error>
    where
        F: FnOnce(&mut Instance) -> Result<R, Error>,
    {
        // setup signal stack for this thread
        let guest_sigstack = SigStack::new(
            self.alloc.slot().sigstack,
            SigStackFlags::empty(),
            libc::SIGSTKSZ,
        );
        let previous_sigstack = unsafe { sigaltstack(Some(guest_sigstack)) }
            .expect("enabling or changing the signal stack succeeds");
        if let Some(previous_sigstack) = previous_sigstack {
            assert!(
                !previous_sigstack
                    .flags()
                    .contains(SigStackFlags::SS_ONSTACK),
                "an instance was created with a signal stack"
            );
        }
        let mut ostate = LUCET_SIGNAL_STATE.lock().unwrap();
        if let Some(ref mut state) = *ostate {
            state.counter += 1;
        } else {
            unsafe {
                setup_guest_signal_state(&mut ostate);
            }
        }
        drop(ostate);

        // run the body
        let res = f(self);

        let mut ostate = LUCET_SIGNAL_STATE.lock().unwrap();
        let counter_zero = if let Some(ref mut state) = *ostate {
            state.counter -= 1;
            if state.counter == 0 {
                unsafe {
                    // restore the host signal stack
                    if !altstack_flags()
                        .expect("the current stack flags could be retrieved")
                        .contains(SigStackFlags::SS_ONSTACK)
                    {
                        sigaltstack(previous_sigstack).expect("sigaltstack restoration succeeds");
                    }
                    restore_host_signal_state(state);
                }
                true
            } else {
                false
            }
        } else {
            panic!("signal handlers weren't installed at instance exit");
        };
        if counter_zero {
            *ostate = None;
        }

        res
    }
}

/// Signal handler installed during instance execution.
///
/// This function is only designed to handle signals that are the direct result of execution of a
/// hardware instruction from the faulting WASM thread. It thus safely assumes the signal is
/// directed specifically at this thread (i.e. not a different thread or the process as a whole).
extern "C" fn handle_signal(signum: c_int, siginfo_ptr: *mut siginfo_t, ucontext_ptr: *mut c_void) {
    let signal = Signal::from_c_int(signum).expect("signum is a valid signal");
    if !(signal == Signal::SIGBUS
        || signal == Signal::SIGSEGV
        || signal == Signal::SIGILL
        || signal == Signal::SIGFPE)
    {
        panic!("unexpected signal in guest signal handler: {:?}", signal);
    }
    assert!(!siginfo_ptr.is_null(), "siginfo must not be null");

    // Safety: when using a SA_SIGINFO sigaction, the third argument can be cast to a `ucontext_t`
    // pointer per the manpage
    assert!(!ucontext_ptr.is_null(), "ucontext_ptr must not be null");
    let ctx = UContextPtr::new(ucontext_ptr);
    let rip = ctx.get_ip();

    let switch_to_host = CURRENT_INSTANCE.with(|current_instance| {
        let mut current_instance = current_instance.borrow_mut();

        if current_instance.is_none() {
            // If there is no current instance, we've caught a signal raised by a thread that's not
            // running a lucet instance. Restore the host signal handler and reraise the signal,
            // then return if the host handler returns
            unsafe {
                reraise_host_signal_in_handler(signal, signum, siginfo_ptr, ucontext_ptr);
            }
            // don't try context-switching
            return false;
        }

        // Safety: the memory pointed to by CURRENT_INSTANCE should be a valid instance. This is not
        // a trivial property, but relies on the compiler not emitting guest programs that can
        // overwrite the instance.
        let inst = unsafe {
            current_instance
                .as_mut()
                .expect("current instance exists")
                .as_mut()
        };

        let trapcode = inst.module.lookup_trapcode(rip);

        let behavior = (inst.signal_handler)(inst, &trapcode, signum, siginfo_ptr, ucontext_ptr);
        match behavior {
            SignalBehavior::Continue => {
                // return to the guest context without making any modifications to the instance
                false
            }
            SignalBehavior::Terminate => {
                // set the state before jumping back to the host context
                inst.state = State::Terminated {
                    details: TerminationDetails::Signal,
                };
                true
            }
            SignalBehavior::Default => {
                // safety: pointer is checked for null at the top of the function, and the
                // manpage guarantees that a siginfo_t will be passed as the second argument
                let siginfo = unsafe { *siginfo_ptr };
                let rip_addr = rip as usize;
                // If the trap table lookup returned unknown, it is a fatal error
                let unknown_fault = trapcode.is_none();

                // If the trap was a segv or bus fault and the addressed memory was outside the
                // guard pages, it is also a fatal error
                let outside_guard = (siginfo.si_signo == SIGSEGV || siginfo.si_signo == SIGBUS)
                    && !inst.alloc.addr_in_heap_guard(siginfo.si_addr());

                // record the fault and jump back to the host context
                inst.state = State::Fault {
                    details: FaultDetails {
                        fatal: unknown_fault || outside_guard,
                        trapcode: trapcode,
                        rip_addr,
                        // Details set to `None` here: have to wait until `verify_trap_safety` to
                        // fill in these details, because access may not be signal safe.
                        rip_addr_details: None,
                    },
                    siginfo,
                    context: ctx.into(),
                };
                true
            }
        }
    });

    if switch_to_host {
        HOST_CTX.with(|host_ctx| unsafe {
            Context::set_from_signal(&*host_ctx.get())
                .expect("can successfully switch back to the host context");
        });
        unreachable!()
    }
}

struct SignalState {
    counter: usize,
    saved_sigbus: SigAction,
    saved_sigfpe: SigAction,
    saved_sigill: SigAction,
    saved_sigsegv: SigAction,
}

// raw pointers in the saved types
unsafe impl Send for SignalState {}

unsafe fn setup_guest_signal_state(ostate: &mut Option<SignalState>) {
    let mut masked_signals = SigSet::empty();
    masked_signals.add(Signal::SIGBUS);
    masked_signals.add(Signal::SIGFPE);
    masked_signals.add(Signal::SIGILL);
    masked_signals.add(Signal::SIGSEGV);

    // setup signal handlers
    let sa = SigAction::new(
        SigHandler::SigAction(handle_signal),
        SaFlags::SA_RESTART | SaFlags::SA_SIGINFO | SaFlags::SA_ONSTACK,
        masked_signals,
    );
    let saved_sigbus = sigaction(Signal::SIGBUS, &sa).expect("sigaction succeeds");
    let saved_sigfpe = sigaction(Signal::SIGFPE, &sa).expect("sigaction succeeds");
    let saved_sigill = sigaction(Signal::SIGILL, &sa).expect("sigaction succeeds");
    let saved_sigsegv = sigaction(Signal::SIGSEGV, &sa).expect("sigaction succeeds");

    *ostate = Some(SignalState {
        counter: 1,
        saved_sigbus,
        saved_sigfpe,
        saved_sigill,
        saved_sigsegv,
    });
}

unsafe fn restore_host_signal_state(state: &mut SignalState) {
    // restore signal handlers
    sigaction(Signal::SIGBUS, &state.saved_sigbus).expect("sigaction succeeds");
    sigaction(Signal::SIGFPE, &state.saved_sigfpe).expect("sigaction succeeds");
    sigaction(Signal::SIGILL, &state.saved_sigill).expect("sigaction succeeds");
    sigaction(Signal::SIGSEGV, &state.saved_sigsegv).expect("sigaction succeeds");
}

unsafe fn reraise_host_signal_in_handler(
    sig: Signal,
    signum: libc::c_int,
    siginfo_ptr: *mut libc::siginfo_t,
    ucontext_ptr: *mut c_void,
) {
    let saved_handler = {
        // TODO: avoid taking a mutex here, probably by having some static muts just for this
        // function
        if let Some(ref state) = *LUCET_SIGNAL_STATE.lock().unwrap() {
            match sig {
                Signal::SIGBUS => state.saved_sigbus.clone(),
                Signal::SIGFPE => state.saved_sigfpe.clone(),
                Signal::SIGILL => state.saved_sigill.clone(),
                Signal::SIGSEGV => state.saved_sigsegv.clone(),
                sig => panic!(
                    "unexpected signal in reraise_host_signal_in_handler: {:?}",
                    sig
                ),
            }
        } else {
            // this case is very fishy; it can arise when the last lucet instance spins down and
            // uninstalls the lucet handlers while a signal handler is running on this thread, but
            // before taking the mutex above. The theory is that if this has happened, the host
            // handler has been reinstalled, so we shouldn't end up back here if we reraise

            // unmask the signal to reraise; we don't have to restore it because the handler will return
            // after this. If it signals again between here and now, that's a double fault and the
            // process is going to die anyway
            let mut unmask = SigSet::empty();
            unmask.add(sig);
            pthread_sigmask(SigmaskHow::SIG_UNBLOCK, Some(&unmask), None)
                .expect("pthread_sigmask succeeds");
            // if there's no current signal state, just re-raise and hope for the best
            raise(sig).expect("raise succeeds");
            return;
        }
    };

    match saved_handler.handler() {
        SigHandler::SigDfl => {
            // reinstall default signal handler and reraise the signal; this should terminate the
            // program
            sigaction(sig, &saved_handler).expect("sigaction succeeds");
            let mut unmask = SigSet::empty();
            unmask.add(sig);
            pthread_sigmask(SigmaskHow::SIG_UNBLOCK, Some(&unmask), None)
                .expect("pthread_sigmask succeeds");
            raise(sig).expect("raise succeeds");
        }
        SigHandler::SigIgn => {
            // don't do anything; if we hit this case, whatever program is hosting us is almost
            // certainly doing something wrong, because our set of signals requires intervention to
            // proceed
            return;
        }
        SigHandler::Handler(f) => {
            // call the saved handler directly so there is no altstack confusion
            f(signum)
        }
        SigHandler::SigAction(f) => {
            // call the saved handler directly so there is no altstack confusion
            f(signum, siginfo_ptr, ucontext_ptr)
        }
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// A collection of wrappers that will be upstreamed to the `nix` crate eventually.
////////////////////////////////////////////////////////////////////////////////////////////////////

use bitflags::bitflags;

#[derive(Copy, Clone)]
pub struct SigStack {
    stack: libc::stack_t,
}

impl SigStack {
    pub fn new(sp: *mut libc::c_void, flags: SigStackFlags, size: libc::size_t) -> SigStack {
        let mut stack = unsafe { std::mem::uninitialized::<libc::stack_t>() };
        stack.ss_sp = sp;
        stack.ss_flags = flags.bits();
        stack.ss_size = size;
        SigStack { stack }
    }

    pub fn disabled() -> SigStack {
        let mut stack = unsafe { std::mem::uninitialized::<libc::stack_t>() };
        stack.ss_sp = std::ptr::null_mut();
        stack.ss_flags = SigStackFlags::SS_DISABLE.bits();
        stack.ss_size = libc::SIGSTKSZ;
        SigStack { stack }
    }

    pub fn flags(&self) -> SigStackFlags {
        SigStackFlags::from_bits_truncate(self.stack.ss_flags)
    }
}

impl AsRef<libc::stack_t> for SigStack {
    fn as_ref(&self) -> &libc::stack_t {
        &self.stack
    }
}

impl AsMut<libc::stack_t> for SigStack {
    fn as_mut(&mut self) -> &mut libc::stack_t {
        &mut self.stack
    }
}

bitflags! {
    pub struct SigStackFlags: libc::c_int {
        const SS_ONSTACK = libc::SS_ONSTACK;
        const SS_DISABLE = libc::SS_DISABLE;
    }
}

pub unsafe fn sigaltstack(new_sigstack: Option<SigStack>) -> nix::Result<Option<SigStack>> {
    let mut previous_stack = std::mem::uninitialized::<libc::stack_t>();
    let disabled_sigstack = SigStack::disabled();
    let new_stack = match new_sigstack {
        None => &disabled_sigstack.stack,
        Some(ref new_stack) => &new_stack.stack,
    };
    let res = libc::sigaltstack(
        new_stack as *const libc::stack_t,
        &mut previous_stack as *mut libc::stack_t,
    );
    nix::errno::Errno::result(res).map(|_| {
        let sigstack = SigStack {
            stack: previous_stack,
        };
        if sigstack.flags().contains(SigStackFlags::SS_DISABLE) {
            None
        } else {
            Some(sigstack)
        }
    })
}

pub unsafe fn altstack_flags() -> nix::Result<SigStackFlags> {
    let mut current_stack = std::mem::uninitialized::<libc::stack_t>();
    let res = libc::sigaltstack(
        std::ptr::null_mut(),
        &mut current_stack as *mut libc::stack_t,
    );
    nix::errno::Errno::result(res)
        .map(|_| SigStackFlags::from_bits_truncate(current_stack.ss_flags))
}
<|endoftext|>


<commit_before>mod add;
mod split;

use crate::config::Config;
use crate::series::config::SeriesConfig;
use crate::series::SeriesData;
use crate::series::{LoadedSeries, SeriesPath};
use crate::tui::component::{Component, Draw};
use crate::tui::widget_util::{block, text};
use crate::tui::UIState;
use add::AddPanel;
use anime::local::{CategorizedEpisodes, SortedEpisodes};
use anime::remote::{Remote, RemoteService, SeriesInfo as RemoteInfo};
use anime::SeriesKind;
use anyhow::{anyhow, Context, Result};
use split::{SplitPanel, SplitResult};
use std::borrow::Cow;
use std::mem;
use std::path::PathBuf;
use std::thread;
use std::time::Duration;
use std::{fs, io};
use termion::event::Key;
use tui::backend::Backend;
use tui::layout::{Alignment, Constraint, Direction, Layout, Rect};
use tui::terminal::Frame;
use tui::widgets::Paragraph;

pub struct SplitSeriesPanel {
    state: PanelState,
}

impl SplitSeriesPanel {
    pub fn new() -> Self {
        Self {
            state: PanelState::Loading,
        }
    }

    fn draw_loading_panel<B>(rect: Rect, frame: &mut Frame<B>)
    where
        B: Backend,
    {
        let outline = block::with_borders("Split Series");
        frame.render_widget(outline, rect);

        let layout = Layout::default()
            .direction(Direction::Vertical)
            .constraints([Constraint::Percentage(50), Constraint::Percentage(50)].as_ref())
            .margin(1)
            .split(rect);

        let text = [text::bold("Loading..")];
        let widget = Paragraph::new(text.iter())
            .alignment(Alignment::Center)
            .wrap(false);
        frame.render_widget(widget, layout[1]);
    }
}

impl Component for SplitSeriesPanel {
    type State = UIState;
    type KeyResult = Result<SplitPanelResult>;

    fn tick(&mut self, state: &mut UIState) -> Result<()> {
        match &mut self.state {
            PanelState::Loading => {
                let series = match state.series.selected() {
                    Some(LoadedSeries::Complete(series)) => &series.data,
                    Some(LoadedSeries::Partial(data, _)) => data,
                    Some(LoadedSeries::None(_, _)) | None => {
                        return Err(anyhow!("cannot split a series with errors"))
                    }
                };

                let merged_series =
                    match MergedSeries::resolve(series, &state.remote, &state.config) {
                        Ok(merged) => merged,
                        Err(err) => return Err(err),
                    };

                self.state = PanelState::Splitting(SplitPanel::new(merged_series).into());
                Ok(())
            }
            PanelState::Splitting(split_panel) => split_panel.tick(state),
            PanelState::AddingSeries(add_panel, _) => add_panel.tick(state),
        }
    }

    fn process_key(&mut self, key: Key, state: &mut Self::State) -> Self::KeyResult {
        match &mut self.state {
            PanelState::Loading => Ok(SplitPanelResult::Ok),
            PanelState::Splitting(split_panel) => match split_panel.process_key(key, state) {
                Ok(SplitResult::Ok) => Ok(SplitPanelResult::Ok),
                Ok(SplitResult::Reset) => Ok(SplitPanelResult::Reset),
                Ok(SplitResult::AddSeries(info, path)) => {
                    let add_panel = AddPanel::new(info, path);
                    let split_panel = mem::take(split_panel);

                    self.state = PanelState::AddingSeries(add_panel.into(), split_panel);

                    Ok(SplitPanelResult::Ok)
                }
                Err(err) => Err(err),
            },
            PanelState::AddingSeries(add_panel, split_panel) => {
                match add_panel.process_key(key, state) {
                    Ok(result @ SplitPanelResult::Reset)
                    | Ok(result @ SplitPanelResult::AddSeries(_, _)) => {
                        let split_panel = mem::take(split_panel);
                        self.state = PanelState::Splitting(split_panel);

                        match result {
                            SplitPanelResult::Reset => Ok(SplitPanelResult::Ok),
                            other => Ok(other),
                        }
                    }
                    other => other,
                }
            }
        }
    }
}

impl<B> Draw<B> for SplitSeriesPanel
where
    B: Backend,
{
    type State = ();

    fn draw(&mut self, _: &Self::State, rect: Rect, frame: &mut Frame<B>) {
        match &mut self.state {
            PanelState::Loading => Self::draw_loading_panel(rect, frame),
            PanelState::Splitting(split_panel) => split_panel.draw(&(), rect, frame),
            PanelState::AddingSeries(add_panel, _) => add_panel.draw(&(), rect, frame),
        }
    }
}

enum PanelState {
    Loading,
    Splitting(Box<SplitPanel>),
    AddingSeries(Box<AddPanel>, Box<SplitPanel>),
}

pub enum SplitPanelResult {
    Ok,
    Reset,
    AddSeries(Box<RemoteInfo>, Box<SeriesConfig>),
}

impl SplitPanelResult {
    #[inline(always)]
    fn add_series(info: RemoteInfo, sconfig: SeriesConfig) -> Self {
        Self::AddSeries(Box::new(info), Box::new(sconfig))
    }
}

#[allow(variant_size_differences)]
enum MergedSeries {
    Resolved(Box<ResolvedSeries>),
    Failed(SeriesKind),
}

impl MergedSeries {
    #[inline(always)]
    fn resolved(resolved: ResolvedSeries) -> Self {
        Self::Resolved(Box::new(resolved))
    }

    fn resolve(data: &SeriesData, remote: &Remote, config: &Config) -> Result<Vec<Self>> {
        let episodes = CategorizedEpisodes::parse(
            data.config.path.absolute(config),
            &data.config.episode_parser,
        )?;

        let base_info = remote.search_info_by_id(data.info.id as u32)?;

        if base_info.sequels.is_empty() {
            return Ok(Vec::new());
        }

        let mut results = Vec::with_capacity(1);

        for (cat, eps) in episodes.iter() {
            let sequel = match base_info.sequel_by_kind(*cat) {
                Some(sequel) => sequel,
                None => continue,
            };

            // Seasons need special handling as they can have several merged together
            if let SeriesKind::Season = sequel.kind {
                Self::resolve_merged_season(
                    &base_info,
                    &data.config.path,
                    remote,
                    eps,
                    config,
                    &mut results,
                );

                continue;
            }

            thread::sleep(Duration::from_millis(250));

            let sequel_info = match remote.search_info_by_id(sequel.id) {
                Ok(info) => info,
                Err(_) => {
                    results.push(Self::Failed(sequel.kind));
                    continue;
                }
            };

            let resolved =
                ResolvedSeries::new(sequel_info, data.config.path.clone(), eps, 0, config);

            results.push(Self::resolved(resolved));
        }

        Ok(results)
    }

    fn resolve_merged_season(
        base_info: &RemoteInfo,
        base_path: &SeriesPath,
        remote: &Remote,
        episodes: &SortedEpisodes,
        config: &Config,
        results: &mut Vec<Self>,
    ) {
        let last_episode_num = episodes.last_episode_number();
        let mut info = Cow::Borrowed(base_info);

        // Exit early if we don't have enough episodes locally to have any merged seasons
        if info.episodes > last_episode_num {
            return;
        }

        let mut episode_offset = 0;

        while let Some(sequel) = info.direct_sequel() {
            info = match remote.search_info_by_id(sequel.id) {
                Ok(info) => info.into(),
                Err(_) => {
                    results.push(Self::Failed(sequel.kind));
                    continue;
                }
            };

            episode_offset += info.episodes;

            // Exit early if we don't have enough episodes locally to have another merged season
            if episode_offset > last_episode_num {
                break;
            }

            let resolved = ResolvedSeries::new(
                info.clone().into_owned(),
                base_path.clone(),
                episodes,
                episode_offset,
                config,
            );

            results.push(Self::resolved(resolved));

            // We don't need to sleep if there isn't another sequel
            if info.sequels.is_empty() {
                break;
            }

            thread::sleep(Duration::from_millis(250));
        }
    }

    fn split_all(merged: &[Self], config: &Config) -> Result<()> {
        for series in merged {
            let series = match series {
                Self::Resolved(series) => series,
                Self::Failed(_) => continue,
            };

            series
                .perform_split_actions(config)
                .context("performing split actions")?;
        }

        Ok(())
    }
}

pub type EpisodeOffset = u32;

struct ResolvedSeries {
    info: RemoteInfo,
    base_dir: SeriesPath,
    out_dir: SeriesPath,
    actions: Vec<SplitAction>,
}

impl ResolvedSeries {
    fn new(
        info: RemoteInfo,
        base_dir: SeriesPath,
        episodes: &SortedEpisodes,
        offset: EpisodeOffset,
        config: &Config,
    ) -> Self {
        let actions = SplitAction::from_merged_seasons(&info, episodes, offset);
        let out_dir = PathBuf::from(&info.title.preferred);
        let out_dir = SeriesPath::new(out_dir, config);

        Self {
            info,
            base_dir,
            out_dir,
            actions,
        }
    }

    fn perform_split_actions(&self, config: &Config) -> Result<()> {
        use std::os::unix::fs::symlink;

        if self.actions.is_empty() {
            return Ok(());
        }

        let base_dir = self.base_dir.absolute(config);

        if !base_dir.exists() {
            fs::create_dir_all(&base_dir).context("dir creation")?;
        }

        let out_dir = self.out_dir.absolute(config);

        if !out_dir.exists() {
            fs::create_dir_all(&out_dir).context("dir creation")?;
        }

        for action in &self.actions {
            let from_path = base_dir.join(&action.old_name);
            let to_path = out_dir.join(&action.new_name);

            if let Err(err) = symlink(&from_path, &to_path) {
                if err.kind() == io::ErrorKind::AlreadyExists {
                    continue;
                }

                return Err(anyhow!(
                    "failed to symlink files:\nfrom: {}\nto: {}\nreason: {}",
                    from_path.display(),
                    to_path.display(),
                    err
                ));
            }
        }

        Ok(())
    }
}

struct SplitAction {
    old_name: String,
    new_name: String,
}

impl SplitAction {
    fn new<S, N>(old_name: S, new_name: N) -> Self
    where
        S: Into<String>,
        N: Into<String>,
    {
        Self {
            old_name: old_name.into(),
            new_name: new_name.into(),
        }
    }

    fn from_merged_seasons(
        info: &RemoteInfo,
        episodes: &SortedEpisodes,
        offset: EpisodeOffset,
    ) -> Vec<Self> {
        let mut actions = Vec::new();

        let sequel_start = 1 + offset;
        let sequel_end = offset + info.episodes;

        for real_ep_num in sequel_start..=sequel_end {
            let episode = match episodes.find(real_ep_num) {
                Some(episode) => episode,
                None => continue,
            };

            let extension = PathBuf::from(&episode.filename)
                .extension()
                .map(|e| format!(".{}", e.to_string_lossy()).into())
                .unwrap_or_else(|| Cow::Borrowed(""));

            let new_filename = format!(
                "{} - {:02}{}",
                info.title.preferred,
                real_ep_num - offset,
                extension
            );

            let action = Self::new(&episode.filename, new_filename);

            actions.push(action);
        }

        actions
    }
}
<commit_msg>anup: Fix incorrect episode offset calculation when splitting a series<commit_after>mod add;
mod split;

use crate::config::Config;
use crate::series::config::SeriesConfig;
use crate::series::SeriesData;
use crate::series::{LoadedSeries, SeriesPath};
use crate::tui::component::{Component, Draw};
use crate::tui::widget_util::{block, text};
use crate::tui::UIState;
use add::AddPanel;
use anime::local::{CategorizedEpisodes, SortedEpisodes};
use anime::remote::{Remote, RemoteService, SeriesInfo as RemoteInfo};
use anime::SeriesKind;
use anyhow::{anyhow, Context, Result};
use split::{SplitPanel, SplitResult};
use std::borrow::Cow;
use std::mem;
use std::path::PathBuf;
use std::thread;
use std::time::Duration;
use std::{fs, io};
use termion::event::Key;
use tui::backend::Backend;
use tui::layout::{Alignment, Constraint, Direction, Layout, Rect};
use tui::terminal::Frame;
use tui::widgets::Paragraph;

pub struct SplitSeriesPanel {
    state: PanelState,
}

impl SplitSeriesPanel {
    pub fn new() -> Self {
        Self {
            state: PanelState::Loading,
        }
    }

    fn draw_loading_panel<B>(rect: Rect, frame: &mut Frame<B>)
    where
        B: Backend,
    {
        let outline = block::with_borders("Split Series");
        frame.render_widget(outline, rect);

        let layout = Layout::default()
            .direction(Direction::Vertical)
            .constraints([Constraint::Percentage(50), Constraint::Percentage(50)].as_ref())
            .margin(1)
            .split(rect);

        let text = [text::bold("Loading..")];
        let widget = Paragraph::new(text.iter())
            .alignment(Alignment::Center)
            .wrap(false);
        frame.render_widget(widget, layout[1]);
    }
}

impl Component for SplitSeriesPanel {
    type State = UIState;
    type KeyResult = Result<SplitPanelResult>;

    fn tick(&mut self, state: &mut UIState) -> Result<()> {
        match &mut self.state {
            PanelState::Loading => {
                let series = match state.series.selected() {
                    Some(LoadedSeries::Complete(series)) => &series.data,
                    Some(LoadedSeries::Partial(data, _)) => data,
                    Some(LoadedSeries::None(_, _)) | None => {
                        return Err(anyhow!("cannot split a series with errors"))
                    }
                };

                let merged_series =
                    match MergedSeries::resolve(series, &state.remote, &state.config) {
                        Ok(merged) => merged,
                        Err(err) => return Err(err),
                    };

                self.state = PanelState::Splitting(SplitPanel::new(merged_series).into());
                Ok(())
            }
            PanelState::Splitting(split_panel) => split_panel.tick(state),
            PanelState::AddingSeries(add_panel, _) => add_panel.tick(state),
        }
    }

    fn process_key(&mut self, key: Key, state: &mut Self::State) -> Self::KeyResult {
        match &mut self.state {
            PanelState::Loading => Ok(SplitPanelResult::Ok),
            PanelState::Splitting(split_panel) => match split_panel.process_key(key, state) {
                Ok(SplitResult::Ok) => Ok(SplitPanelResult::Ok),
                Ok(SplitResult::Reset) => Ok(SplitPanelResult::Reset),
                Ok(SplitResult::AddSeries(info, path)) => {
                    let add_panel = AddPanel::new(info, path);
                    let split_panel = mem::take(split_panel);

                    self.state = PanelState::AddingSeries(add_panel.into(), split_panel);

                    Ok(SplitPanelResult::Ok)
                }
                Err(err) => Err(err),
            },
            PanelState::AddingSeries(add_panel, split_panel) => {
                match add_panel.process_key(key, state) {
                    Ok(result @ SplitPanelResult::Reset)
                    | Ok(result @ SplitPanelResult::AddSeries(_, _)) => {
                        let split_panel = mem::take(split_panel);
                        self.state = PanelState::Splitting(split_panel);

                        match result {
                            SplitPanelResult::Reset => Ok(SplitPanelResult::Ok),
                            other => Ok(other),
                        }
                    }
                    other => other,
                }
            }
        }
    }
}

impl<B> Draw<B> for SplitSeriesPanel
where
    B: Backend,
{
    type State = ();

    fn draw(&mut self, _: &Self::State, rect: Rect, frame: &mut Frame<B>) {
        match &mut self.state {
            PanelState::Loading => Self::draw_loading_panel(rect, frame),
            PanelState::Splitting(split_panel) => split_panel.draw(&(), rect, frame),
            PanelState::AddingSeries(add_panel, _) => add_panel.draw(&(), rect, frame),
        }
    }
}

enum PanelState {
    Loading,
    Splitting(Box<SplitPanel>),
    AddingSeries(Box<AddPanel>, Box<SplitPanel>),
}

pub enum SplitPanelResult {
    Ok,
    Reset,
    AddSeries(Box<RemoteInfo>, Box<SeriesConfig>),
}

impl SplitPanelResult {
    #[inline(always)]
    fn add_series(info: RemoteInfo, sconfig: SeriesConfig) -> Self {
        Self::AddSeries(Box::new(info), Box::new(sconfig))
    }
}

#[allow(variant_size_differences)]
enum MergedSeries {
    Resolved(Box<ResolvedSeries>),
    Failed(SeriesKind),
}

impl MergedSeries {
    #[inline(always)]
    fn resolved(resolved: ResolvedSeries) -> Self {
        Self::Resolved(Box::new(resolved))
    }

    fn resolve(data: &SeriesData, remote: &Remote, config: &Config) -> Result<Vec<Self>> {
        let episodes = CategorizedEpisodes::parse(
            data.config.path.absolute(config),
            &data.config.episode_parser,
        )?;

        let base_info = remote.search_info_by_id(data.info.id as u32)?;

        if base_info.sequels.is_empty() {
            return Ok(Vec::new());
        }

        let mut results = Vec::with_capacity(1);

        for (cat, eps) in episodes.iter() {
            let sequel = match base_info.sequel_by_kind(*cat) {
                Some(sequel) => sequel,
                None => continue,
            };

            // Seasons need special handling as they can have several merged together
            if let SeriesKind::Season = sequel.kind {
                Self::resolve_merged_season(
                    &base_info,
                    &data.config.path,
                    remote,
                    eps,
                    config,
                    &mut results,
                );

                continue;
            }

            thread::sleep(Duration::from_millis(250));

            let sequel_info = match remote.search_info_by_id(sequel.id) {
                Ok(info) => info,
                Err(_) => {
                    results.push(Self::Failed(sequel.kind));
                    continue;
                }
            };

            let resolved =
                ResolvedSeries::new(sequel_info, data.config.path.clone(), eps, 0, config);

            results.push(Self::resolved(resolved));
        }

        Ok(results)
    }

    fn resolve_merged_season(
        base_info: &RemoteInfo,
        base_path: &SeriesPath,
        remote: &Remote,
        episodes: &SortedEpisodes,
        config: &Config,
        results: &mut Vec<Self>,
    ) {
        let last_episode_num = episodes.last_episode_number();
        let mut info = Cow::Borrowed(base_info);

        // Exit early if we don't have enough episodes locally to have any merged seasons
        if info.episodes > last_episode_num {
            return;
        }

        let mut episode_offset = info.episodes;

        while let Some(sequel) = info.direct_sequel() {
            info = match remote.search_info_by_id(sequel.id) {
                Ok(info) => info.into(),
                Err(_) => {
                    results.push(Self::Failed(sequel.kind));
                    continue;
                }
            };

            let resolved = ResolvedSeries::new(
                info.clone().into_owned(),
                base_path.clone(),
                episodes,
                episode_offset,
                config,
            );

            results.push(Self::resolved(resolved));

            episode_offset += info.episodes;

            // We can stop if we don't have anymore sequels or if we don't have enough episodes locally to have another merged season
            if episode_offset > last_episode_num || info.direct_sequel().is_none() {
                break;
            }

            thread::sleep(Duration::from_millis(250));
        }
    }

    fn split_all(merged: &[Self], config: &Config) -> Result<()> {
        for series in merged {
            let series = match series {
                Self::Resolved(series) => series,
                Self::Failed(_) => continue,
            };

            series
                .perform_split_actions(config)
                .context("performing split actions")?;
        }

        Ok(())
    }
}

pub type EpisodeOffset = u32;

struct ResolvedSeries {
    info: RemoteInfo,
    base_dir: SeriesPath,
    out_dir: SeriesPath,
    actions: Vec<SplitAction>,
}

impl ResolvedSeries {
    fn new(
        info: RemoteInfo,
        base_dir: SeriesPath,
        episodes: &SortedEpisodes,
        offset: EpisodeOffset,
        config: &Config,
    ) -> Self {
        let actions = SplitAction::from_merged_seasons(&info, episodes, offset);
        let out_dir = PathBuf::from(&info.title.preferred);
        let out_dir = SeriesPath::new(out_dir, config);

        Self {
            info,
            base_dir,
            out_dir,
            actions,
        }
    }

    fn perform_split_actions(&self, config: &Config) -> Result<()> {
        use std::os::unix::fs::symlink;

        if self.actions.is_empty() {
            return Ok(());
        }

        let base_dir = self.base_dir.absolute(config);

        if !base_dir.exists() {
            fs::create_dir_all(&base_dir).context("dir creation")?;
        }

        let out_dir = self.out_dir.absolute(config);

        if !out_dir.exists() {
            fs::create_dir_all(&out_dir).context("dir creation")?;
        }

        for action in &self.actions {
            let from_path = base_dir.join(&action.old_name);
            let to_path = out_dir.join(&action.new_name);

            if let Err(err) = symlink(&from_path, &to_path) {
                if err.kind() == io::ErrorKind::AlreadyExists {
                    continue;
                }

                return Err(anyhow!(
                    "failed to symlink files:\nfrom: {}\nto: {}\nreason: {}",
                    from_path.display(),
                    to_path.display(),
                    err
                ));
            }
        }

        Ok(())
    }
}

struct SplitAction {
    old_name: String,
    new_name: String,
}

impl SplitAction {
    fn new<S, N>(old_name: S, new_name: N) -> Self
    where
        S: Into<String>,
        N: Into<String>,
    {
        Self {
            old_name: old_name.into(),
            new_name: new_name.into(),
        }
    }

    fn from_merged_seasons(
        info: &RemoteInfo,
        episodes: &SortedEpisodes,
        offset: EpisodeOffset,
    ) -> Vec<Self> {
        let mut actions = Vec::new();

        let sequel_start = 1 + offset;
        let sequel_end = offset + info.episodes;

        for real_ep_num in sequel_start..=sequel_end {
            let episode = match episodes.find(real_ep_num) {
                Some(episode) => episode,
                None => continue,
            };

            let extension = PathBuf::from(&episode.filename)
                .extension()
                .map(|e| format!(".{}", e.to_string_lossy()).into())
                .unwrap_or_else(|| Cow::Borrowed(""));

            let new_filename = format!(
                "{} - {:02}{}",
                info.title.preferred,
                real_ep_num - offset,
                extension
            );

            let action = Self::new(&episode.filename, new_filename);

            actions.push(action);
        }

        actions
    }
}
<|endoftext|>


<commit_before>extern crate nalgebra as na;
extern crate ncollide3d;
extern crate nphysics3d;
extern crate nphysics_testbed3d;

use std::f32::consts::PI;
use na::{Isometry3, Point3, Unit, Vector3};
use ncollide3d::shape::{Cuboid, ShapeHandle};
use nphysics3d::world::World;
use nphysics3d::object::{BodyHandle, Material};
use nphysics3d::joint::{BallConstraint, PinSlotConstraint, PrismaticConstraint, RevoluteConstraint};
use nphysics3d::volumetric::Volumetric;
use nphysics_testbed3d::Testbed;

const COLLIDER_MARGIN: f32 = 0.01;

fn main() {
    /*
     * World
     */
    let mut world = World::new();
    world.set_gravity(Vector3::new(0.0, -9.81, 0.0));

    // Material.
    let material = Material::default();

    /*
     * Ground
     */
    let ground_size = 50.0;
    let ground_shape =
        ShapeHandle::new(Cuboid::new(Vector3::repeat(ground_size - COLLIDER_MARGIN)));
    let ground_pos = Isometry3::new(Vector3::y() * (-ground_size - 5.0), na::zero());
    // let ground_shape = ShapeHandle::new(Plane::new(Vector3::y_axis()));
    // let ground_pos = Isometry3::identity();

    world.add_collider(
        COLLIDER_MARGIN,
        ground_shape,
        BodyHandle::ground(),
        ground_pos,
        material.clone(),
    );

    /*
     * Geometries that will be re-used for several multibody links..
     */
    let rad = 0.2;
    let cuboid = ShapeHandle::new(Cuboid::new(Vector3::repeat(rad)));
    let cuboid_inertia = cuboid.inertia(1.0);
    let cuboid_center_of_mass = cuboid.center_of_mass();

    /*
     * Revolute joints.
     */
    let num = 6;
    let mut parent = BodyHandle::ground();
    let first_anchor = Point3::new(0.0, 5.0, 11.0);
    let mut pos = first_anchor.coords;

    for i in 0usize..num {
        let body_anchor = Point3::new(0.0, 0.0, 1.0) * (rad * 3.0 + 0.2);
        let parent_anchor = if i == 0 {
            first_anchor
        } else {
            Point3::origin()
        };

        pos -= body_anchor.coords;

        let rb = world.add_rigid_body(
            Isometry3::new(pos, na::zero()),
            cuboid_inertia,
            cuboid_center_of_mass,
        );

        world.add_collider(
            COLLIDER_MARGIN,
            cuboid.clone(),
            rb,
            Isometry3::identity(),
            material.clone(),
        );

        let constraint = RevoluteConstraint::new(
            parent,
            rb,
            parent_anchor,
            Vector3::x_axis(),
            body_anchor,
            Vector3::x_axis(),
        );

        world.add_constraint(constraint);

        parent = rb;
    }

    /*
     * Prismatic joint.
     */
    let first_anchor = Point3::new(0.0, 5.0, 5.0);
    let mut pos = first_anchor.coords;
    parent = BodyHandle::ground();

    for i in 0usize..2 {
        let mut body_anchor = Point3::origin();
        let mut parent_anchor = Point3::origin();
        if i == 0 {
            parent_anchor = first_anchor;
        } else {
            body_anchor = Point3::new(0.0, 0.0, 1.0) * (rad * 3.0);
        }

        pos -= body_anchor.coords;

        let rb = world.add_rigid_body(
            Isometry3::new(pos, na::zero()),
            cuboid_inertia,
            cuboid_center_of_mass,
        );

        world.add_collider(
            COLLIDER_MARGIN,
            cuboid.clone(),
            rb,
            Isometry3::identity(),
            material.clone(),
        );

        let constraint = PrismaticConstraint::new(
            parent,
            rb,
            parent_anchor,
            Vector3::y_axis(),
            body_anchor,
            Vector3::y_axis(),
        );

        world.add_constraint(constraint);

        parent = rb;
    }

    /*
     * Ball joint.
     */
    let first_anchor = Point3::new(0.0, 5.0, 0.0);
    let mut pos = first_anchor.coords;
    parent = BodyHandle::ground();

    for i in 0usize..num {
        let angle = i as f32 * 2.0 * PI / (num as f32);
        let mut body_anchor = Point3::origin();
        let mut parent_anchor = Point3::origin();
        if i == 0 {
            parent_anchor = first_anchor;
        } else {
            body_anchor = Point3::new(angle.cos(), 0.3, angle.sin()) * (rad * 5.0);
        }

        pos -= body_anchor.coords;

        let rb = world.add_rigid_body(
            Isometry3::new(pos, na::zero()),
            cuboid_inertia,
            cuboid_center_of_mass,
        );

        world.add_collider(
            COLLIDER_MARGIN,
            cuboid.clone(),
            rb,
            Isometry3::identity(),
            material.clone(),
        );

        let constraint = BallConstraint::new(parent, rb, parent_anchor, body_anchor);

        world.add_constraint(constraint);

        parent = rb;
    }

    /*
    /*
     * Ball joint.
     */
    parent = BodyHandle::ground();
    for i in 0usize..num {
        // The multibody links are initialized along a circle.
        let angle = i as f32 * 2.0 * PI / (num as f32);
        let shift = rad * 5.0;
        let mut parent_shift = Vector3::zeros();
        let mut body_shift = Vector3::new(angle.cos(), 0.3, angle.sin()) * shift;

        if i == 0 {
            parent_shift = Vector3::new(0.0, 5.0, 0.0);
        }

        let spherical = BallJoint::new(na::zero());
        parent = world.add_multibody_link(
            parent,
            spherical,
            parent_shift,
            body_shift,
            cuboid_inertia,
            cuboid_center_of_mass,
        );
        world.add_collider(
            COLLIDER_MARGIN,
            cuboid.clone(),
            parent,
            Isometry3::identity(),
            material.clone(),
        );
    }
    // Setup damping for the whole multibody.
    world.multibody_mut(parent).unwrap().damping_mut().fill(0.1);

    /*
     * Universal joint.
     */
    let axis1 = Vector3::x_axis();
    let axis2 = Vector3::z_axis();
    let fixed = FixedJoint::new(Isometry3::identity());
    let mut uni = UniversalJoint::new(axis1, axis2, 0.0, 0.0);
    uni.enable_angular_motor_2();
    uni.set_desired_angular_motor_velocity_2(5.0);

    let parent_shift = Vector3::new(0.0, 3.0, -5.0);
    let body_shift = -Vector3::z();

    parent = world.add_multibody_link(
        BodyHandle::ground(),
        fixed,
        parent_shift,
        na::zero(),
        cuboid_inertia,
        cuboid_center_of_mass,
    );
    world.add_collider(
        COLLIDER_MARGIN,
        cuboid.clone(),
        parent,
        Isometry3::identity(),
        material.clone(),
    );

    parent = world.add_multibody_link(
        parent,
        uni,
        na::zero(),
        body_shift,
        cuboid_inertia,
        cuboid_center_of_mass,
    );
    world.add_collider(
        COLLIDER_MARGIN,
        cuboid.clone(),
        parent,
        Isometry3::identity(),
        material.clone(),
    );

    /*
     * Helical joint.
     */
    let cuboid = ShapeHandle::new(Cuboid::new(Vector3::repeat(rad)));
    let cuboid_inertia = cuboid.inertia(1.0);
    let axis = Vector3::y_axis();

    let mut hel = HelicalJoint::new(axis, 1.0, 0.0);
    hel.set_desired_angular_motor_velocity(4.0);

    let parent_shift = Vector3::new(0.0, -2.0, 10.0);

    let hel_handle = world.add_multibody_link(
        BodyHandle::ground(),
        hel,
        parent_shift,
        na::zero(),
        cuboid_inertia,
        cuboid_center_of_mass,
    );
    world.add_collider(
        COLLIDER_MARGIN,
        cuboid.clone(),
        hel_handle,
        Isometry3::identity(),
        material.clone(),
    );

    /*
     * Planar joint.
     */
    let axis1 = Vector3::z_axis();
    let axis2 = Vector3::y_axis();
    let shift = Vector3::new(0.0, -2.0, 5.0);
    let width = 5.0 * rad * 4.0;
    for i in 0..5 {
        for j in 0..5 {
            let mut x = i as f32 * rad * 4.0 - width / 2.0;
            let y = j as f32 * rad * 4.0 - width / 2.0;

            if j % 2 == 0 {
                x += rad * 2.0;
            }

            let mut planar = PlanarJoint::new(axis1, axis2, x, y, 0.0);
            planar.enable_min_offset_1(-width / 2.0);
            planar.enable_max_offset_1(width / 2.0);
            planar.enable_min_offset_2(-5.0);
            let handle = world.add_multibody_link(
                BodyHandle::ground(),
                planar,
                shift,
                na::zero(),
                cuboid_inertia,
                cuboid_center_of_mass,
            );
            world.add_collider(
                COLLIDER_MARGIN,
                cuboid.clone(),
                handle,
                Isometry3::identity(),
                material.clone(),
            );
        }
    }

    /*
     * Rectangular joint.
     */
    let axis1 = Vector3::z_axis();
    let axis2 = Vector3::y_axis();
    let shift = Vector3::new(0.0, -2.0, 0.0);
    let width = 5.0 * rad * 4.0;
    for i in 0..5 {
        for j in 0..5 {
            let mut x = i as f32 * rad * 4.0 - width / 2.0;
            let y = j as f32 * rad * 4.0 - width / 2.0;

            if j % 2 == 0 {
                x += rad * 2.0;
            }

            let mut rect = RectangularJoint::new(axis1, axis2, x, y);
            rect.enable_min_offset_1(-width / 2.0);
            rect.enable_max_offset_1(width / 2.0);
            rect.enable_min_offset_2(-5.0);
            let handle = world.add_multibody_link(
                BodyHandle::ground(),
                rect,
                shift,
                na::zero(),
                cuboid_inertia,
                cuboid_center_of_mass,
            );
            world.add_collider(
                COLLIDER_MARGIN,
                cuboid.clone(),
                handle,
                Isometry3::identity(),
                material.clone(),
            );
        }
    }*/

    /*
     * Pin-slot joint.
     */
    let cuboid = ShapeHandle::new(Cuboid::new(Vector3::new(rad * 5.0, rad, rad * 5.0)));
    let cuboid_inertia = cuboid.inertia(1.0);
    let cuboid_center_of_mass = cuboid.center_of_mass();
    let rb = world.add_rigid_body(Isometry3::identity(), cuboid_inertia, cuboid_center_of_mass);

    world.add_collider(
        COLLIDER_MARGIN,
        cuboid.clone(),
        rb,
        Isometry3::identity(),
        material.clone(),
    );

    let constraint = PinSlotConstraint::new(
        BodyHandle::ground(),
        rb,
        Point3::origin(),
        Vector3::y_axis(),
        Vector3::x_axis(),
        Point3::origin(),
        Vector3::y_axis(),
        Vector3::x_axis(),
    );

    world.add_constraint(constraint);

    /*
     * Set up the testbed.
     */
    let mut testbed = Testbed::new(world);
    /*
    testbed.add_callback(move |world, _| {
        /*
         * Activate the helical joint motor if it is to low.
         */
        // Might be None if the user interactively deleted the helical body.
        if let Some(mut helical) = world.multibody_link_mut(hel_handle) {
            let dof = helical
                .joint_mut()
                .downcast_mut::<HelicalJoint<f32>>()
                .unwrap();

            if dof.offset() < -5.0 {
                dof.enable_angular_motor();
            } else if dof.offset() > 0.0 {
                dof.disable_angular_motor();
            }
        }
    });

    testbed.add_callback(move |world, _| {
        /*
         * Activate the pin-slot joint linear motor if it is to low.
         */
        // Might be None if the user interactively deleted the pin-slot body.
        if let Some(mut pin_slot) = world.multibody_link_mut(pin_handle) {
            let dof = pin_slot
                .joint_mut()
                .downcast_mut::<PinSlotJoint<f32>>()
                .unwrap();

            if dof.offset() < -10.0 {
                dof.enable_linear_motor();
            } else if dof.offset() > -4.0 {
                dof.disable_linear_motor();
            }
        }
    });
    */

    testbed.look_at(Point3::new(30.0, -2.0, 0.0), Point3::new(0.0, -2.0, 0.0));
    testbed.run();
}
<commit_msg>Minor fix of demo comments.<commit_after>extern crate nalgebra as na;
extern crate ncollide3d;
extern crate nphysics3d;
extern crate nphysics_testbed3d;

use std::f32::consts::PI;
use na::{Isometry3, Point3, Unit, Vector3};
use ncollide3d::shape::{Cuboid, ShapeHandle};
use nphysics3d::world::World;
use nphysics3d::object::{BodyHandle, Material};
use nphysics3d::joint::{BallConstraint, PinSlotConstraint, PrismaticConstraint, RevoluteConstraint};
use nphysics3d::volumetric::Volumetric;
use nphysics_testbed3d::Testbed;

const COLLIDER_MARGIN: f32 = 0.01;

fn main() {
    /*
     * World
     */
    let mut world = World::new();
    world.set_gravity(Vector3::new(0.0, -9.81, 0.0));

    // Material.
    let material = Material::default();

    /*
     * Ground
     */
    let ground_size = 50.0;
    let ground_shape =
        ShapeHandle::new(Cuboid::new(Vector3::repeat(ground_size - COLLIDER_MARGIN)));
    let ground_pos = Isometry3::new(Vector3::y() * (-ground_size - 5.0), na::zero());
    // let ground_shape = ShapeHandle::new(Plane::new(Vector3::y_axis()));
    // let ground_pos = Isometry3::identity();

    world.add_collider(
        COLLIDER_MARGIN,
        ground_shape,
        BodyHandle::ground(),
        ground_pos,
        material.clone(),
    );

    /*
     * Geometries that will be re-used for several multibody links..
     */
    let rad = 0.2;
    let cuboid = ShapeHandle::new(Cuboid::new(Vector3::repeat(rad)));
    let cuboid_inertia = cuboid.inertia(1.0);
    let cuboid_center_of_mass = cuboid.center_of_mass();

    /*
     * Revolute joints.
     */
    let num = 6;
    let mut parent = BodyHandle::ground();
    let first_anchor = Point3::new(0.0, 5.0, 11.0);
    let mut pos = first_anchor.coords;

    for i in 0usize..num {
        let body_anchor = Point3::new(0.0, 0.0, 1.0) * (rad * 3.0 + 0.2);
        let parent_anchor = if i == 0 {
            first_anchor
        } else {
            Point3::origin()
        };

        pos -= body_anchor.coords;

        let rb = world.add_rigid_body(
            Isometry3::new(pos, na::zero()),
            cuboid_inertia,
            cuboid_center_of_mass,
        );

        world.add_collider(
            COLLIDER_MARGIN,
            cuboid.clone(),
            rb,
            Isometry3::identity(),
            material.clone(),
        );

        let constraint = RevoluteConstraint::new(
            parent,
            rb,
            parent_anchor,
            Vector3::x_axis(),
            body_anchor,
            Vector3::x_axis(),
        );

        world.add_constraint(constraint);

        parent = rb;
    }

    /*
     * Prismatic constraint.
     */
    let first_anchor = Point3::new(0.0, 5.0, 5.0);
    let mut pos = first_anchor.coords;
    parent = BodyHandle::ground();

    for i in 0usize..2 {
        let mut body_anchor = Point3::origin();
        let mut parent_anchor = Point3::origin();
        if i == 0 {
            parent_anchor = first_anchor;
        } else {
            body_anchor = Point3::new(0.0, 0.0, 1.0) * (rad * 3.0);
        }

        pos -= body_anchor.coords;

        let rb = world.add_rigid_body(
            Isometry3::new(pos, na::zero()),
            cuboid_inertia,
            cuboid_center_of_mass,
        );

        world.add_collider(
            COLLIDER_MARGIN,
            cuboid.clone(),
            rb,
            Isometry3::identity(),
            material.clone(),
        );

        let constraint = PrismaticConstraint::new(
            parent,
            rb,
            parent_anchor,
            Vector3::y_axis(),
            body_anchor,
            Vector3::y_axis(),
        );

        world.add_constraint(constraint);

        parent = rb;
    }

    /*
     * Ball constraint.
     */
    let first_anchor = Point3::new(0.0, 5.0, 0.0);
    let mut pos = first_anchor.coords;
    parent = BodyHandle::ground();

    for i in 0usize..num {
        let angle = i as f32 * 2.0 * PI / (num as f32);
        let mut body_anchor = Point3::origin();
        let mut parent_anchor = Point3::origin();
        if i == 0 {
            parent_anchor = first_anchor;
        } else {
            body_anchor = Point3::new(angle.cos(), 0.3, angle.sin()) * (rad * 5.0);
        }

        pos -= body_anchor.coords;

        let rb = world.add_rigid_body(
            Isometry3::new(pos, na::zero()),
            cuboid_inertia,
            cuboid_center_of_mass,
        );

        world.add_collider(
            COLLIDER_MARGIN,
            cuboid.clone(),
            rb,
            Isometry3::identity(),
            material.clone(),
        );

        let constraint = BallConstraint::new(parent, rb, parent_anchor, body_anchor);

        world.add_constraint(constraint);

        parent = rb;
    }

    /*
    /*
     * Ball constraint.
     */
    parent = BodyHandle::ground();
    for i in 0usize..num {
        // The multibody links are initialized along a circle.
        let angle = i as f32 * 2.0 * PI / (num as f32);
        let shift = rad * 5.0;
        let mut parent_shift = Vector3::zeros();
        let mut body_shift = Vector3::new(angle.cos(), 0.3, angle.sin()) * shift;

        if i == 0 {
            parent_shift = Vector3::new(0.0, 5.0, 0.0);
        }

        let spherical = BallJoint::new(na::zero());
        parent = world.add_multibody_link(
            parent,
            spherical,
            parent_shift,
            body_shift,
            cuboid_inertia,
            cuboid_center_of_mass,
        );
        world.add_collider(
            COLLIDER_MARGIN,
            cuboid.clone(),
            parent,
            Isometry3::identity(),
            material.clone(),
        );
    }
    // Setup damping for the whole multibody.
    world.multibody_mut(parent).unwrap().damping_mut().fill(0.1);

    /*
     * Universal constraint.
     */
    let axis1 = Vector3::x_axis();
    let axis2 = Vector3::z_axis();
    let fixed = FixedJoint::new(Isometry3::identity());
    let mut uni = UniversalJoint::new(axis1, axis2, 0.0, 0.0);
    uni.enable_angular_motor_2();
    uni.set_desired_angular_motor_velocity_2(5.0);

    let parent_shift = Vector3::new(0.0, 3.0, -5.0);
    let body_shift = -Vector3::z();

    parent = world.add_multibody_link(
        BodyHandle::ground(),
        fixed,
        parent_shift,
        na::zero(),
        cuboid_inertia,
        cuboid_center_of_mass,
    );
    world.add_collider(
        COLLIDER_MARGIN,
        cuboid.clone(),
        parent,
        Isometry3::identity(),
        material.clone(),
    );

    parent = world.add_multibody_link(
        parent,
        uni,
        na::zero(),
        body_shift,
        cuboid_inertia,
        cuboid_center_of_mass,
    );
    world.add_collider(
        COLLIDER_MARGIN,
        cuboid.clone(),
        parent,
        Isometry3::identity(),
        material.clone(),
    );

    /*
     * Helical constraint.
     */
    let cuboid = ShapeHandle::new(Cuboid::new(Vector3::repeat(rad)));
    let cuboid_inertia = cuboid.inertia(1.0);
    let axis = Vector3::y_axis();

    let mut hel = HelicalJoint::new(axis, 1.0, 0.0);
    hel.set_desired_angular_motor_velocity(4.0);

    let parent_shift = Vector3::new(0.0, -2.0, 10.0);

    let hel_handle = world.add_multibody_link(
        BodyHandle::ground(),
        hel,
        parent_shift,
        na::zero(),
        cuboid_inertia,
        cuboid_center_of_mass,
    );
    world.add_collider(
        COLLIDER_MARGIN,
        cuboid.clone(),
        hel_handle,
        Isometry3::identity(),
        material.clone(),
    );

    /*
     * Planar constraint.
     */
    let axis1 = Vector3::z_axis();
    let axis2 = Vector3::y_axis();
    let shift = Vector3::new(0.0, -2.0, 5.0);
    let width = 5.0 * rad * 4.0;
    for i in 0..5 {
        for j in 0..5 {
            let mut x = i as f32 * rad * 4.0 - width / 2.0;
            let y = j as f32 * rad * 4.0 - width / 2.0;

            if j % 2 == 0 {
                x += rad * 2.0;
            }

            let mut planar = PlanarJoint::new(axis1, axis2, x, y, 0.0);
            planar.enable_min_offset_1(-width / 2.0);
            planar.enable_max_offset_1(width / 2.0);
            planar.enable_min_offset_2(-5.0);
            let handle = world.add_multibody_link(
                BodyHandle::ground(),
                planar,
                shift,
                na::zero(),
                cuboid_inertia,
                cuboid_center_of_mass,
            );
            world.add_collider(
                COLLIDER_MARGIN,
                cuboid.clone(),
                handle,
                Isometry3::identity(),
                material.clone(),
            );
        }
    }

    /*
     * Rectangular constraint.
     */
    let axis1 = Vector3::z_axis();
    let axis2 = Vector3::y_axis();
    let shift = Vector3::new(0.0, -2.0, 0.0);
    let width = 5.0 * rad * 4.0;
    for i in 0..5 {
        for j in 0..5 {
            let mut x = i as f32 * rad * 4.0 - width / 2.0;
            let y = j as f32 * rad * 4.0 - width / 2.0;

            if j % 2 == 0 {
                x += rad * 2.0;
            }

            let mut rect = RectangularJoint::new(axis1, axis2, x, y);
            rect.enable_min_offset_1(-width / 2.0);
            rect.enable_max_offset_1(width / 2.0);
            rect.enable_min_offset_2(-5.0);
            let handle = world.add_multibody_link(
                BodyHandle::ground(),
                rect,
                shift,
                na::zero(),
                cuboid_inertia,
                cuboid_center_of_mass,
            );
            world.add_collider(
                COLLIDER_MARGIN,
                cuboid.clone(),
                handle,
                Isometry3::identity(),
                material.clone(),
            );
        }
    }*/

    /*
     * Pin-slot constraint.
     */
    let cuboid = ShapeHandle::new(Cuboid::new(Vector3::new(rad * 5.0, rad, rad * 5.0)));
    let cuboid_inertia = cuboid.inertia(1.0);
    let cuboid_center_of_mass = cuboid.center_of_mass();
    let rb = world.add_rigid_body(Isometry3::identity(), cuboid_inertia, cuboid_center_of_mass);

    world.add_collider(
        COLLIDER_MARGIN,
        cuboid.clone(),
        rb,
        Isometry3::identity(),
        material.clone(),
    );

    let constraint = PinSlotConstraint::new(
        BodyHandle::ground(),
        rb,
        Point3::origin(),
        Vector3::y_axis(),
        Vector3::x_axis(),
        Point3::origin(),
        Vector3::y_axis(),
        Vector3::x_axis(),
    );

    world.add_constraint(constraint);

    /*
     * Set up the testbed.
     */
    let mut testbed = Testbed::new(world);
    /*
    testbed.add_callback(move |world, _| {
        /*
         * Activate the helical constraint motor if it is to low.
         */
        // Might be None if the user interactively deleted the helical body.
        if let Some(mut helical) = world.multibody_link_mut(hel_handle) {
            let dof = helical
                .joint_mut()
                .downcast_mut::<HelicalJoint<f32>>()
                .unwrap();

            if dof.offset() < -5.0 {
                dof.enable_angular_motor();
            } else if dof.offset() > 0.0 {
                dof.disable_angular_motor();
            }
        }
    });

    testbed.add_callback(move |world, _| {
        /*
         * Activate the pin-slot constraint linear motor if it is to low.
         */
        // Might be None if the user interactively deleted the pin-slot body.
        if let Some(mut pin_slot) = world.multibody_link_mut(pin_handle) {
            let dof = pin_slot
                .joint_mut()
                .downcast_mut::<PinSlotJoint<f32>>()
                .unwrap();

            if dof.offset() < -10.0 {
                dof.enable_linear_motor();
            } else if dof.offset() > -4.0 {
                dof.disable_linear_motor();
            }
        }
    });
    */

    testbed.look_at(Point3::new(30.0, -2.0, 0.0), Point3::new(0.0, -2.0, 0.0));
    testbed.run();
}
<|endoftext|>