<commit_before>extern crate libc;
extern crate regex;

extern crate sys_util;

mod cgroup;
mod env;

use std::ffi::OsStr;
use std::fs::{canonicalize, metadata};
use std::io;
use std::os::unix::io::AsRawFd;
use std::os::unix::net::UnixListener;
use std::path::PathBuf;
use std::result;

use env::Env;

pub const KVM_FD: i32 = 3;
pub const DEV_NET_TUN_FD: i32 = 4;
pub const LISTENER_FD: i32 = 5;

const SOCKET_FILE_NAME: &str = "api.socket";

#[derive(Debug)]
pub enum Error {
    Canonicalize(PathBuf, io::Error),
    CgroupLineNotFound(&'static str, &'static str),
    CgroupLineNotUnique(&'static str, &'static str),
    Chroot(i32),
    Copy(PathBuf, PathBuf, io::Error),
    CreateDir(PathBuf, io::Error),
    Exec(io::Error),
    FileCreate(PathBuf, io::Error),
    FileName(PathBuf),
    FileOpen(PathBuf, io::Error),
    Gid(String),
    Metadata(PathBuf, io::Error),
    NotAFile(PathBuf),
    NotAFolder(PathBuf),
    NotAlphanumeric(String),
    OpenDevKvm(sys_util::Error),
    OpenDevNetTun(sys_util::Error),
    ReadLine(PathBuf, io::Error),
    RegEx(regex::Error),
    Uid(String),
    UnexpectedKvmFd(i32),
    UnexpectedDevNetTunFd(i32),
    UnexpectedListenerFd(i32),
    UnixListener(io::Error),
    UnsetCloexec(sys_util::Error),
    Write(PathBuf, io::Error),
}

pub type Result<T> = result::Result<T, Error>;

pub struct JailerArgs<'a> {
    id: &'a str,
    exec_file_path: PathBuf,
    chroot_base_dir: PathBuf,
    uid: u32,
    gid: u32,
}

impl<'a> JailerArgs<'a> {
    pub fn new(
        id: &'a str,
        exec_file: &str,
        chroot_base: &str,
        uid: &str,
        gid: &str,
    ) -> Result<Self> {
        // Maybe it's a good idea to restrict the id to alphanumeric strings.
        for c in id.chars() {
            if !c.is_alphanumeric() {
                return Err(Error::NotAlphanumeric(id.to_string()));
            }
        }

        let exec_file_path =
            canonicalize(exec_file).map_err(|e| Error::Canonicalize(PathBuf::from(exec_file), e))?;

        if !metadata(&exec_file_path)
            .map_err(|e| Error::Metadata(exec_file_path.clone(), e))?
            .is_file()
        {
            return Err(Error::NotAFile(exec_file_path));
        }

        let chroot_base_dir = canonicalize(chroot_base)
            .map_err(|e| Error::Canonicalize(PathBuf::from(chroot_base), e))?;

        if !metadata(&chroot_base_dir)
            .map_err(|e| Error::Metadata(exec_file_path.clone(), e))?
            .is_dir()
        {
            return Err(Error::NotAFolder(chroot_base_dir));
        }

        let uid = uid.parse::<u32>()
            .map_err(|_| Error::Uid(String::from(uid)))?;
        let gid = gid.parse::<u32>()
            .map_err(|_| Error::Gid(String::from(gid)))?;

        Ok(JailerArgs {
            id,
            exec_file_path,
            chroot_base_dir,
            uid,
            gid,
        })
    }

    pub fn exec_file_name(&self) -> Result<&OsStr> {
        self.exec_file_path
            .file_name()
            .ok_or_else(|| Error::FileName(self.exec_file_path.clone()))
    }
}

pub fn run(args: JailerArgs) -> Result<()> {
    // We open /dev/kvm, /dev/tun, and create the listening socket. These file descriptors will be
    // passed on to Firecracker post exec, and used via knowing their values in advance.

    // TODO: use dup2 to make sure we're actually getting 3, 4, and 5?

    // TODO: can a malicious guest that takes over firecracker use its access to the KVM fd to
    // starve the host of resources? (cgroups should take care of that, but do they currently?)

    // Safe because we use a constant null-terminated string and verify the result.
    let ret = unsafe { libc::open("/dev/kvm\0".as_ptr() as *const libc::c_char, libc::O_RDWR) };
    if ret < 0 {
        return Err(Error::OpenDevKvm(sys_util::Error::last()));
    }
    if ret != KVM_FD {
        return Err(Error::UnexpectedKvmFd(ret));
    }

    // TODO: is RDWR required for /dev/tun (most likely)?
    // Safe because we use a constant null-terminated string and verify the result.
    let ret = unsafe {
        libc::open(
            "/dev/net/tun\0".as_ptr() as *const libc::c_char,
            libc::O_RDWR | libc::O_NONBLOCK,
        )
    };
    if ret < 0 {
        return Err(Error::OpenDevNetTun(sys_util::Error::last()));
    }
    if ret != DEV_NET_TUN_FD {
        return Err(Error::UnexpectedDevNetTunFd(ret));
    }

    let env = Env::new(args)?;

    let listener = UnixListener::bind(env.chroot_dir().join(SOCKET_FILE_NAME))
        .map_err(|e| Error::UnixListener(e))?;

    let listener_fd = listener.as_raw_fd();
    if listener_fd != LISTENER_FD {
        return Err(Error::UnexpectedListenerFd(listener_fd));
    }

    // It turns out Rust is so safe, it opens everything with CLOSE_ON_EXEC.

    // TODO: So as of today (20180612), FD_CLOEXEC is the only file descriptor flag, so setting
    // flags to 0 should clear that and only that. Maybe at some point it would make sense to
    // get the flags first, clear FD_CLOEXEC, and set the resulting flags.

    // This is safe because we know the fd is valid.
    if unsafe { libc::fcntl(listener_fd, libc::F_SETFD, 0) } < 0 {
        return Err(Error::UnsetCloexec(sys_util::Error::last()));
    }

    env.run()
}
<commit_msg>jailer: properly remove FD_CLOEXEC<commit_after>extern crate libc;
extern crate regex;

extern crate sys_util;

mod cgroup;
mod env;

use std::ffi::OsStr;
use std::fs::{canonicalize, metadata};
use std::io;
use std::os::unix::io::AsRawFd;
use std::os::unix::net::UnixListener;
use std::path::PathBuf;
use std::result;

use env::Env;

pub const KVM_FD: i32 = 3;
pub const DEV_NET_TUN_FD: i32 = 4;
pub const LISTENER_FD: i32 = 5;

const SOCKET_FILE_NAME: &str = "api.socket";

#[derive(Debug)]
pub enum Error {
    Canonicalize(PathBuf, io::Error),
    CgroupLineNotFound(&'static str, &'static str),
    CgroupLineNotUnique(&'static str, &'static str),
    Chroot(i32),
    Copy(PathBuf, PathBuf, io::Error),
    CreateDir(PathBuf, io::Error),
    Exec(io::Error),
    FileCreate(PathBuf, io::Error),
    FileName(PathBuf),
    FileOpen(PathBuf, io::Error),
    GetOldFdFlags(sys_util::Error),
    Gid(String),
    Metadata(PathBuf, io::Error),
    NotAFile(PathBuf),
    NotAFolder(PathBuf),
    NotAlphanumeric(String),
    OpenDevKvm(sys_util::Error),
    OpenDevNetTun(sys_util::Error),
    ReadLine(PathBuf, io::Error),
    RegEx(regex::Error),
    Uid(String),
    UnexpectedKvmFd(i32),
    UnexpectedDevNetTunFd(i32),
    UnexpectedListenerFd(i32),
    UnixListener(io::Error),
    UnsetCloexec(sys_util::Error),
    Write(PathBuf, io::Error),
}

pub type Result<T> = result::Result<T, Error>;

pub struct JailerArgs<'a> {
    id: &'a str,
    exec_file_path: PathBuf,
    chroot_base_dir: PathBuf,
    uid: u32,
    gid: u32,
}

impl<'a> JailerArgs<'a> {
    pub fn new(
        id: &'a str,
        exec_file: &str,
        chroot_base: &str,
        uid: &str,
        gid: &str,
    ) -> Result<Self> {
        // Maybe it's a good idea to restrict the id to alphanumeric strings.
        for c in id.chars() {
            if !c.is_alphanumeric() {
                return Err(Error::NotAlphanumeric(id.to_string()));
            }
        }

        let exec_file_path =
            canonicalize(exec_file).map_err(|e| Error::Canonicalize(PathBuf::from(exec_file), e))?;

        if !metadata(&exec_file_path)
            .map_err(|e| Error::Metadata(exec_file_path.clone(), e))?
            .is_file()
        {
            return Err(Error::NotAFile(exec_file_path));
        }

        let chroot_base_dir = canonicalize(chroot_base)
            .map_err(|e| Error::Canonicalize(PathBuf::from(chroot_base), e))?;

        if !metadata(&chroot_base_dir)
            .map_err(|e| Error::Metadata(exec_file_path.clone(), e))?
            .is_dir()
        {
            return Err(Error::NotAFolder(chroot_base_dir));
        }

        let uid = uid.parse::<u32>()
            .map_err(|_| Error::Uid(String::from(uid)))?;
        let gid = gid.parse::<u32>()
            .map_err(|_| Error::Gid(String::from(gid)))?;

        Ok(JailerArgs {
            id,
            exec_file_path,
            chroot_base_dir,
            uid,
            gid,
        })
    }

    pub fn exec_file_name(&self) -> Result<&OsStr> {
        self.exec_file_path
            .file_name()
            .ok_or_else(|| Error::FileName(self.exec_file_path.clone()))
    }
}

pub fn run(args: JailerArgs) -> Result<()> {
    // We open /dev/kvm, /dev/tun, and create the listening socket. These file descriptors will be
    // passed on to Firecracker post exec, and used via knowing their values in advance.

    // TODO: use dup2 to make sure we're actually getting 3, 4, and 5?

    // TODO: can a malicious guest that takes over firecracker use its access to the KVM fd to
    // starve the host of resources? (cgroups should take care of that, but do they currently?)

    // Safe because we use a constant null-terminated string and verify the result.
    let ret = unsafe { libc::open("/dev/kvm\0".as_ptr() as *const libc::c_char, libc::O_RDWR) };
    if ret < 0 {
        return Err(Error::OpenDevKvm(sys_util::Error::last()));
    }
    if ret != KVM_FD {
        return Err(Error::UnexpectedKvmFd(ret));
    }

    // TODO: is RDWR required for /dev/tun (most likely)?
    // Safe because we use a constant null-terminated string and verify the result.
    let ret = unsafe {
        libc::open(
            "/dev/net/tun\0".as_ptr() as *const libc::c_char,
            libc::O_RDWR | libc::O_NONBLOCK,
        )
    };
    if ret < 0 {
        return Err(Error::OpenDevNetTun(sys_util::Error::last()));
    }
    if ret != DEV_NET_TUN_FD {
        return Err(Error::UnexpectedDevNetTunFd(ret));
    }

    let env = Env::new(args)?;

    let listener = UnixListener::bind(env.chroot_dir().join(SOCKET_FILE_NAME))
        .map_err(|e| Error::UnixListener(e))?;

    let listener_fd = listener.as_raw_fd();
    if listener_fd != LISTENER_FD {
        return Err(Error::UnexpectedListenerFd(listener_fd));
    }

    // It turns out Rust is so safe, it opens everything with FD_CLOEXEC, which we have to unset.

    // This is safe because we know fd and the cmd are valid.
    let mut fd_flags = unsafe { libc::fcntl(listener_fd, libc::F_GETFD, 0) };
    if fd_flags < 0 {
        return Err(Error::GetOldFdFlags(sys_util::Error::last()));
    }

    fd_flags &= !libc::FD_CLOEXEC;

    // This is safe because we know the fd, the cmd, and the last arg are valid.
    if unsafe { libc::fcntl(listener_fd, libc::F_SETFD, fd_flags) } < 0 {
        return Err(Error::UnsetCloexec(sys_util::Error::last()));
    }

    env.run()
}
<|endoftext|>


<commit_before>use std::sync::atomic::{fence, AtomicBool, AtomicIsize, AtomicU64, Ordering::*};

fn main() {
    atomic_bool();
    atomic_isize();
    atomic_u64();
    atomic_fences();
}

fn atomic_bool() {
    static mut ATOMIC: AtomicBool = AtomicBool::new(false);

    unsafe {
        assert_eq!(*ATOMIC.get_mut(), false);
        ATOMIC.store(true, SeqCst);
        assert_eq!(*ATOMIC.get_mut(), true);
        ATOMIC.fetch_or(false, SeqCst);
        assert_eq!(*ATOMIC.get_mut(), true);
        ATOMIC.fetch_and(false, SeqCst);
        assert_eq!(*ATOMIC.get_mut(), false);
        ATOMIC.fetch_nand(true, SeqCst);
        assert_eq!(*ATOMIC.get_mut(), true);
        ATOMIC.fetch_xor(true, SeqCst);
        assert_eq!(*ATOMIC.get_mut(), false);
    }
}

fn atomic_isize() {
    static ATOMIC: AtomicIsize = AtomicIsize::new(0);

    // Make sure trans can emit all the intrinsics correctly
    assert_eq!(ATOMIC.compare_exchange(0, 1, Relaxed, Relaxed), Ok(0));
    assert_eq!(ATOMIC.compare_exchange(0, 2, Acquire, Relaxed), Err(1));
    assert_eq!(ATOMIC.compare_exchange(0, 1, Release, Relaxed), Err(1));
    assert_eq!(ATOMIC.compare_exchange(1, 0, AcqRel, Relaxed), Ok(1));
    ATOMIC.compare_exchange(0, 1, SeqCst, Relaxed).ok();
    ATOMIC.compare_exchange(0, 1, Acquire, Acquire).ok();
    ATOMIC.compare_exchange(0, 1, AcqRel, Acquire).ok();
    ATOMIC.compare_exchange(0, 1, SeqCst, Acquire).ok();
    ATOMIC.compare_exchange(0, 1, SeqCst, SeqCst).ok();

    ATOMIC.store(0, SeqCst);

    assert_eq!(ATOMIC.compare_exchange_weak(0, 1, Relaxed, Relaxed), Ok(0));
    assert_eq!(ATOMIC.compare_exchange_weak(0, 2, Acquire, Relaxed), Err(1));
    assert_eq!(ATOMIC.compare_exchange_weak(0, 1, Release, Relaxed), Err(1));
    assert_eq!(ATOMIC.compare_exchange_weak(1, 0, AcqRel, Relaxed), Ok(1));
    ATOMIC.compare_exchange_weak(0, 1, AcqRel, Relaxed).ok();
    ATOMIC.compare_exchange_weak(0, 1, SeqCst, Relaxed).ok();
    ATOMIC.compare_exchange_weak(0, 1, Acquire, Acquire).ok();
    ATOMIC.compare_exchange_weak(0, 1, AcqRel, Acquire).ok();
    ATOMIC.compare_exchange_weak(0, 1, SeqCst, Acquire).ok();
    ATOMIC.compare_exchange_weak(0, 1, SeqCst, SeqCst).ok();
}

fn atomic_u64() {
    static ATOMIC: AtomicU64 = AtomicU64::new(0);

    ATOMIC.store(1, SeqCst);
    assert_eq!(ATOMIC.compare_exchange(0, 0x100, AcqRel, Acquire), Err(1));
    assert_eq!(
        ATOMIC.compare_exchange_weak(1, 0x100, AcqRel, Acquire),
        Ok(1)
    );
    assert_eq!(ATOMIC.load(Relaxed), 0x100);
}

fn atomic_fences() {
    fence(SeqCst);
    fence(Release);
    fence(Acquire);
    fence(AcqRel);
}
<commit_msg>Add compiler fences to test<commit_after>use std::sync::atomic::{compiler_fence, fence, AtomicBool, AtomicIsize, AtomicU64, Ordering::*};

fn main() {
    atomic_bool();
    atomic_isize();
    atomic_u64();
    atomic_fences();
}

fn atomic_bool() {
    static mut ATOMIC: AtomicBool = AtomicBool::new(false);

    unsafe {
        assert_eq!(*ATOMIC.get_mut(), false);
        ATOMIC.store(true, SeqCst);
        assert_eq!(*ATOMIC.get_mut(), true);
        ATOMIC.fetch_or(false, SeqCst);
        assert_eq!(*ATOMIC.get_mut(), true);
        ATOMIC.fetch_and(false, SeqCst);
        assert_eq!(*ATOMIC.get_mut(), false);
        ATOMIC.fetch_nand(true, SeqCst);
        assert_eq!(*ATOMIC.get_mut(), true);
        ATOMIC.fetch_xor(true, SeqCst);
        assert_eq!(*ATOMIC.get_mut(), false);
    }
}

fn atomic_isize() {
    static ATOMIC: AtomicIsize = AtomicIsize::new(0);

    // Make sure trans can emit all the intrinsics correctly
    assert_eq!(ATOMIC.compare_exchange(0, 1, Relaxed, Relaxed), Ok(0));
    assert_eq!(ATOMIC.compare_exchange(0, 2, Acquire, Relaxed), Err(1));
    assert_eq!(ATOMIC.compare_exchange(0, 1, Release, Relaxed), Err(1));
    assert_eq!(ATOMIC.compare_exchange(1, 0, AcqRel, Relaxed), Ok(1));
    ATOMIC.compare_exchange(0, 1, SeqCst, Relaxed).ok();
    ATOMIC.compare_exchange(0, 1, Acquire, Acquire).ok();
    ATOMIC.compare_exchange(0, 1, AcqRel, Acquire).ok();
    ATOMIC.compare_exchange(0, 1, SeqCst, Acquire).ok();
    ATOMIC.compare_exchange(0, 1, SeqCst, SeqCst).ok();

    ATOMIC.store(0, SeqCst);

    assert_eq!(ATOMIC.compare_exchange_weak(0, 1, Relaxed, Relaxed), Ok(0));
    assert_eq!(ATOMIC.compare_exchange_weak(0, 2, Acquire, Relaxed), Err(1));
    assert_eq!(ATOMIC.compare_exchange_weak(0, 1, Release, Relaxed), Err(1));
    assert_eq!(ATOMIC.compare_exchange_weak(1, 0, AcqRel, Relaxed), Ok(1));
    ATOMIC.compare_exchange_weak(0, 1, AcqRel, Relaxed).ok();
    ATOMIC.compare_exchange_weak(0, 1, SeqCst, Relaxed).ok();
    ATOMIC.compare_exchange_weak(0, 1, Acquire, Acquire).ok();
    ATOMIC.compare_exchange_weak(0, 1, AcqRel, Acquire).ok();
    ATOMIC.compare_exchange_weak(0, 1, SeqCst, Acquire).ok();
    ATOMIC.compare_exchange_weak(0, 1, SeqCst, SeqCst).ok();
}

fn atomic_u64() {
    static ATOMIC: AtomicU64 = AtomicU64::new(0);

    ATOMIC.store(1, SeqCst);
    assert_eq!(ATOMIC.compare_exchange(0, 0x100, AcqRel, Acquire), Err(1));
    assert_eq!(
        ATOMIC.compare_exchange_weak(1, 0x100, AcqRel, Acquire),
        Ok(1)
    );
    assert_eq!(ATOMIC.load(Relaxed), 0x100);
}

fn atomic_fences() {
    fence(SeqCst);
    fence(Release);
    fence(Acquire);
    fence(AcqRel);
    compiler_fence(SeqCst);
    compiler_fence(Release);
    compiler_fence(Acquire);
    compiler_fence(AcqRel);
}
<|endoftext|>


<commit_before>//! Tests for serializing and deserializing Weld objects.

extern crate weld;

use std::fmt::Debug;

mod common;
use common::*;

/// A helper for serialization tests.
///
/// Each Weld function should take a vec[i32] and return `T`.
fn check<T: Clone+Debug+PartialEq>(code: &str,
                             input_vec: &Vec<i32>,
                             expect: T) {
    let ref mut conf = default_conf();
    let ref input_data = WeldVec::from(input_vec);
    let ret_value = compile_and_run(code, conf, input_data);
    let data = ret_value.data() as *const T;
    let result = unsafe { (*data).clone() };
    assert_eq!(expect, result);
}

#[test]
fn scalar() {
    let code = "|v: vec[i32]| deserialize[i32](serialize(lookup(v, 0L)))";
    let input_vec: Vec<i32> = (10..20).collect();
    let expect = input_vec[0];
    check(code, &input_vec, expect);
}

#[test]
fn vector_with_structs() {
    let code = "|v: vec[i32]| deserialize[{i32,i32}](serialize({lookup(v, 0L), lookup(v, 1L)}))";
    let input_vec: Vec<i32> = (10..20).collect();
    let expect = Pair::new(input_vec[0], input_vec[1]);
    check(code, &input_vec, expect);
}

#[test]
fn vector_nopointers() {
    let code = "|v: vec[i32]| deserialize[vec[i32]](serialize(v))";
    let input_vec: Vec<i32> = (10..20).collect();
    let expect = WeldVec::from(&input_vec);
    check(code, &input_vec, expect);
}

#[test]
fn nested_vectors() {
    let code = "|v: vec[i32]| deserialize[vec[vec[i32]]](serialize([v, v, v]))";
    let input_vec: Vec<i32> = (10..20).collect();

    let input_data = WeldVec::from(&input_vec);
    let ref vv = vec![input_data.clone(), input_data.clone(), input_data.clone()];
    let expect = WeldVec::from(vv);

    check(code, &input_vec, expect);
}

#[test]
fn struct_with_pointers() {
    let code = "|v:vec[i32]| deserialize[{i32,vec[i32]}](serialize({lookup(v, 0L), v}))";
    let input_vec: Vec<i32> = (10..20).collect();
    let expect = Pair::new(input_vec[0], WeldVec::from(&input_vec));
    check(code, &input_vec, expect);
}

#[test]
fn dict_nopointers() {
    let code = "|v: vec[i32]|
        let dict1 = result(for(v, dictmerger[i32,i32,+], |b,i,e| merge(b, {e,e})));
        tovec(deserialize[dict[i32,i32]](serialize(dict1)))";
    let input_vec: Vec<i32> = (10..20).collect();
    let expect_vec = input_vec.iter().map(|e| Pair::new(*e, *e)).collect::<Vec<_>>();
    let expect = WeldVec::from(&expect_vec);

    let ref conf = default_conf();
    let ref input_data = WeldVec::from(&input_vec);

    let ret_value = compile_and_run(code, conf, input_data);
    let data = ret_value.data() as *const WeldVec<Pair<i32, i32>>;
    let result = unsafe { (*data).clone() };

    fn to_native(v: &WeldVec<Pair<i32,i32>>) -> Vec<(i32, i32)> {
        let mut res: Vec<(i32, i32)> = (0..v.len)
            .into_iter()
            .map(|x| unsafe {
                (
                    (*v.data.offset(x as isize)).ele1,
                    (*v.data.offset(x as isize)).ele2,
                    )
            })
        .collect();
        res.sort_by_key(|a| a.0);
        res
    }
    assert_eq!(to_native(&expect), to_native(&result));
}

#[test]
fn dict_pointers() {
    let code = "|v: vec[i32]|
        let dict2 = result(for(v, groupmerger[i32,i32], |b,i,e| merge(b, {e,e})));
        tovec(deserialize[dict[i32,vec[i32]]](serialize(dict2)))";
    let input_vec: Vec<i32> = (10..20).collect();

    let dict2_inners: Vec<_> = input_vec.iter().map(|e| (*e, vec![*e])).collect();
    let expect_vec = dict2_inners.iter()
        .map(|e| {
            let e1 = e.0;
            let e2 = WeldVec::from(&e.1);
            Pair::new(e1, e2)
        })
    .collect::<Vec<_>>();
    let expect = WeldVec::from(&expect_vec);

    let ref mut conf = default_conf();

    let ref input_data = WeldVec::from(&input_vec);

    let ret_value = compile_and_run(code, conf, input_data);
    let data = ret_value.data() as *const WeldVec<Pair<i32, WeldVec<i32>>>;
    let result = unsafe { (*data).clone() };

    fn to_native(v: &WeldVec<Pair<i32, WeldVec<i32>>>) -> Vec<(i32, Vec<i32>)> {
        let mut res: Vec<(i32, Vec<i32>)> = (0..v.len)
            .into_iter()
            .map(|x| {
                let key = unsafe { (*v.data.offset(x as isize)).ele1 };
                let val = unsafe { ((*v.data.offset(x as isize)).ele2).clone() };
                let vec: Vec<i32> = (0..val.len)
                    .into_iter()
                    .map(|y| unsafe { *val.data.offset(y as isize) })
                    .collect();
                (key, vec)
            })
        .collect();

        // For dictionary outputs, we need to ignore the order.
        res.sort_by_key(|a| a.0);
        res
    }

    assert_eq!(to_native(&expect), to_native(&result));
}
<commit_msg>Test for serde with with nested pointer dictionary<commit_after>//! Tests for serializing and deserializing Weld objects.

extern crate weld;

use std::fmt::Debug;

mod common;
use common::*;

/// A helper for serialization tests.
///
/// Each Weld function should take a vec[i32] and return `T`.
fn check<T: Clone+Debug+PartialEq>(code: &str,
                             input_vec: &Vec<i32>,
                             expect: T) {
    let ref mut conf = default_conf();
    let ref input_data = WeldVec::from(input_vec);
    let ret_value = compile_and_run(code, conf, input_data);
    let data = ret_value.data() as *const T;
    let result = unsafe { (*data).clone() };
    assert_eq!(expect, result);
}

#[test]
fn scalar() {
    let code = "|v: vec[i32]| deserialize[i32](serialize(lookup(v, 0L)))";
    let input_vec: Vec<i32> = (10..20).collect();
    let expect = input_vec[0];
    check(code, &input_vec, expect);
}

#[test]
fn vector_with_structs() {
    let code = "|v: vec[i32]| deserialize[{i32,i32}](serialize({lookup(v, 0L), lookup(v, 1L)}))";
    let input_vec: Vec<i32> = (10..20).collect();
    let expect = Pair::new(input_vec[0], input_vec[1]);
    check(code, &input_vec, expect);
}

#[test]
fn vector_nopointers() {
    let code = "|v: vec[i32]| deserialize[vec[i32]](serialize(v))";
    let input_vec: Vec<i32> = (10..20).collect();
    let expect = WeldVec::from(&input_vec);
    check(code, &input_vec, expect);
}

#[test]
fn nested_vectors() {
    let code = "|v: vec[i32]| deserialize[vec[vec[i32]]](serialize([v, v, v]))";
    let input_vec: Vec<i32> = (10..20).collect();

    let input_data = WeldVec::from(&input_vec);
    let ref vv = vec![input_data.clone(), input_data.clone(), input_data.clone()];
    let expect = WeldVec::from(vv);

    check(code, &input_vec, expect);
}

#[test]
fn struct_with_pointers() {
    let code = "|v:vec[i32]| deserialize[{i32,vec[i32]}](serialize({lookup(v, 0L), v}))";
    let input_vec: Vec<i32> = (10..20).collect();
    let expect = Pair::new(input_vec[0], WeldVec::from(&input_vec));
    check(code, &input_vec, expect);
}

#[test]
fn dict_nopointers() {
    let code = "|v: vec[i32]|
        let dict1 = result(for(v, dictmerger[i32,i32,+], |b,i,e| merge(b, {e,e})));
        tovec(deserialize[dict[i32,i32]](serialize(dict1)))";
    let input_vec: Vec<i32> = (10..20).collect();
    let expect_vec = input_vec.iter().map(|e| Pair::new(*e, *e)).collect::<Vec<_>>();
    let expect = WeldVec::from(&expect_vec);

    let ref conf = default_conf();
    let ref input_data = WeldVec::from(&input_vec);

    let ret_value = compile_and_run(code, conf, input_data);
    let data = ret_value.data() as *const WeldVec<Pair<i32, i32>>;
    let result = unsafe { (*data).clone() };

    fn to_native(v: &WeldVec<Pair<i32,i32>>) -> Vec<(i32, i32)> {
        let mut res: Vec<(i32, i32)> = (0..v.len)
            .into_iter()
            .map(|x| unsafe {
                (
                    (*v.data.offset(x as isize)).ele1,
                    (*v.data.offset(x as isize)).ele2,
                    )
            })
        .collect();
        res.sort_by_key(|a| a.0);
        res
    }
    assert_eq!(to_native(&expect), to_native(&result));
}

#[test]
fn dict_pointers() {
    let code = "|v: vec[i32]|
        let dict2 = result(for(v, groupmerger[i32,i32], |b,i,e| merge(b, {e,e})));
        tovec(deserialize[dict[i32,vec[i32]]](serialize(dict2)))";
    let input_vec: Vec<i32> = (10..20).collect();

    let dict2_inners: Vec<_> = input_vec.iter().map(|e| (*e, vec![*e])).collect();
    let expect_vec = dict2_inners.iter()
        .map(|e| {
            let e1 = e.0;
            let e2 = WeldVec::from(&e.1);
            Pair::new(e1, e2)
        })
    .collect::<Vec<_>>();
    let expect = WeldVec::from(&expect_vec);

    let ref mut conf = default_conf();

    let ref input_data = WeldVec::from(&input_vec);

    let ret_value = compile_and_run(code, conf, input_data);
    let data = ret_value.data() as *const WeldVec<Pair<i32, WeldVec<i32>>>;
    let result = unsafe { (*data).clone() };

    fn to_native(v: &WeldVec<Pair<i32, WeldVec<i32>>>) -> Vec<(i32, Vec<i32>)> {
        let mut res: Vec<(i32, Vec<i32>)> = (0..v.len)
            .into_iter()
            .map(|x| {
                let key = unsafe { (*v.data.offset(x as isize)).ele1 };
                let val = unsafe { ((*v.data.offset(x as isize)).ele2).clone() };
                let vec: Vec<i32> = (0..val.len)
                    .into_iter()
                    .map(|y| unsafe { *val.data.offset(y as isize) })
                    .collect();
                (key, vec)
            })
        .collect();

        // For dictionary outputs, we need to ignore the order.
        res.sort_by_key(|a| a.0);
        res
    }

    assert_eq!(to_native(&expect), to_native(&result));
}

/// Tests dictionaries where the value contains nested pointers.
#[test]
fn dict_nested_pointers() {
    let code = "|v: vec[i32]|
        let dict2 = result(for(v, groupmerger[i32,vec[i32]], |b,i,e| merge(b, {e,[e,e,e,e,e]})));
        tovec(deserialize[dict[i32,vec[vec[i32]]]](serialize(dict2)))";
    let input_vec: Vec<i32> = (10..20).collect();

    let dict2_inners: Vec<(i32, Vec<Vec<i32>>)> = input_vec.iter()
                           .map(|e| (*e, vec![vec![*e; 5]])).collect();

    // Need this to keep the reference to the inner vectors live.
    let mut inner_refs = vec![];

    // Construct the Key/Value vector.
    let expect_vec = dict2_inners.iter()
        .map(|e| {
            let e1 = e.0;
            let inner_vec = e.1.iter()
                                   .map(|v| WeldVec::from(v))
                                   .collect::<Vec<_>>();
            let e2 = WeldVec::from(&inner_vec);

            // Prevent the inner vector from being dropped by holding a ref to it.
            inner_refs.push(inner_vec);

            Pair::new(e1, e2)
        })
    .collect::<Vec<_>>();
    let expect = WeldVec::from(&expect_vec);

    let ref mut conf = default_conf();
    let ref input_data = WeldVec::from(&input_vec);

    let ret_value = compile_and_run(code, conf, input_data);
    let data = ret_value.data() as *const WeldVec<Pair<i32, WeldVec<WeldVec<i32>>>>;
    let result = unsafe { (*data).clone() };

    fn to_native(v: &WeldVec<Pair<i32, WeldVec<WeldVec<i32>>>>) -> Vec<(i32, Vec<Vec<i32>>)> {
        let mut res: Vec<(i32, Vec<Vec<i32>>)> = (0..v.len)
            .into_iter()
            .map(|x| {
                let key = unsafe { (*v.data.offset(x as isize)).ele1 };
                let val = unsafe { ((*v.data.offset(x as isize)).ele2).clone() };
                let vec: Vec<Vec<i32>> = (0..val.len)
                    .into_iter()
                    .map(|y| unsafe {
                        let inner_vec = (*val.data.offset(y as isize)).clone() ;
                        (0..inner_vec.len).into_iter()
                            .map(|z| *inner_vec.data.offset(z as isize) )
                            .collect()
                    })
                    .collect();
                (key, vec)
            })
        .collect();

        // For dictionary outputs, we need to ignore the order.
        res.sort_by_key(|a| a.0);
        res
    }

    assert_eq!(to_native(&expect), to_native(&result));
}
<|endoftext|>


<commit_before>extern crate itertools;

use itertools::{EitherOrBoth, Itertools};

use std::fmt::Debug;
use std::ops::BitXor;

struct Unspecialized<I>(I);
impl<I> Iterator for Unspecialized<I>
where
    I: Iterator,
{
    type Item = I::Item;

    #[inline(always)]
    fn next(&mut self) -> Option<I::Item> {
        self.0.next()
    }

    #[inline(always)]
    fn size_hint(&self) -> (usize, Option<usize>) {
        self.0.size_hint()
    }
}

fn check_specialized<'a, V, IterItem, Iter, F>(iterator: &Iter, mapper: F)
where
    V: Eq + Debug,
    IterItem: 'a,
    Iter: Iterator<Item = IterItem> + Clone + 'a,
    F: Fn(Box<Iterator<Item = IterItem> + 'a>) -> V,
{
    assert_eq!(
        mapper(Box::new(Unspecialized(iterator.clone()))),
        mapper(Box::new(iterator.clone()))
    )
}

fn check_specialized_count_last_nth_sizeh<'a, IterItem, Iter>(it: &Iter, expected_size: usize)
where
    IterItem: 'a + Eq + Debug,
    Iter: Iterator<Item = IterItem> + Clone + 'a,
{
    let size = it.clone().count();
    assert_eq!(size, expected_size);
    check_specialized(it, |i| i.count());
    check_specialized(it, |i| i.last());
    for n in 0..size + 2 {
        check_specialized(it, |mut i| i.nth(n));
    }
    let mut it_sh = it.clone();
    for n in 0..size + 2 {
        let len = it_sh.clone().count();
        let (min, max) = it_sh.size_hint();
        assert_eq!((size - n.min(size)), len);
        assert!(min <= len);
        if let Some(max) = max {
            assert!(len <= max);
        }
        it_sh.next();
    }
}

fn check_specialized_fold_xor<'a, IterItem, Iter>(it: &Iter)
where
    IterItem: 'a
        + BitXor
        + Eq
        + Debug
        + BitXor<<IterItem as BitXor>::Output, Output = <IterItem as BitXor>::Output>
        + Clone,
    <IterItem as BitXor>::Output:
        BitXor<Output = <IterItem as BitXor>::Output> + Eq + Debug + Clone,
    Iter: Iterator<Item = IterItem> + Clone + 'a,
{
    check_specialized(it, |mut i| {
        let first = i.next().map(|f| f.clone() ^ (f.clone() ^ f));
        i.fold(first, |acc, v: IterItem| acc.map(move |a| v ^ a))
    });
}

#[test]
fn put_back() {
    let test_vec = vec![7, 4, 1];
    {
        // Lexical lifetimes support
        let pb = itertools::put_back(test_vec.iter());
        check_specialized_count_last_nth_sizeh(&pb, 3);
        check_specialized_fold_xor(&pb);
    }

    let mut pb = itertools::put_back(test_vec.into_iter());
    pb.put_back(1);
    check_specialized_count_last_nth_sizeh(&pb, 4);
    check_specialized_fold_xor(&pb);
}

#[test]
fn merge_join_by() {
    let i1 = vec![1, 3, 5].into_iter();
    let i2 = vec![0, 3, 4, 5].into_iter();
    let mjb = i1.merge_join_by(i2, std::cmp::Ord::cmp);
    check_specialized_count_last_nth_sizeh(&mjb, 5);
    // Rust 1.24 compatibility:
    fn eob_left_z(eob: EitherOrBoth<usize, usize>) -> usize {
        eob.left().unwrap_or(0)
    }
    fn eob_right_z(eob: EitherOrBoth<usize, usize>) -> usize {
        eob.left().unwrap_or(0)
    }
    fn eob_both_z(eob: EitherOrBoth<usize, usize>) -> usize {
        let (a, b) = eob.both().unwrap_or((0, 0));
        assert_eq!(a, b);
        a
    }
    check_specialized_fold_xor(&mjb.clone().map(eob_left_z));
    check_specialized_fold_xor(&mjb.clone().map(eob_right_z));
    check_specialized_fold_xor(&mjb.clone().map(eob_both_z));
}
<commit_msg>More exhaustive MergeJoinBy specialization test<commit_after>extern crate itertools;

use itertools::{EitherOrBoth, Itertools};

use std::fmt::Debug;
use std::ops::BitXor;

struct Unspecialized<I>(I);
impl<I> Iterator for Unspecialized<I>
where
    I: Iterator,
{
    type Item = I::Item;

    #[inline(always)]
    fn next(&mut self) -> Option<I::Item> {
        self.0.next()
    }

    #[inline(always)]
    fn size_hint(&self) -> (usize, Option<usize>) {
        self.0.size_hint()
    }
}

fn check_specialized<'a, V, IterItem, Iter, F>(iterator: &Iter, mapper: F)
where
    V: Eq + Debug,
    IterItem: 'a,
    Iter: Iterator<Item = IterItem> + Clone + 'a,
    F: Fn(Box<Iterator<Item = IterItem> + 'a>) -> V,
{
    assert_eq!(
        mapper(Box::new(Unspecialized(iterator.clone()))),
        mapper(Box::new(iterator.clone()))
    )
}

fn check_specialized_count_last_nth_sizeh<'a, IterItem, Iter>(it: &Iter, expected_size: usize)
where
    IterItem: 'a + Eq + Debug,
    Iter: Iterator<Item = IterItem> + Clone + 'a,
{
    let size = it.clone().count();
    assert_eq!(size, expected_size);
    check_specialized(it, |i| i.count());
    check_specialized(it, |i| i.last());
    for n in 0..size + 2 {
        check_specialized(it, |mut i| i.nth(n));
    }
    let mut it_sh = it.clone();
    for n in 0..size + 2 {
        let len = it_sh.clone().count();
        let (min, max) = it_sh.size_hint();
        assert_eq!((size - n.min(size)), len);
        assert!(min <= len);
        if let Some(max) = max {
            assert!(len <= max);
        }
        it_sh.next();
    }
}

fn check_specialized_fold_xor<'a, IterItem, Iter>(it: &Iter)
where
    IterItem: 'a
        + BitXor
        + Eq
        + Debug
        + BitXor<<IterItem as BitXor>::Output, Output = <IterItem as BitXor>::Output>
        + Clone,
    <IterItem as BitXor>::Output:
        BitXor<Output = <IterItem as BitXor>::Output> + Eq + Debug + Clone,
    Iter: Iterator<Item = IterItem> + Clone + 'a,
{
    check_specialized(it, |mut i| {
        let first = i.next().map(|f| f.clone() ^ (f.clone() ^ f));
        i.fold(first, |acc, v: IterItem| acc.map(move |a| v ^ a))
    });
}

#[test]
fn put_back() {
    let test_vec = vec![7, 4, 1];
    {
        // Lexical lifetimes support
        let pb = itertools::put_back(test_vec.iter());
        check_specialized_count_last_nth_sizeh(&pb, 3);
        check_specialized_fold_xor(&pb);
    }

    let mut pb = itertools::put_back(test_vec.into_iter());
    pb.put_back(1);
    check_specialized_count_last_nth_sizeh(&pb, 4);
    check_specialized_fold_xor(&pb);
}

#[test]
fn merge_join_by() {
    let i1 = vec![1, 3, 5, 7, 8, 9].into_iter();
    let i2 = vec![0, 3, 4, 5].into_iter();
    let mjb = i1.clone().merge_join_by(i2.clone(), std::cmp::Ord::cmp);
    check_specialized_count_last_nth_sizeh(&mjb, 8);
    // Rust 1.24 compatibility:
    fn eob_left_z(eob: EitherOrBoth<usize, usize>) -> usize {
        eob.left().unwrap_or(0)
    }
    fn eob_right_z(eob: EitherOrBoth<usize, usize>) -> usize {
        eob.left().unwrap_or(0)
    }
    fn eob_both_z(eob: EitherOrBoth<usize, usize>) -> usize {
        let (a, b) = eob.both().unwrap_or((0, 0));
        assert_eq!(a, b);
        a
    }
    check_specialized_fold_xor(&mjb.clone().map(eob_left_z));
    check_specialized_fold_xor(&mjb.clone().map(eob_right_z));
    check_specialized_fold_xor(&mjb.clone().map(eob_both_z));

	// And the other way around
    let mjb = i2.merge_join_by(i1, std::cmp::Ord::cmp);
    check_specialized_count_last_nth_sizeh(&mjb, 8);
    check_specialized_fold_xor(&mjb.clone().map(eob_left_z));
    check_specialized_fold_xor(&mjb.clone().map(eob_right_z));
    check_specialized_fold_xor(&mjb.clone().map(eob_both_z));
}
<|endoftext|>


<commit_before>// rust
// http://rosettacode.org/wiki/foo
// Accepts two command line arguments
// cargo run --name agm arg1 arg2

use std::os;
use std::num;

fn main () {
    let args = os::args();
    let args = args.as_slice();

    let x = from_str::<f32>(args[1].as_slice()).unwrap() ;
    let y = from_str::<f32>(args[2].as_slice()).unwrap() ;

    let result = agm(x,y);
    println!("The arithmetic-geometric mean is {}", result);
}

fn agm (x: f32, y: f32) -> (f32) {
    let e: f32 = 0.000001;
    let mut a = x;
    let mut g = y;
    let mut a1: f32;
    let mut g1: f32;

    loop {
            a1 = (a + g) / 2f32;
            g1 = (a * g).sqrt();
            a = a1;
            g = g1;
            if num::abs( a - g) < e {  return a; }
        }

}
<commit_msg>added a test<commit_after>// rust
// http://rosettacode.org/wiki/foo
// Accepts two command line arguments
// cargo run --name agm arg1 arg2

use std::os;
use std::num;

fn main () {
    let args = os::args();
    let args = args.as_slice();

    let x = from_str::<f32>(args[1].as_slice()).unwrap() ;
    let y = from_str::<f32>(args[2].as_slice()).unwrap() ;

    let result = agm(x,y);
    println!("The arithmetic-geometric mean is {}", result);
}

fn agm (x: f32, y: f32) -> (f32) {
    let e: f32 = 0.000001;
    let mut a = x;
    let mut g = y;
    let mut a1: f32;
    let mut g1: f32;

    loop {
            a1 = (a + g) / 2f32;
            g1 = (a * g).sqrt();
            a = a1;
            g = g1;
            if num::abs( a - g) < e {  return a; }
        }

}

#[test]
fn_test_agm_12_6() {
    assert! ( agm(12,6) == 8.740746 );
}
<|endoftext|>


<commit_before>use display_list::DisplayListBuilder;
use euclid::{Point2D, Size2D};
use ipc_channel::ipc::{self, IpcSender};
use stacking_context::StackingContext;
use std::cell::Cell;
use types::{ColorF, DisplayListId, Epoch, FontKey, StackingContextId};
use types::{ImageKey, ImageFormat, NativeFontHandle, PipelineId};
use webgl::{WebGLContextId, WebGLCommand};
use offscreen_gl_context::GLContextAttributes;

#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
pub struct IdNamespace(pub u32);

#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
pub struct ResourceId(pub u32);

#[derive(Serialize, Deserialize)]
pub enum ApiMsg {
    AddRawFont(FontKey, Vec<u8>),
    AddNativeFont(FontKey, NativeFontHandle),
    AddImage(ImageKey, u32, u32, ImageFormat, Vec<u8>),
    UpdateImage(ImageKey, u32, u32, ImageFormat, Vec<u8>),
    AddDisplayList(DisplayListId, PipelineId, Epoch, DisplayListBuilder),
    AddStackingContext(StackingContextId, PipelineId, Epoch, StackingContext),
    CloneApi(IpcSender<IdNamespace>),
    SetRootStackingContext(StackingContextId, ColorF, Epoch, PipelineId),
    SetRootPipeline(PipelineId),
    Scroll(Point2D<f32>),
    TranslatePointToLayerSpace(Point2D<f32>, IpcSender<Point2D<f32>>),
    RequestWebGLContext(Size2D<i32>, GLContextAttributes, IpcSender<Result<WebGLContextId, String>>),
    WebGLCommand(WebGLContextId, WebGLCommand),
}

#[derive(Serialize, Deserialize, Clone)]
pub struct RenderApiSender(IpcSender<ApiMsg>);

impl RenderApiSender {
    pub fn new(tx: IpcSender<ApiMsg>) -> RenderApiSender {
        RenderApiSender(tx)
    }

    pub fn create_api(&self) -> RenderApi {
        let RenderApiSender(ref tx) = *self;
        let (sync_tx, sync_rx) = ipc::channel().unwrap();
        let msg = ApiMsg::CloneApi(sync_tx);
        tx.send(msg).unwrap();
        RenderApi {
            tx: tx.clone(),
            id_namespace: sync_rx.recv().unwrap(),
            next_id: Cell::new(ResourceId(0)),
        }
    }
}

pub struct RenderApi {
    pub tx: IpcSender<ApiMsg>,
    pub id_namespace: IdNamespace,
    pub next_id: Cell<ResourceId>,
}

impl RenderApi {
    pub fn clone_sender(&self) -> RenderApiSender {
        RenderApiSender::new(self.tx.clone())
    }

    pub fn add_raw_font(&self, bytes: Vec<u8>) -> FontKey {
        let new_id = self.next_unique_id();
        let key = FontKey::new(new_id.0, new_id.1);
        let msg = ApiMsg::AddRawFont(key, bytes);
        self.tx.send(msg).unwrap();
        key
    }

    pub fn add_native_font(&self, native_font_handle: NativeFontHandle) -> FontKey {
        let new_id = self.next_unique_id();
        let key = FontKey::new(new_id.0, new_id.1);
        let msg = ApiMsg::AddNativeFont(key, native_font_handle);
        self.tx.send(msg).unwrap();
        key
    }

    pub fn alloc_image(&self) -> ImageKey {
        let new_id = self.next_unique_id();
        ImageKey::new(new_id.0, new_id.1)
    }

    pub fn add_image(&self,
                     width: u32,
                     height: u32,
                     format: ImageFormat,
                     bytes: Vec<u8>) -> ImageKey {
        let new_id = self.next_unique_id();
        let key = ImageKey::new(new_id.0, new_id.1);
        let msg = ApiMsg::AddImage(key, width, height, format, bytes);
        self.tx.send(msg).unwrap();
        key
    }

    // TODO: Support changing dimensions (and format) during image update?
    pub fn update_image(&self,
                        key: ImageKey,
                        width: u32,
                        height: u32,
                        format: ImageFormat,
                        bytes: Vec<u8>) {
        let msg = ApiMsg::UpdateImage(key, width, height, format, bytes);
        self.tx.send(msg).unwrap();
    }

    pub fn add_display_list(&self,
                            display_list: DisplayListBuilder,
                            stacking_context: &mut StackingContext,
                            pipeline_id: PipelineId,
                            epoch: Epoch) -> Option<DisplayListId> {
        //TODO! debug_assert!(display_list.item_count() > 0, "Avoid adding empty lists!");
        let new_id = self.next_unique_id();
        let id = DisplayListId(new_id.0, new_id.1);
        stacking_context.has_stacking_contexts = stacking_context.has_stacking_contexts ||
                                                 display_list.has_stacking_contexts;
        let msg = ApiMsg::AddDisplayList(id, pipeline_id, epoch, display_list);
        self.tx.send(msg).unwrap();
        stacking_context.display_lists.push(id);

        Some(id)
    }

    pub fn add_stacking_context(&self,
                                stacking_context: StackingContext,
                                pipeline_id: PipelineId,
                                epoch: Epoch) -> StackingContextId {
        let new_id = self.next_unique_id();
        let id = StackingContextId(new_id.0, new_id.1);
        let msg = ApiMsg::AddStackingContext(id, pipeline_id, epoch, stacking_context);
        self.tx.send(msg).unwrap();
        id
    }

    pub fn set_root_pipeline(&self, pipeline_id: PipelineId) {
        let msg = ApiMsg::SetRootPipeline(pipeline_id);
        self.tx.send(msg).unwrap();
    }

    pub fn set_root_stacking_context(&self,
                                     stacking_context_id: StackingContextId,
                                     background_color: ColorF,
                                     epoch: Epoch,
                                     pipeline_id: PipelineId) {
        let msg = ApiMsg::SetRootStackingContext(stacking_context_id,
                                                 background_color,
                                                 epoch,
                                                 pipeline_id);
        self.tx.send(msg).unwrap();
    }

    pub fn scroll(&self, delta: Point2D<f32>) {
        let msg = ApiMsg::Scroll(delta);
        self.tx.send(msg).unwrap();
    }

    pub fn translate_point_to_layer_space(&self, point: &Point2D<f32>) -> Point2D<f32> {
        let (tx, rx) = ipc::channel().unwrap();
        let msg = ApiMsg::TranslatePointToLayerSpace(*point, tx);
        self.tx.send(msg).unwrap();
        rx.recv().unwrap()
    }

    pub fn request_webgl_context(&self, size: &Size2D<i32>, attributes: GLContextAttributes) -> Result<WebGLContextId, String> {
        let (tx, rx) = ipc::channel().unwrap();
        let msg = ApiMsg::RequestWebGLContext(*size, attributes, tx);
        self.tx.send(msg).unwrap();
        rx.recv().unwrap()
    }

    pub fn send_webgl_command(&self, context_id: WebGLContextId, command: WebGLCommand) {
        let msg = ApiMsg::WebGLCommand(context_id, command);
        self.tx.send(msg).unwrap();
    }

    fn next_unique_id(&self) -> (u32, u32) {
        let IdNamespace(namespace) = self.id_namespace;
        let ResourceId(id) = self.next_id.get();
        self.next_id.set(ResourceId(id + 1));
        (namespace, id)
    }
}
<commit_msg>api fix<commit_after>use display_list::DisplayListBuilder;
use euclid::{Point2D, Size2D};
use ipc_channel::ipc::{self, IpcSender};
use stacking_context::StackingContext;
use std::cell::Cell;
use types::{ColorF, DisplayListId, Epoch, FontKey, StackingContextId};
use types::{ImageKey, ImageFormat, NativeFontHandle, PipelineId};
use webgl::{WebGLContextId, WebGLCommand};
use offscreen_gl_context::GLContextAttributes;

#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
pub struct IdNamespace(pub u32);

#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
pub struct ResourceId(pub u32);

#[derive(Serialize, Deserialize)]
pub enum ApiMsg {
    AddRawFont(FontKey, Vec<u8>),
    AddNativeFont(FontKey, NativeFontHandle),
    AddImage(ImageKey, u32, u32, ImageFormat, Vec<u8>),
    UpdateImage(ImageKey, u32, u32, ImageFormat, Vec<u8>),
    AddDisplayList(DisplayListId, PipelineId, Epoch, DisplayListBuilder),
    AddStackingContext(StackingContextId, PipelineId, Epoch, StackingContext),
    CloneApi(IpcSender<IdNamespace>),
    SetRootStackingContext(StackingContextId, ColorF, Epoch, PipelineId),
    SetRootPipeline(PipelineId),
    Scroll(Point2D<f32>, Point2D<f32>),
    TranslatePointToLayerSpace(Point2D<f32>, IpcSender<Point2D<f32>>),
    RequestWebGLContext(Size2D<i32>, GLContextAttributes, IpcSender<Result<WebGLContextId, String>>),
    WebGLCommand(WebGLContextId, WebGLCommand),
}

#[derive(Serialize, Deserialize, Clone)]
pub struct RenderApiSender(IpcSender<ApiMsg>);

impl RenderApiSender {
    pub fn new(tx: IpcSender<ApiMsg>) -> RenderApiSender {
        RenderApiSender(tx)
    }

    pub fn create_api(&self) -> RenderApi {
        let RenderApiSender(ref tx) = *self;
        let (sync_tx, sync_rx) = ipc::channel().unwrap();
        let msg = ApiMsg::CloneApi(sync_tx);
        tx.send(msg).unwrap();
        RenderApi {
            tx: tx.clone(),
            id_namespace: sync_rx.recv().unwrap(),
            next_id: Cell::new(ResourceId(0)),
        }
    }
}

pub struct RenderApi {
    pub tx: IpcSender<ApiMsg>,
    pub id_namespace: IdNamespace,
    pub next_id: Cell<ResourceId>,
}

impl RenderApi {
    pub fn clone_sender(&self) -> RenderApiSender {
        RenderApiSender::new(self.tx.clone())
    }

    pub fn add_raw_font(&self, bytes: Vec<u8>) -> FontKey {
        let new_id = self.next_unique_id();
        let key = FontKey::new(new_id.0, new_id.1);
        let msg = ApiMsg::AddRawFont(key, bytes);
        self.tx.send(msg).unwrap();
        key
    }

    pub fn add_native_font(&self, native_font_handle: NativeFontHandle) -> FontKey {
        let new_id = self.next_unique_id();
        let key = FontKey::new(new_id.0, new_id.1);
        let msg = ApiMsg::AddNativeFont(key, native_font_handle);
        self.tx.send(msg).unwrap();
        key
    }

    pub fn alloc_image(&self) -> ImageKey {
        let new_id = self.next_unique_id();
        ImageKey::new(new_id.0, new_id.1)
    }

    pub fn add_image(&self,
                     width: u32,
                     height: u32,
                     format: ImageFormat,
                     bytes: Vec<u8>) -> ImageKey {
        let new_id = self.next_unique_id();
        let key = ImageKey::new(new_id.0, new_id.1);
        let msg = ApiMsg::AddImage(key, width, height, format, bytes);
        self.tx.send(msg).unwrap();
        key
    }

    // TODO: Support changing dimensions (and format) during image update?
    pub fn update_image(&self,
                        key: ImageKey,
                        width: u32,
                        height: u32,
                        format: ImageFormat,
                        bytes: Vec<u8>) {
        let msg = ApiMsg::UpdateImage(key, width, height, format, bytes);
        self.tx.send(msg).unwrap();
    }

    pub fn add_display_list(&self,
                            display_list: DisplayListBuilder,
                            stacking_context: &mut StackingContext,
                            pipeline_id: PipelineId,
                            epoch: Epoch) -> Option<DisplayListId> {
        //TODO! debug_assert!(display_list.item_count() > 0, "Avoid adding empty lists!");
        let new_id = self.next_unique_id();
        let id = DisplayListId(new_id.0, new_id.1);
        stacking_context.has_stacking_contexts = stacking_context.has_stacking_contexts ||
                                                 display_list.has_stacking_contexts;
        let msg = ApiMsg::AddDisplayList(id, pipeline_id, epoch, display_list);
        self.tx.send(msg).unwrap();
        stacking_context.display_lists.push(id);

        Some(id)
    }

    pub fn add_stacking_context(&self,
                                stacking_context: StackingContext,
                                pipeline_id: PipelineId,
                                epoch: Epoch) -> StackingContextId {
        let new_id = self.next_unique_id();
        let id = StackingContextId(new_id.0, new_id.1);
        let msg = ApiMsg::AddStackingContext(id, pipeline_id, epoch, stacking_context);
        self.tx.send(msg).unwrap();
        id
    }

    pub fn set_root_pipeline(&self, pipeline_id: PipelineId) {
        let msg = ApiMsg::SetRootPipeline(pipeline_id);
        self.tx.send(msg).unwrap();
    }

    pub fn set_root_stacking_context(&self,
                                     stacking_context_id: StackingContextId,
                                     background_color: ColorF,
                                     epoch: Epoch,
                                     pipeline_id: PipelineId) {
        let msg = ApiMsg::SetRootStackingContext(stacking_context_id,
                                                 background_color,
                                                 epoch,
                                                 pipeline_id);
        self.tx.send(msg).unwrap();
    }

    pub fn scroll(&self, delta: Point2D<f32>, cursor: Point2D<f32>) {
        let msg = ApiMsg::Scroll(delta, cursor);
        self.tx.send(msg).unwrap();
    }

    pub fn translate_point_to_layer_space(&self, point: &Point2D<f32>) -> Point2D<f32> {
        let (tx, rx) = ipc::channel().unwrap();
        let msg = ApiMsg::TranslatePointToLayerSpace(*point, tx);
        self.tx.send(msg).unwrap();
        rx.recv().unwrap()
    }

    pub fn request_webgl_context(&self, size: &Size2D<i32>, attributes: GLContextAttributes) -> Result<WebGLContextId, String> {
        let (tx, rx) = ipc::channel().unwrap();
        let msg = ApiMsg::RequestWebGLContext(*size, attributes, tx);
        self.tx.send(msg).unwrap();
        rx.recv().unwrap()
    }

    pub fn send_webgl_command(&self, context_id: WebGLContextId, command: WebGLCommand) {
        let msg = ApiMsg::WebGLCommand(context_id, command);
        self.tx.send(msg).unwrap();
    }

    fn next_unique_id(&self) -> (u32, u32) {
        let IdNamespace(namespace) = self.id_namespace;
        let ResourceId(id) = self.next_id.get();
        self.next_id.set(ResourceId(id + 1));
        (namespace, id)
    }
}
<|endoftext|>


<commit_before>pub enum GTPError {
    NotImplemented,
    InvalidBoardSize,
    InvalidMove,
    BadVertexList,
    BoardNotEmpty,
    CannotUndo,
}

pub enum Colour {
    Black,
    White
}

#[allow(dead_code)]
pub struct Vertex {
    x: u8, // letter
    y: u8  // number
}

pub enum Move {
    Stone(Vertex),
    Pass,
    Resign
}

#[allow(dead_code)]
pub struct ColouredMove {
    player: Colour,
    move: Move
}

pub enum StoneStatus {
    Alive,
    Seki,
    Dead
}

pub trait GoBot {
    // Static functions identifying the bot :

    // name (ex : "My super Bot")
    fn gtp_name() -> &'static str;
    // version (ex : "v2.3-r5")
    fn gtp_version() -> &'static str;

    // Any function returning a GTPError that it is not supposed
    // to return will be fatal to the framework.

    // Basic functions, must be implemented

    // clear_board : clears the board, can never fail
    fn gtp_clear_board(&mut self) -> ();
    // komi : sets the komi, can never fail, must accept absurd values
    fn gtp_komi(&mut self, komi: f32) -> ();
    // boardsize : sets the board size.
    // Returns InvalidBoardSize if the size is not supported.
    fn gtp_boardsize(&mut self, size: uint) -> Result<(), GTPError>;
    // play : plays the provided move on the board
    // Returns InvalidMove is the move is invalid
    fn gtp_play(&mut self, move: ColouredMove) -> Result<(), GTPError>;
    // genmove : ask the bot for a move of the chosen color
    // cannot fail, the bot must provide a move even if the last
    // played move is of the same colour
    // plays the move as well
    fn gtp_genmove(&mut self, player: Colour) -> Move;

    // Optional functions, if not iplemented, the corresponding
    // commands will not be activated
    // All these functions will be called once by the framework
    // at startup, then clear_board will be called

    // genmove_regression : like genmove, but must be deterministic
    // and must not actually play the move
    // should always return Ok(Move), never raise any error
    #[allow(unused_variable)]
    fn gtp_genmove_regression(&self, player: Colour) -> Result<Move, GTPError> {
        Err(NotImplemented)
    }
    // undo : undo last move if possible
    // if not, return Err(CannotUndo)
    // if undo is never possible, should not be implemented
    #[allow(unused_variable)]
    fn gtp_undo(&mut self) -> Result<(), GTPError> {
        Err(NotImplemented)
    }
    // place_free_handicap : The bot places its handicap stones
    // and returns a slice to a vector of Vertexes
    // it can place less stones if the asked number is too high
    // fails with Err(BoardNotEmpty) if board isn't empty
    #[allow(unused_variable)]
    fn gtp_place_free_handicap(&mut self, number: uint) -> Result<&[Vertex], GTPError> {
        Err(NotImplemented)
    }
    // set_free_handicap : uses the provided list as handicap stones
    // for black
    // fails with Err(BoardNotEmpty) if board isn't empty
    // fails woth Err(BadVertexList) if the vertex list is unusable
    // (two stones at the same place, or stones outside the board)
    #[allow(unused_variable)]
    fn gtp_set_free_handicap(&mut self, stones: &[Vertex]) -> Result<(), GTPError> {
        Err(NotImplemented)
    }
    // time_settings : sets the time settings for the game
    // it is only informative, the btot should count it's own time,
    // but the controller is supposed to enforce it
    // time are give in minute, should never fail
    #[allow(unused_variable)]
    fn gtp_time_settings(&mut self, main_time: int, byoyomi_time: int, byoyomi_stones: int) -> Result<(), GTPError> {
        Err(NotImplemented)
    }
    // final_status_list : returns a slice to the list of stones of
    // any color in the given status, in the opinion of the bot
    // should never fail
    #[allow(unused_variable)]
    fn gtp_final_status_list(&self, status: StoneStatus) -> Result<(), GTPError> {
        Err(NotImplemented)
    }
    // final_score : computes the bot's calculation of the final score
    // if it is a draw, float value must be 0 and colour is not important
    // should never fail
    #[allow(unused_variable)]
    fn gtp_final_score(&self) -> Result<(f32, Colour), GTPError> {
        Err(NotImplemented)
    }
    // showboard : returns a description of the board as saw by the bot :
    // (boardsize, black_stones, white_stones, black_captured_count, white_captured_count)
    // should never fail
    #[allow(unused_variable)]
    fn gtp_showboard(&self) -> Result<(int, &[Vertex], &[Vertex], int, int), GTPError> {
        Err(NotImplemented)
    }
}

// Vertex implementation for messing with strings
impl Vertex {
    #[allow(dead_code)]
    pub fn from_coords(x: u8, y:u8) -> Vertex {
        if x == 0 || x > 25 || y == 0 || y > 25 {
            fail!("Vertex coordinates must be in 1-25.");
        }
        Vertex{x: x, y: y}
    }

    #[allow(dead_code)]
    pub fn from_str(text: &str) -> Vertex {
        if text.len() < 2 || text.len() > 3 {
            fail!("Invalid text coordinates.");
        }
        let mut x: u8 = text[0];
        if x < ('A' as u8) || x > ('Z' as u8) || (x as char) == 'I' {
            fail!("Invalid text coordinates.");
        }
        x -= ('A' as u8) - 1;
        if x > 9 {
            x -= 1;
        } // eliminate 'I'
        let number = ::std::u8::parse_bytes(text.as_bytes().slice_from(1), 10);
        let mut y: u8 = 0;
        match number {
            Some(num) => y = num,
            _ => (),
        }
        if y == 0 || y > 25 {
            fail!("Invalid text coordinates.");
        }
        Vertex{x: x, y: y}
    }

    #[allow(dead_code)]
    pub fn to_coords(&self) -> (u8, u8) {
        (self.x, self.y)
    }

    #[allow(dead_code)]
    pub fn to_string(&self) -> String {
        let mut letter: u8 = 'A' as u8;
        if self.x >= 9 {
            // eliminate 'I'
            letter += self.x;
        } else {
            letter += self.x-1;
        }
        format!("{:c}{:u}", letter as char, self.y)
    }
}

#[cfg(test)]
mod tests {
    #[test]
    fn vertex_to_string() {
        let vrtx1 = super::Vertex::from_coords(8u8, 7u8);
        assert_eq!(vrtx1.to_string().as_slice(), "H7");
        let vrtx2 = super::Vertex::from_coords(9u8, 13u8);
        assert_eq!(vrtx2.to_string().as_slice(), "J13");
        let vrtx3 = super::Vertex::from_coords(19u8, 1u8);
        assert_eq!(vrtx3.to_string().as_slice(), "T1");
    }

    #[test]
    fn string_to_vertex() {
        let vrtx1 = super::Vertex::from_str("C7");
        assert_eq!(vrtx1.to_coords(), (3u8, 7u8));
        let vrtx2 = super::Vertex::from_str("J11");
        assert_eq!(vrtx2.to_coords(), (9u8, 11u8));
        let vrtx3 = super::Vertex::from_str("Z25");
        assert_eq!(vrtx3.to_coords(), (25u8, 25u8));
    }

    #[test]
    #[should_fail]
    fn too_big_coordinates() {
        let vrtx = super::Vertex::from_coords(26u8, 13u8);
        assert_eq!(vrtx.to_coords(), (26u8, 13u8));
    }

    #[test]
    #[should_fail]
    fn invalid_string() {
        let vrtx = super::Vertex::from_str("I13");
        assert_eq!(vrtx.to_coords(), (9u8, 13u8));
    }

}
<commit_msg>Fix vertex coordinates parsing.<commit_after>pub enum GTPError {
    NotImplemented,
    InvalidBoardSize,
    InvalidMove,
    BadVertexList,
    BoardNotEmpty,
    CannotUndo,
}

pub enum Colour {
    Black,
    White
}

#[allow(dead_code)]
pub struct Vertex {
    x: u8, // letter
    y: u8  // number
}

pub enum Move {
    Stone(Vertex),
    Pass,
    Resign
}

#[allow(dead_code)]
pub struct ColouredMove {
    player: Colour,
    move: Move
}

pub enum StoneStatus {
    Alive,
    Seki,
    Dead
}

pub trait GoBot {
    // Static functions identifying the bot :

    // name (ex : "My super Bot")
    fn gtp_name() -> &'static str;
    // version (ex : "v2.3-r5")
    fn gtp_version() -> &'static str;

    // Any function returning a GTPError that it is not supposed
    // to return will be fatal to the framework.

    // Basic functions, must be implemented

    // clear_board : clears the board, can never fail
    fn gtp_clear_board(&mut self) -> ();
    // komi : sets the komi, can never fail, must accept absurd values
    fn gtp_komi(&mut self, komi: f32) -> ();
    // boardsize : sets the board size.
    // Returns InvalidBoardSize if the size is not supported.
    fn gtp_boardsize(&mut self, size: uint) -> Result<(), GTPError>;
    // play : plays the provided move on the board
    // Returns InvalidMove is the move is invalid
    fn gtp_play(&mut self, move: ColouredMove) -> Result<(), GTPError>;
    // genmove : ask the bot for a move of the chosen color
    // cannot fail, the bot must provide a move even if the last
    // played move is of the same colour
    // plays the move as well
    fn gtp_genmove(&mut self, player: Colour) -> Move;

    // Optional functions, if not iplemented, the corresponding
    // commands will not be activated
    // All these functions will be called once by the framework
    // at startup, then clear_board will be called

    // genmove_regression : like genmove, but must be deterministic
    // and must not actually play the move
    // should always return Ok(Move), never raise any error
    #[allow(unused_variable)]
    fn gtp_genmove_regression(&self, player: Colour) -> Result<Move, GTPError> {
        Err(NotImplemented)
    }
    // undo : undo last move if possible
    // if not, return Err(CannotUndo)
    // if undo is never possible, should not be implemented
    #[allow(unused_variable)]
    fn gtp_undo(&mut self) -> Result<(), GTPError> {
        Err(NotImplemented)
    }
    // place_free_handicap : The bot places its handicap stones
    // and returns a slice to a vector of Vertexes
    // it can place less stones if the asked number is too high
    // fails with Err(BoardNotEmpty) if board isn't empty
    #[allow(unused_variable)]
    fn gtp_place_free_handicap(&mut self, number: uint) -> Result<&[Vertex], GTPError> {
        Err(NotImplemented)
    }
    // set_free_handicap : uses the provided list as handicap stones
    // for black
    // fails with Err(BoardNotEmpty) if board isn't empty
    // fails woth Err(BadVertexList) if the vertex list is unusable
    // (two stones at the same place, or stones outside the board)
    #[allow(unused_variable)]
    fn gtp_set_free_handicap(&mut self, stones: &[Vertex]) -> Result<(), GTPError> {
        Err(NotImplemented)
    }
    // time_settings : sets the time settings for the game
    // it is only informative, the btot should count it's own time,
    // but the controller is supposed to enforce it
    // time are give in minute, should never fail
    #[allow(unused_variable)]
    fn gtp_time_settings(&mut self, main_time: int, byoyomi_time: int, byoyomi_stones: int) -> Result<(), GTPError> {
        Err(NotImplemented)
    }
    // final_status_list : returns a slice to the list of stones of
    // any color in the given status, in the opinion of the bot
    // should never fail
    #[allow(unused_variable)]
    fn gtp_final_status_list(&self, status: StoneStatus) -> Result<(), GTPError> {
        Err(NotImplemented)
    }
    // final_score : computes the bot's calculation of the final score
    // if it is a draw, float value must be 0 and colour is not important
    // should never fail
    #[allow(unused_variable)]
    fn gtp_final_score(&self) -> Result<(f32, Colour), GTPError> {
        Err(NotImplemented)
    }
    // showboard : returns a description of the board as saw by the bot :
    // (boardsize, black_stones, white_stones, black_captured_count, white_captured_count)
    // should never fail
    #[allow(unused_variable)]
    fn gtp_showboard(&self) -> Result<(int, &[Vertex], &[Vertex], int, int), GTPError> {
        Err(NotImplemented)
    }
}

// Vertex implementation for messing with strings
impl Vertex {
    #[allow(dead_code)]
    pub fn from_coords(x: u8, y:u8) -> Vertex {
        if x == 0 || x > 25 || y == 0 || y > 25 {
            fail!("Vertex coordinates must be in 1-25.");
        }
        Vertex{x: x, y: y}
    }

    #[allow(dead_code)]
    pub fn from_str(text: &str) -> Vertex {
        if text.len() < 2 || text.len() > 3 {
            fail!("Invalid text coordinates.");
        }
        let mut x: u8 = text.as_bytes()[0];
        if x < ('A' as u8) || x > ('Z' as u8) || (x as char) == 'I' {
            fail!("Invalid text coordinates.");
        }
        x -= ('A' as u8) - 1;
        if x > 9 {
            x -= 1;
        } // eliminate 'I'
        let number = ::std::u8::parse_bytes(text.as_bytes().slice_from(1), 10);
        let mut y: u8 = 0;
        match number {
            Some(num) => y = num,
            _ => (),
        }
        if y == 0 || y > 25 {
            fail!("Invalid text coordinates.");
        }
        Vertex{x: x, y: y}
    }

    #[allow(dead_code)]
    pub fn to_coords(&self) -> (u8, u8) {
        (self.x, self.y)
    }

    #[allow(dead_code)]
    pub fn to_string(&self) -> String {
        let mut letter: u8 = 'A' as u8;
        if self.x >= 9 {
            // eliminate 'I'
            letter += self.x;
        } else {
            letter += self.x-1;
        }
        format!("{:c}{:u}", letter as char, self.y)
    }
}

#[cfg(test)]
mod tests {
    #[test]
    fn vertex_to_string() {
        let vrtx1 = super::Vertex::from_coords(8u8, 7u8);
        assert_eq!(vrtx1.to_string().as_slice(), "H7");
        let vrtx2 = super::Vertex::from_coords(9u8, 13u8);
        assert_eq!(vrtx2.to_string().as_slice(), "J13");
        let vrtx3 = super::Vertex::from_coords(19u8, 1u8);
        assert_eq!(vrtx3.to_string().as_slice(), "T1");
    }

    #[test]
    fn string_to_vertex() {
        let vrtx1 = super::Vertex::from_str("C7");
        assert_eq!(vrtx1.to_coords(), (3u8, 7u8));
        let vrtx2 = super::Vertex::from_str("J11");
        assert_eq!(vrtx2.to_coords(), (9u8, 11u8));
        let vrtx3 = super::Vertex::from_str("Z25");
        assert_eq!(vrtx3.to_coords(), (25u8, 25u8));
    }

    #[test]
    #[should_fail]
    fn too_big_coordinates() {
        let vrtx = super::Vertex::from_coords(26u8, 13u8);
        assert_eq!(vrtx.to_coords(), (26u8, 13u8));
    }

    #[test]
    #[should_fail]
    fn invalid_string() {
        let vrtx = super::Vertex::from_str("I13");
        assert_eq!(vrtx.to_coords(), (9u8, 13u8));
    }

}
<|endoftext|>


<commit_before>// Copyright 2018 Pants project contributors (see CONTRIBUTORS.md).
// Licensed under the Apache License, Version 2.0 (see LICENSE).

#![deny(warnings)]
// Enable all clippy lints except for many of the pedantic ones. It's a shame this needs to be copied and pasted across crates, but there doesn't appear to be a way to include inner attributes from a common source.
#![deny(
  clippy::all,
  clippy::default_trait_access,
  clippy::expl_impl_clone_on_copy,
  clippy::if_not_else,
  clippy::needless_continue,
  clippy::single_match_else,
  clippy::unseparated_literal_suffix,
  clippy::used_underscore_binding
)]
// It is often more clear to show that nothing is being moved.
#![allow(clippy::match_ref_pats)]
// Subjective style.
#![allow(
  clippy::len_without_is_empty,
  clippy::redundant_field_names,
  clippy::too_many_arguments
)]
// Default isn't as big a deal as people seem to think it is.
#![allow(clippy::new_without_default, clippy::new_ret_no_self)]
// Arc<Mutex> can be more clear than needing to grok Orderings:
#![allow(clippy::mutex_atomic)]

use termion;

use std::collections::{BTreeMap, VecDeque};
use std::io::Write;
use std::io::{stdout, Result, Stdout};

use termion::cursor::DetectCursorPos;
use termion::raw::IntoRawMode;
use termion::raw::RawTerminal;
use termion::{clear, color, cursor};
use unicode_segmentation::UnicodeSegmentation;

enum Console {
  Terminal(RawTerminal<Stdout>),
  Pipe(Stdout),
}

#[derive(Clone)]
struct PrintableMsg {
  msg: String,
  output: PrintableMsgOutput,
}

#[derive(Clone)]
enum PrintableMsgOutput {
  Stdout,
  Stderr,
}

pub struct EngineDisplay {
  sigil: char,
  divider: String,
  padding: String,
  terminal: Console,
  action_map: BTreeMap<String, String>,
  logs: VecDeque<String>,
  printable_msgs: VecDeque<PrintableMsg>,
  running: bool,
  cursor_start: (u16, u16),
  terminal_size: (u16, u16),
}

// TODO: Prescribe a threading/polling strategy for callers - or implement a built-in one.
// TODO: Better error handling for .flush() and .write() failure modes.
// TODO: Permit scrollback in the terminal - both at exit and during the live run.
impl EngineDisplay {
  /// Create a new EngineDisplay
  pub fn new(indent_level: u16) -> EngineDisplay {
    let write_handle = stdout();

    let mut display = EngineDisplay {
      sigil: '⚡',
      divider: "▵".to_string(),
      padding: " ".repeat(indent_level.into()),
      terminal: match write_handle.into_raw_mode() {
        Ok(t) => Console::Terminal(t),
        Err(_) => Console::Pipe(stdout()),
      },
      action_map: BTreeMap::new(),
      // This is arbitrary based on a guesstimated peak terminal row size for modern displays.
      // The reason this can't be capped to e.g. the starting size is because of resizing - we
      // want to be able to fill the entire screen if resized much larger than when we started.
      logs: VecDeque::with_capacity(500),
      printable_msgs: VecDeque::with_capacity(500),
      running: false,
      // N.B. This will cause the screen to clear - but with some improved position
      // tracking logic we could avoid screen clearing in favor of using the value
      // of `EngineDisplay::get_cursor_pos()` as initialization here. From there, the
      // trick would be to push the on-screen text screen up (ideally, prior to entering
      // raw mode) via newline printing - and then ensuring the rendered text never
      // overflows past the screen row bounds. Before we could safely do that tho, we
      // probably want to plumb a signal handler for SIGWINCH so that ^L works to redraw
      // the screen in the case of any display issues. Otherwise, simply clearing the screen
      // as we've done here is the safest way to avoid terminal oddness.
      cursor_start: (1, 1),
      terminal_size: EngineDisplay::get_size(),
    };

    display.stop_raw_mode().unwrap();
    display
  }

  pub fn initialize(&mut self, display_worker_count: usize) {
    let worker_ids: Vec<String> = (0..display_worker_count)
      .map(|s| format!("{}", s))
      .collect();
    for worker_id in worker_ids {
      self.add_worker(worker_id);
    }
  }

  pub fn stdout_is_tty() -> bool {
    termion::is_tty(&stdout())
  }

  pub fn write_stdout(&mut self, msg: &str) {
    self.printable_msgs.push_back(PrintableMsg {
      msg: msg.to_string(),
      output: PrintableMsgOutput::Stdout,
    });
  }

  pub fn write_stderr(&mut self, msg: &str) {
    self.printable_msgs.push_back(PrintableMsg {
      msg: msg.to_string(),
      output: PrintableMsgOutput::Stderr,
    });
  }

  fn stop_raw_mode(&mut self) -> Result<()> {
    match self.terminal {
      Console::Terminal(ref mut t) => t.suspend_raw_mode(),
      _ => Ok(()),
    }
  }

  fn start_raw_mode(&mut self) -> Result<()> {
    match self.terminal {
      Console::Terminal(ref mut t) => t.activate_raw_mode(),
      _ => Ok(()),
    }
  }

  // Gets the current terminal's cursor position, if applicable.
  fn get_cursor_pos(&mut self) -> (u16, u16) {
    match self.terminal {
      // N.B. Real TTY coordinates start at (1, 1).
      Console::Terminal(ref mut t) => t.cursor_pos().unwrap_or((0, 0)),
      Console::Pipe(_) => (0, 0),
    }
  }

  // Gets the current terminal's width and height, if applicable.
  fn get_size() -> (u16, u16) {
    termion::terminal_size().unwrap_or((0, 0))
  }

  // Whether or not the EngineDisplay is running (whether .start() has been called).
  pub fn is_running(&self) -> bool {
    self.running
  }

  // Sets the terminal size per-render for signal-free resize detection.
  fn set_size(&mut self) {
    self.terminal_size = EngineDisplay::get_size();
  }

  // Sets the terminal size for signal-free resize detection.
  fn get_max_log_rows(&self) -> usize {
    // TODO: If the terminal size is smaller than the action map, we should fall back
    // to non-tty mode output to avoid.
    self.terminal_size.1 as usize - self.action_map.len() - 1
  }

  // Prep the screen for painting by clearing it from the cursor start position.
  fn clear(&mut self) {
    let cursor_start = self.cursor_start;
    self
      .write(&format!(
        "{goto_origin}{clear}",
        goto_origin = cursor::Goto(cursor_start.0, cursor_start.1),
        clear = clear::AfterCursor,
      ))
      .expect("could not write to terminal");
  }

  // Flush terminal output.
  fn flush(&mut self) -> Result<()> {
    match self.terminal {
      Console::Terminal(ref mut t) => t.flush(),
      Console::Pipe(ref mut p) => p.flush(),
    }
  }

  // Writes output to the terminal.
  fn write(&mut self, msg: &str) -> Result<usize> {
    let res = match self.terminal {
      Console::Terminal(ref mut t) => t.write(msg.as_bytes()),
      Console::Pipe(ref mut p) => p.write(msg.as_bytes()),
    };
    self.flush()?;
    res
  }

  // Renders a divider between the logs and action output.
  fn render_divider(&mut self, offset: u16) {
    let cursor_start = self.cursor_start;
    let padding = self.padding.clone();
    let divider = self.divider.clone();

    self
      .write(&format!(
        "{pos}{clear_line}{padding}{blue}{divider}{reset}",
        pos = cursor::Goto(1, cursor_start.1 + offset),
        clear_line = clear::CurrentLine,
        padding = padding,
        blue = color::Fg(color::Blue),
        divider = divider,
        reset = color::Fg(color::Reset)
      ))
      .expect("could not write to terminal");
  }

  // Renders one frame of the log portion of the screen.
  fn render_logs(&mut self, max_entries: usize) -> usize {
    let cursor_start = self.cursor_start;
    let printable_logs: Vec<String> = self.logs.iter().take(max_entries).cloned().collect();

    let mut counter: usize = 0;
    for (n, log_entry) in printable_logs.iter().rev().enumerate() {
      counter += 1;
      let line_shortened_log_entry: String = format!(
        "{padding}{log_entry}",
        padding = self.padding,
        log_entry = log_entry
      )
      .graphemes(true)
      .take(self.terminal_size.0 as usize)
      .collect();

      self
        .write(&format!(
          "{pos}{clear_line}{entry}",
          pos = cursor::Goto(1, cursor_start.1 + n as u16),
          clear_line = clear::CurrentLine,
          entry = line_shortened_log_entry
        ))
        .expect("could not write to terminal");
    }

    if counter > 0 {
      self.render_divider(counter as u16);
      counter += 1;
    }
    counter
  }

  // Renders one frame of the action portion of the screen.
  fn render_actions(&mut self, start_row: usize) {
    let cursor_start = self.cursor_start;
    let worker_states = self.action_map.clone();

    // For every active worker in the action map, jump to the exact cursor
    // representing the swimlane for this worker and lay down a text label.
    for (n, (_worker_id, action)) in worker_states.iter().enumerate() {
      let line_shortened_output: String = format!(
        "{padding}{blue}{sigil}{reset}{action}",
        padding = self.padding,
        blue = color::Fg(color::LightBlue),
        sigil = self.sigil,
        reset = color::Fg(color::Reset),
        action = action
      )
      .graphemes(true)
      // Account for control characters.
      .take(self.terminal_size.0 as usize + 14)
      .collect();

      self
        .write(&format!(
          "{pos}{entry}",
          pos = cursor::Goto(1, cursor_start.1 + start_row as u16 + n as u16),
          entry = line_shortened_output
        ))
        .expect("could not write to terminal");
    }
  }

  // Paints one screen of rendering.
  pub fn render(&mut self) {
    self.set_size();
    self.clear();
    let max_log_rows = self.get_max_log_rows();
    let rendered_count = self.render_logs(max_log_rows);
    self.render_actions(rendered_count);
    self.flush().expect("could not flush terminal!");
  }

  // Starts the EngineDisplay at the current cursor position.
  pub fn start(&mut self) {
    self.running = true;
    self.start_raw_mode().unwrap();
    let cursor_start = self.cursor_start;
    self
      .write(&format!(
        "{hide_cursor}{cursor_init}{clear_after_cursor}",
        hide_cursor = termion::cursor::Hide,
        cursor_init = cursor::Goto(cursor_start.0, cursor_start.1),
        clear_after_cursor = clear::AfterCursor
      ))
      .expect("could not write to terminal");
  }

  // Adds a worker/thread to the visual representation.
  pub fn add_worker(&mut self, worker_name: String) {
    let action_msg = format!("booting {}", worker_name);
    self.update(worker_name, action_msg);
  }

  // Updates the status of a worker/thread.
  pub fn update(&mut self, worker_name: String, action: String) {
    self.action_map.insert(worker_name, action);
  }

  // Removes a worker/thread from the visual representation.
  pub fn remove_worker(&mut self, worker_id: &str) {
    self.action_map.remove(worker_id);
  }

  // Adds a log entry for display.
  pub fn log(&mut self, log_entry: String) {
    self.logs.push_front(log_entry)
  }

  pub fn worker_count(&self) -> usize {
    self.action_map.len()
  }

  // Initiates one last screen render, then terminates the EngineDisplay and returns the cursor
  // to a static position, then prints all buffered stdout/stderr output.
  pub fn finish(&mut self) {
    self.render();
    self.running = false;
    let current_pos = self.get_cursor_pos();
    let action_count = self.action_map.len() as u16;
    self
      .write(&format!(
        "{park_cursor}{clear_after_cursor}{reveal_cursor}",
        park_cursor = cursor::Goto(1, current_pos.1 - action_count),
        clear_after_cursor = clear::AfterCursor,
        reveal_cursor = termion::cursor::Show
      ))
      .expect("could not write to terminal");
    self.stop_raw_mode().unwrap();
    for PrintableMsg { msg, output } in self.printable_msgs.iter() {
      match output {
        PrintableMsgOutput::Stdout => print!("{}", msg),
        PrintableMsgOutput::Stderr => eprint!("{}", msg),
      }
    }
  }
}
<commit_msg>Fix terminal display with v2 UI (#8821)<commit_after>// Copyright 2018 Pants project contributors (see CONTRIBUTORS.md).
// Licensed under the Apache License, Version 2.0 (see LICENSE).

#![deny(warnings)]
// Enable all clippy lints except for many of the pedantic ones. It's a shame this needs to be copied and pasted across crates, but there doesn't appear to be a way to include inner attributes from a common source.
#![deny(
  clippy::all,
  clippy::default_trait_access,
  clippy::expl_impl_clone_on_copy,
  clippy::if_not_else,
  clippy::needless_continue,
  clippy::single_match_else,
  clippy::unseparated_literal_suffix,
  clippy::used_underscore_binding
)]
// It is often more clear to show that nothing is being moved.
#![allow(clippy::match_ref_pats)]
// Subjective style.
#![allow(
  clippy::len_without_is_empty,
  clippy::redundant_field_names,
  clippy::too_many_arguments
)]
// Default isn't as big a deal as people seem to think it is.
#![allow(clippy::new_without_default, clippy::new_ret_no_self)]
// Arc<Mutex> can be more clear than needing to grok Orderings:
#![allow(clippy::mutex_atomic)]

use termion;

use std::collections::{BTreeMap, VecDeque};
use std::io::Write;
use std::io::{stdout, Result, Stdout};

use termion::raw::IntoRawMode;
use termion::raw::RawTerminal;
use termion::screen::AlternateScreen;
use termion::{clear, color, cursor};
use unicode_segmentation::UnicodeSegmentation;

enum Console {
  Uninitialized,
  Terminal(RawTerminal<AlternateScreen<Stdout>>),
  Pipe(Stdout),
}

#[derive(Clone)]
struct PrintableMsg {
  msg: String,
  output: PrintableMsgOutput,
}

#[derive(Clone)]
enum PrintableMsgOutput {
  Stdout,
  Stderr,
}

pub struct EngineDisplay {
  sigil: char,
  divider: String,
  padding: String,
  terminal: Console,
  action_map: BTreeMap<String, String>,
  logs: VecDeque<String>,
  printable_msgs: VecDeque<PrintableMsg>,
  running: bool,
  cursor_start: (u16, u16),
  terminal_size: (u16, u16),
}

// TODO: Prescribe a threading/polling strategy for callers - or implement a built-in one.
// TODO: Better error handling for .flush() and .write() failure modes.
// TODO: Permit scrollback in the terminal - both at exit and during the live run.
impl EngineDisplay {
  /// Create a new EngineDisplay
  pub fn new(indent_level: u16) -> EngineDisplay {
    EngineDisplay {
      sigil: '⚡',
      divider: "▵".to_string(),
      padding: " ".repeat(indent_level.into()),
      terminal: Console::Uninitialized,
      action_map: BTreeMap::new(),
      // This is arbitrary based on a guesstimated peak terminal row size for modern displays.
      // The reason this can't be capped to e.g. the starting size is because of resizing - we
      // want to be able to fill the entire screen if resized much larger than when we started.
      logs: VecDeque::with_capacity(500),
      printable_msgs: VecDeque::with_capacity(500),
      running: false,
      // N.B. This will cause the screen to clear - but with some improved position
      // tracking logic we could avoid screen clearing in favor of using the value
      // of `EngineDisplay::get_cursor_pos()` as initialization here. From there, the
      // trick would be to push the on-screen text screen up (ideally, prior to entering
      // raw mode) via newline printing - and then ensuring the rendered text never
      // overflows past the screen row bounds. Before we could safely do that tho, we
      // probably want to plumb a signal handler for SIGWINCH so that ^L works to redraw
      // the screen in the case of any display issues. Otherwise, simply clearing the screen
      // as we've done here is the safest way to avoid terminal oddness.
      cursor_start: (1, 1),
      terminal_size: EngineDisplay::get_size(),
    }
  }

  pub fn initialize(&mut self, display_worker_count: usize) {
    let worker_ids: Vec<String> = (0..display_worker_count)
      .map(|s| format!("{}", s))
      .collect();
    for worker_id in worker_ids {
      self.add_worker(worker_id);
    }
  }

  pub fn stdout_is_tty() -> bool {
    termion::is_tty(&stdout())
  }

  pub fn write_stdout(&mut self, msg: &str) {
    self.printable_msgs.push_back(PrintableMsg {
      msg: msg.to_string(),
      output: PrintableMsgOutput::Stdout,
    });
  }

  pub fn write_stderr(&mut self, msg: &str) {
    self.printable_msgs.push_back(PrintableMsg {
      msg: msg.to_string(),
      output: PrintableMsgOutput::Stderr,
    });
  }

  fn start_raw_mode(&mut self) -> Result<()> {
    match self.terminal {
      Console::Terminal(ref mut t) => t.activate_raw_mode(),
      _ => Ok(()),
    }
  }

  // Gets the current terminal's width and height, if applicable.
  fn get_size() -> (u16, u16) {
    termion::terminal_size().unwrap_or((0, 0))
  }

  // Whether or not the EngineDisplay is running (whether .start() has been called).
  pub fn is_running(&self) -> bool {
    self.running
  }

  // Sets the terminal size per-render for signal-free resize detection.
  fn set_size(&mut self) {
    self.terminal_size = EngineDisplay::get_size();
  }

  // Sets the terminal size for signal-free resize detection.
  fn get_max_log_rows(&self) -> usize {
    // TODO: If the terminal size is smaller than the action map, we should fall back
    // to non-tty mode output to avoid.
    self.terminal_size.1 as usize - self.action_map.len() - 1
  }

  // Prep the screen for painting by clearing it from the cursor start position.
  fn clear(&mut self) {
    let cursor_start = self.cursor_start;
    self
      .write(&format!(
        "{goto_origin}{clear}",
        goto_origin = cursor::Goto(cursor_start.0, cursor_start.1),
        clear = clear::AfterCursor,
      ))
      .expect("could not write to terminal");
  }

  // Flush terminal output.
  fn flush(&mut self) -> Result<()> {
    match self.terminal {
      Console::Terminal(ref mut t) => t.flush(),
      Console::Pipe(ref mut p) => p.flush(),
      Console::Uninitialized => Ok(()),
    }
  }

  // Writes output to the terminal.
  fn write(&mut self, msg: &str) -> Result<usize> {
    let res = match self.terminal {
      Console::Terminal(ref mut t) => t.write(msg.as_bytes()),
      Console::Pipe(ref mut p) => p.write(msg.as_bytes()),
      Console::Uninitialized => Ok(0),
    };
    self.flush()?;
    res
  }

  // Renders a divider between the logs and action output.
  fn render_divider(&mut self, offset: u16) {
    let cursor_start = self.cursor_start;
    let padding = self.padding.clone();
    let divider = self.divider.clone();

    self
      .write(&format!(
        "{pos}{clear_line}{padding}{blue}{divider}{reset}",
        pos = cursor::Goto(1, cursor_start.1 + offset),
        clear_line = clear::CurrentLine,
        padding = padding,
        blue = color::Fg(color::Blue),
        divider = divider,
        reset = color::Fg(color::Reset)
      ))
      .expect("could not write to terminal");
  }

  // Renders one frame of the log portion of the screen.
  fn render_logs(&mut self, max_entries: usize) -> usize {
    let cursor_start = self.cursor_start;
    let printable_logs: Vec<String> = self.logs.iter().take(max_entries).cloned().collect();

    let mut counter: usize = 0;
    for (n, log_entry) in printable_logs.iter().rev().enumerate() {
      counter += 1;
      let line_shortened_log_entry: String = format!(
        "{padding}{log_entry}",
        padding = self.padding,
        log_entry = log_entry
      )
      .graphemes(true)
      .take(self.terminal_size.0 as usize)
      .collect();

      self
        .write(&format!(
          "{pos}{clear_line}{entry}",
          pos = cursor::Goto(1, cursor_start.1 + n as u16),
          clear_line = clear::CurrentLine,
          entry = line_shortened_log_entry
        ))
        .expect("could not write to terminal");
    }

    if counter > 0 {
      self.render_divider(counter as u16);
      counter += 1;
    }
    counter
  }

  // Renders one frame of the action portion of the screen.
  fn render_actions(&mut self, start_row: usize) {
    let cursor_start = self.cursor_start;
    let worker_states = self.action_map.clone();

    // For every active worker in the action map, jump to the exact cursor
    // representing the swimlane for this worker and lay down a text label.
    for (n, (_worker_id, action)) in worker_states.iter().enumerate() {
      let line_shortened_output: String = format!(
        "{padding}{blue}{sigil}{reset}{action}",
        padding = self.padding,
        blue = color::Fg(color::LightBlue),
        sigil = self.sigil,
        reset = color::Fg(color::Reset),
        action = action
      )
      .graphemes(true)
      // Account for control characters.
      .take(self.terminal_size.0 as usize + 14)
      .collect();

      self
        .write(&format!(
          "{pos}{entry}",
          pos = cursor::Goto(1, cursor_start.1 + start_row as u16 + n as u16),
          entry = line_shortened_output
        ))
        .expect("could not write to terminal");
    }
  }

  // Paints one screen of rendering.
  pub fn render(&mut self) {
    self.set_size();
    self.clear();
    let max_log_rows = self.get_max_log_rows();
    let rendered_count = self.render_logs(max_log_rows);
    self.render_actions(rendered_count);
    self.flush().expect("could not flush terminal!");
  }

  // Starts the EngineDisplay at the current cursor position.
  pub fn start(&mut self) {
    let write_handle = termion::screen::AlternateScreen::from(stdout());
    self.running = true;
    self.terminal = match write_handle.into_raw_mode() {
      Ok(t) => Console::Terminal(t),
      Err(_) => Console::Pipe(stdout()),
    };

    self.start_raw_mode().unwrap();
    let cursor_start = self.cursor_start;
    self
      .write(&format!(
        "{hide_cursor}{cursor_init}{clear_after_cursor}",
        hide_cursor = termion::cursor::Hide,
        cursor_init = cursor::Goto(cursor_start.0, cursor_start.1),
        clear_after_cursor = clear::AfterCursor
      ))
      .expect("could not write to terminal");
  }

  // Adds a worker/thread to the visual representation.
  pub fn add_worker(&mut self, worker_name: String) {
    let action_msg = format!("booting {}", worker_name);
    self.update(worker_name, action_msg);
  }

  // Updates the status of a worker/thread.
  pub fn update(&mut self, worker_name: String, action: String) {
    self.action_map.insert(worker_name, action);
  }

  // Removes a worker/thread from the visual representation.
  pub fn remove_worker(&mut self, worker_id: &str) {
    self.action_map.remove(worker_id);
  }

  // Adds a log entry for display.
  pub fn log(&mut self, log_entry: String) {
    self.logs.push_front(log_entry)
  }

  pub fn worker_count(&self) -> usize {
    self.action_map.len()
  }

  // Initiates one last screen render, then terminates the EngineDisplay and returns the cursor
  // to a static position, then prints all buffered stdout/stderr output.
  pub fn finish(&mut self) {
    self.render();
    self.running = false;
    {
      self.terminal = Console::Uninitialized; //This forces the AlternateScreen to drop, restoring the original terminal state.
    }

    println!("{}", termion::cursor::Show);
    for log in self.logs.iter() {
      println!("{}", log);
    }
    for PrintableMsg { msg, output } in self.printable_msgs.iter() {
      match output {
        PrintableMsgOutput::Stdout => print!("{}", msg),
        PrintableMsgOutput::Stderr => eprint!("{}", msg),
      }
    }
  }
}
<|endoftext|>