<commit_before>// Copyright 2013-2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Interfaces to the operating system provided random number
//! generators.

pub use self::imp::OsRng;

#[cfg(all(unix, not(target_os = "ios"),
          not(target_os = "nacl")))]
mod imp {
    extern crate libc;

    use self::OsRngInner::*;

    use std::io;
    use std::fs::File;
    use Rng;
    use read::ReadRng;
    use std::mem;

    #[cfg(all(target_os = "linux",
              any(target_arch = "x86_64",
                  target_arch = "x86",
                  target_arch = "arm",
                  target_arch = "aarch64",
                  target_arch = "powerpc")))]
    fn getrandom(buf: &mut [u8]) -> libc::c_long {
        extern "C" {
            fn syscall(number: libc::c_long, ...) -> libc::c_long;
        }

        #[cfg(target_arch = "x86_64")]
        const NR_GETRANDOM: libc::c_long = 318;
        #[cfg(target_arch = "x86")]
        const NR_GETRANDOM: libc::c_long = 355;
        #[cfg(target_arch = "arm")]
        const NR_GETRANDOM: libc::c_long = 384;
        #[cfg(target_arch = "aarch64")]
        const NR_GETRANDOM: libc::c_long = 278;
        #[cfg(target_arch = "powerpc")]
        const NR_GETRANDOM: libc::c_long = 384;

        unsafe {
            syscall(NR_GETRANDOM, buf.as_mut_ptr(), buf.len(), 0)
        }
    }

    #[cfg(not(all(target_os = "linux",
                  any(target_arch = "x86_64",
                      target_arch = "x86",
                      target_arch = "arm",
                      target_arch = "aarch64",
                      target_arch = "powerpc"))))]
    fn getrandom(_buf: &mut [u8]) -> libc::c_long { -1 }

    fn getrandom_fill_bytes(v: &mut [u8]) {
        let mut read = 0;
        let len = v.len();
        while read < len {
            let result = getrandom(&mut v[read..]);
            if result == -1 {
                let err = io::Error::last_os_error();
                if err.kind() == io::ErrorKind::Interrupted {
                    continue
                } else {
                    panic!("unexpected getrandom error: {}", err);
                }
            } else {
                read += result as usize;
            }
        }
    }

    fn getrandom_next_u32() -> u32 {
        let mut buf: [u8; 4] = [0u8; 4];
        getrandom_fill_bytes(&mut buf);
        unsafe { mem::transmute::<[u8; 4], u32>(buf) }
    }

    fn getrandom_next_u64() -> u64 {
        let mut buf: [u8; 8] = [0u8; 8];
        getrandom_fill_bytes(&mut buf);
        unsafe { mem::transmute::<[u8; 8], u64>(buf) }
    }

    #[cfg(all(target_os = "linux",
              any(target_arch = "x86_64",
                  target_arch = "x86",
                  target_arch = "arm",
                  target_arch = "aarch64",
                  target_arch = "powerpc")))]
    fn is_getrandom_available() -> bool {
        use std::sync::atomic::{AtomicBool, ATOMIC_BOOL_INIT, Ordering};
        use std::sync::{Once, ONCE_INIT};

        static CHECKER: Once = ONCE_INIT;
        static AVAILABLE: AtomicBool = ATOMIC_BOOL_INIT;

        CHECKER.call_once(|| {
            let mut buf: [u8; 0] = [];
            let result = getrandom(&mut buf);
            let available = if result == -1 {
                let err = io::Error::last_os_error().raw_os_error();
                err != Some(libc::ENOSYS)
            } else {
                true
            };
            AVAILABLE.store(available, Ordering::Relaxed);
        });

        AVAILABLE.load(Ordering::Relaxed)
    }

    #[cfg(not(all(target_os = "linux",
                  any(target_arch = "x86_64",
                      target_arch = "x86",
                      target_arch = "arm",
                      target_arch = "aarch64",
                      target_arch = "powerpc"))))]
    fn is_getrandom_available() -> bool { false }

    /// A random number generator that retrieves randomness straight from
    /// the operating system. Platform sources:
    ///
    /// - Unix-like systems (Linux, Android, Mac OSX): read directly from
    ///   `/dev/urandom`, or from `getrandom(2)` system call if available.
    /// - Windows: calls `CryptGenRandom`, using the default cryptographic
    ///   service provider with the `PROV_RSA_FULL` type.
    /// - iOS: calls SecRandomCopyBytes as /dev/(u)random is sandboxed.
    /// - PNaCl: calls into the `nacl-irt-random-0.1` IRT interface.
    ///
    /// This does not block.
    pub struct OsRng {
        inner: OsRngInner,
    }

    enum OsRngInner {
        OsGetrandomRng,
        OsReadRng(ReadRng<File>),
    }

    impl OsRng {
        /// Create a new `OsRng`.
        pub fn new() -> io::Result<OsRng> {
            if is_getrandom_available() {
                return Ok(OsRng { inner: OsGetrandomRng });
            }

            let reader = try!(File::open("/dev/urandom"));
            let reader_rng = ReadRng::new(reader);

            Ok(OsRng { inner: OsReadRng(reader_rng) })
        }
    }

    impl Rng for OsRng {
        fn next_u32(&mut self) -> u32 {
            match self.inner {
                OsGetrandomRng => getrandom_next_u32(),
                OsReadRng(ref mut rng) => rng.next_u32(),
            }
        }
        fn next_u64(&mut self) -> u64 {
            match self.inner {
                OsGetrandomRng => getrandom_next_u64(),
                OsReadRng(ref mut rng) => rng.next_u64(),
            }
        }
        fn fill_bytes(&mut self, v: &mut [u8]) {
            match self.inner {
                OsGetrandomRng => getrandom_fill_bytes(v),
                OsReadRng(ref mut rng) => rng.fill_bytes(v)
            }
        }
    }
}

#[cfg(target_os = "ios")]
mod imp {
    extern crate libc;

    use std::io;
    use std::mem;
    use Rng;
    use self::libc::{c_int, size_t};

    /// A random number generator that retrieves randomness straight from
    /// the operating system. Platform sources:
    ///
    /// - Unix-like systems (Linux, Android, Mac OSX): read directly from
    ///   `/dev/urandom`, or from `getrandom(2)` system call if available.
    /// - Windows: calls `CryptGenRandom`, using the default cryptographic
    ///   service provider with the `PROV_RSA_FULL` type.
    /// - iOS: calls SecRandomCopyBytes as /dev/(u)random is sandboxed.
    /// - PNaCl: calls into the `nacl-irt-random-0.1` IRT interface.
    ///
    /// This does not block.
    pub struct OsRng {
        // dummy field to ensure that this struct cannot be constructed outside of this module
        _dummy: (),
    }

    #[repr(C)]
    struct SecRandom;

    #[allow(non_upper_case_globals)]
    const kSecRandomDefault: *const SecRandom = 0 as *const SecRandom;

    #[link(name = "Security", kind = "framework")]
    extern "C" {
        fn SecRandomCopyBytes(rnd: *const SecRandom,
                              count: size_t, bytes: *mut u8) -> c_int;
    }

    impl OsRng {
        /// Create a new `OsRng`.
        pub fn new() -> io::Result<OsRng> {
            Ok(OsRng { _dummy: () })
        }
    }

    impl Rng for OsRng {
        fn next_u32(&mut self) -> u32 {
            let mut v = [0u8; 4];
            self.fill_bytes(&mut v);
            unsafe { mem::transmute(v) }
        }
        fn next_u64(&mut self) -> u64 {
            let mut v = [0u8; 8];
            self.fill_bytes(&mut v);
            unsafe { mem::transmute(v) }
        }
        fn fill_bytes(&mut self, v: &mut [u8]) {
            let ret = unsafe {
                SecRandomCopyBytes(kSecRandomDefault, v.len() as size_t, v.as_mut_ptr())
            };
            if ret == -1 {
                panic!("couldn't generate random bytes: {}", io::Error::last_os_error());
            }
        }
    }
}

#[cfg(windows)]
mod imp {
    extern crate winapi;
    extern crate advapi32;

    use std::io;
    use std::mem;
    use std::ptr;
    use Rng;
    use self::winapi::{CRYPT_SILENT, CRYPT_VERIFYCONTEXT, DWORD, HCRYPTPROV, PROV_RSA_FULL};
    use self::advapi32::{CryptAcquireContextA, CryptGenRandom, CryptReleaseContext};

    /// A random number generator that retrieves randomness straight from
    /// the operating system. Platform sources:
    ///
    /// - Unix-like systems (Linux, Android, Mac OSX): read directly from
    ///   `/dev/urandom`, or from `getrandom(2)` system call if available.
    /// - Windows: calls `CryptGenRandom`, using the default cryptographic
    ///   service provider with the `PROV_RSA_FULL` type.
    /// - iOS: calls SecRandomCopyBytes as /dev/(u)random is sandboxed.
    /// - PNaCl: calls into the `nacl-irt-random-0.1` IRT interface.
    ///
    /// This does not block.
    pub struct OsRng {
        hcryptprov: HCRYPTPROV
    }

    impl OsRng {
        /// Create a new `OsRng`.
        pub fn new() -> io::Result<OsRng> {
            let mut hcp = 0;
            let ret = unsafe {
                CryptAcquireContextA(&mut hcp, ptr::null(), ptr::null(),
                                     PROV_RSA_FULL,
                                     CRYPT_VERIFYCONTEXT | CRYPT_SILENT)
            };

            if ret == 0 {
                Err(io::Error::last_os_error())
            } else {
                Ok(OsRng { hcryptprov: hcp })
            }
        }
    }

    impl Rng for OsRng {
        fn next_u32(&mut self) -> u32 {
            let mut v = [0u8; 4];
            self.fill_bytes(&mut v);
            unsafe { mem::transmute(v) }
        }
        fn next_u64(&mut self) -> u64 {
            let mut v = [0u8; 8];
            self.fill_bytes(&mut v);
            unsafe { mem::transmute(v) }
        }
        fn fill_bytes(&mut self, v: &mut [u8]) {
            let ret = unsafe {
                CryptGenRandom(self.hcryptprov, v.len() as DWORD,
                               v.as_mut_ptr())
            };
            if ret == 0 {
                panic!("couldn't generate random bytes: {}",
                       io::Error::last_os_error());
            }
        }
    }

    impl Drop for OsRng {
        fn drop(&mut self) {
            let ret = unsafe {
                CryptReleaseContext(self.hcryptprov, 0)
            };
            if ret == 0 {
                panic!("couldn't release context: {}",
                       io::Error::last_os_error());
            }
        }
    }
}

#[cfg(target_os = "nacl")]
mod imp {
    extern crate libc;

    use std::io;
    use std::mem;
    use Rng;

    /// A random number generator that retrieves randomness straight from
    /// the operating system. Platform sources:
    ///
    /// - Unix-like systems (Linux, Android, Mac OSX): read directly from
    ///   `/dev/urandom`, or from `getrandom(2)` system call if available.
    /// - Windows: calls `CryptGenRandom`, using the default cryptographic
    ///   service provider with the `PROV_RSA_FULL` type.
    /// - iOS: calls SecRandomCopyBytes as /dev/(u)random is sandboxed.
    /// - PNaCl: calls into the `nacl-irt-random-0.1` IRT interface.
    ///
    /// This does not block.
    pub struct OsRng(extern fn(dest: *mut libc::c_void,
                               bytes: libc::size_t,
                               read: *mut libc::size_t) -> libc::c_int);

    extern {
        fn nacl_interface_query(name: *const libc::c_char,
                                table: *mut libc::c_void,
                                table_size: libc::size_t) -> libc::size_t;
    }

    const INTERFACE: &'static [u8] = b"nacl-irt-random-0.1\0";

    #[repr(C)]
    struct NaClIRTRandom {
        get_random_bytes: Option<extern fn(dest: *mut libc::c_void,
                                           bytes: libc::size_t,
                                           read: *mut libc::size_t) -> libc::c_int>,
    }

    impl OsRng {
        /// Create a new `OsRng`.
        pub fn new() -> io::Result<OsRng> {
            let mut iface = NaClIRTRandom {
                get_random_bytes: None,
            };
            let result = unsafe {
                nacl_interface_query(INTERFACE.as_ptr() as *const _,
                                     mem::transmute(&mut iface),
                                     mem::size_of::<NaClIRTRandom>() as libc::size_t)
            };
            if result != 0 {
                assert!(iface.get_random_bytes.is_some());
                let result = OsRng(iface.get_random_bytes.take().unwrap());
                Ok(result)
            } else {
                let error = io::ErrorKind::NotFound;
                let error = io::Error::new(error, "IRT random interface missing");
                Err(error)
            }
        }
    }

    impl Rng for OsRng {
        fn next_u32(&mut self) -> u32 {
            let mut v = [0u8; 4];
            self.fill_bytes(&mut v);
            unsafe { mem::transmute(v) }
        }
        fn next_u64(&mut self) -> u64 {
            let mut v = [0u8; 8];
            self.fill_bytes(&mut v);
            unsafe { mem::transmute(v) }
        }
        fn fill_bytes(&mut self, v: &mut [u8]) {
            let mut read = 0;
            loop {
                let mut r: libc::size_t = 0;
                let len = v.len();
                let error = (self.0)(v[read..].as_mut_ptr() as *mut _,
                                     (len - read) as libc::size_t,
                                     &mut r as *mut _);
                assert!(error == 0, "`get_random_bytes` failed!");
                read += r as usize;

                if read >= v.len() { break; }
            }
        }
    }
}

#[cfg(test)]
mod test {
    use std::sync::mpsc::channel;
    use Rng;
    use OsRng;
    use std::thread;

    #[test]
    fn test_os_rng() {
        let mut r = OsRng::new().unwrap();

        r.next_u32();
        r.next_u64();

        let mut v = [0u8; 1000];
        r.fill_bytes(&mut v);
    }

    #[test]
    fn test_os_rng_tasks() {

        let mut txs = vec!();
        for _ in 0..20 {
            let (tx, rx) = channel();
            txs.push(tx);

            thread::spawn(move|| {
                // wait until all the tasks are ready to go.
                rx.recv().unwrap();

                // deschedule to attempt to interleave things as much
                // as possible (XXX: is this a good test?)
                let mut r = OsRng::new().unwrap();
                thread::yield_now();
                let mut v = [0u8; 1000];

                for _ in 0..100 {
                    r.next_u32();
                    thread::yield_now();
                    r.next_u64();
                    thread::yield_now();
                    r.fill_bytes(&mut v);
                    thread::yield_now();
                }
            });
        }

        // start all the tasks
        for tx in txs.iter() {
            tx.send(()).unwrap();
        }
    }
}
<commit_msg>Clean up style/docs of OsRng<commit_after>// Copyright 2013-2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Interfaces to the operating system provided random number
//! generators.

use std::io;
use Rng;

/// A random number generator that retrieves randomness straight from
/// the operating system. Platform sources:
///
/// - Unix-like systems (Linux, Android, Mac OSX): read directly from
///   `/dev/urandom`, or from `getrandom(2)` system call if available.
/// - Windows: calls `CryptGenRandom`, using the default cryptographic
///   service provider with the `PROV_RSA_FULL` type.
/// - iOS: calls SecRandomCopyBytes as /dev/(u)random is sandboxed.
/// - PNaCl: calls into the `nacl-irt-random-0.1` IRT interface.
///
/// This does not block.
pub struct OsRng(imp::OsRng);

impl OsRng {
    /// Create a new `OsRng`.
    pub fn new() -> io::Result<OsRng> {
        imp::OsRng::new().map(OsRng)
    }
}

impl Rng for OsRng {
    fn next_u32(&mut self) -> u32 { self.0.next_u32() }
    fn next_u64(&mut self) -> u64 { self.0.next_u64() }
    fn fill_bytes(&mut self, v: &mut [u8]) { self.0.fill_bytes(v) }
}

#[cfg(all(unix, not(target_os = "ios"),
          not(target_os = "nacl")))]
mod imp {
    extern crate libc;

    use self::OsRngInner::*;

    use std::io;
    use std::fs::File;
    use Rng;
    use read::ReadRng;
    use std::mem;

    #[cfg(all(target_os = "linux",
              any(target_arch = "x86_64",
                  target_arch = "x86",
                  target_arch = "arm",
                  target_arch = "aarch64",
                  target_arch = "powerpc")))]
    fn getrandom(buf: &mut [u8]) -> libc::c_long {
        extern "C" {
            fn syscall(number: libc::c_long, ...) -> libc::c_long;
        }

        #[cfg(target_arch = "x86_64")]
        const NR_GETRANDOM: libc::c_long = 318;
        #[cfg(target_arch = "x86")]
        const NR_GETRANDOM: libc::c_long = 355;
        #[cfg(target_arch = "arm")]
        const NR_GETRANDOM: libc::c_long = 384;
        #[cfg(target_arch = "aarch64")]
        const NR_GETRANDOM: libc::c_long = 278;
        #[cfg(target_arch = "powerpc")]
        const NR_GETRANDOM: libc::c_long = 384;

        unsafe {
            syscall(NR_GETRANDOM, buf.as_mut_ptr(), buf.len(), 0)
        }
    }

    #[cfg(not(all(target_os = "linux",
                  any(target_arch = "x86_64",
                      target_arch = "x86",
                      target_arch = "arm",
                      target_arch = "aarch64",
                      target_arch = "powerpc"))))]
    fn getrandom(_buf: &mut [u8]) -> libc::c_long { -1 }

    fn getrandom_fill_bytes(v: &mut [u8]) {
        let mut read = 0;
        let len = v.len();
        while read < len {
            let result = getrandom(&mut v[read..]);
            if result == -1 {
                let err = io::Error::last_os_error();
                if err.kind() == io::ErrorKind::Interrupted {
                    continue
                } else {
                    panic!("unexpected getrandom error: {}", err);
                }
            } else {
                read += result as usize;
            }
        }
    }

    fn getrandom_next_u32() -> u32 {
        let mut buf: [u8; 4] = [0u8; 4];
        getrandom_fill_bytes(&mut buf);
        unsafe { mem::transmute::<[u8; 4], u32>(buf) }
    }

    fn getrandom_next_u64() -> u64 {
        let mut buf: [u8; 8] = [0u8; 8];
        getrandom_fill_bytes(&mut buf);
        unsafe { mem::transmute::<[u8; 8], u64>(buf) }
    }

    #[cfg(all(target_os = "linux",
              any(target_arch = "x86_64",
                  target_arch = "x86",
                  target_arch = "arm",
                  target_arch = "aarch64",
                  target_arch = "powerpc")))]
    fn is_getrandom_available() -> bool {
        use std::sync::atomic::{AtomicBool, ATOMIC_BOOL_INIT, Ordering};
        use std::sync::{Once, ONCE_INIT};

        static CHECKER: Once = ONCE_INIT;
        static AVAILABLE: AtomicBool = ATOMIC_BOOL_INIT;

        CHECKER.call_once(|| {
            let mut buf: [u8; 0] = [];
            let result = getrandom(&mut buf);
            let available = if result == -1 {
                let err = io::Error::last_os_error().raw_os_error();
                err != Some(libc::ENOSYS)
            } else {
                true
            };
            AVAILABLE.store(available, Ordering::Relaxed);
        });

        AVAILABLE.load(Ordering::Relaxed)
    }

    #[cfg(not(all(target_os = "linux",
                  any(target_arch = "x86_64",
                      target_arch = "x86",
                      target_arch = "arm",
                      target_arch = "aarch64",
                      target_arch = "powerpc"))))]
    fn is_getrandom_available() -> bool { false }

    pub struct OsRng {
        inner: OsRngInner,
    }

    enum OsRngInner {
        OsGetrandomRng,
        OsReadRng(ReadRng<File>),
    }

    impl OsRng {
        pub fn new() -> io::Result<OsRng> {
            if is_getrandom_available() {
                return Ok(OsRng { inner: OsGetrandomRng });
            }

            let reader = try!(File::open("/dev/urandom"));
            let reader_rng = ReadRng::new(reader);

            Ok(OsRng { inner: OsReadRng(reader_rng) })
        }
    }

    impl Rng for OsRng {
        fn next_u32(&mut self) -> u32 {
            match self.inner {
                OsGetrandomRng => getrandom_next_u32(),
                OsReadRng(ref mut rng) => rng.next_u32(),
            }
        }
        fn next_u64(&mut self) -> u64 {
            match self.inner {
                OsGetrandomRng => getrandom_next_u64(),
                OsReadRng(ref mut rng) => rng.next_u64(),
            }
        }
        fn fill_bytes(&mut self, v: &mut [u8]) {
            match self.inner {
                OsGetrandomRng => getrandom_fill_bytes(v),
                OsReadRng(ref mut rng) => rng.fill_bytes(v)
            }
        }
    }
}

#[cfg(target_os = "ios")]
mod imp {
    extern crate libc;

    use std::io;
    use std::mem;
    use Rng;
    use self::libc::{c_int, size_t};

    pub struct OsRng;

    enum SecRandom {}

    #[allow(non_upper_case_globals)]
    const kSecRandomDefault: *const SecRandom = 0 as *const SecRandom;

    #[link(name = "Security", kind = "framework")]
    extern {
        fn SecRandomCopyBytes(rnd: *const SecRandom,
                              count: size_t, bytes: *mut u8) -> c_int;
    }

    impl OsRng {
        pub fn new() -> io::Result<OsRng> {
            Ok(OsRng)
        }
    }

    impl Rng for OsRng {
        fn next_u32(&mut self) -> u32 {
            let mut v = [0u8; 4];
            self.fill_bytes(&mut v);
            unsafe { mem::transmute(v) }
        }
        fn next_u64(&mut self) -> u64 {
            let mut v = [0u8; 8];
            self.fill_bytes(&mut v);
            unsafe { mem::transmute(v) }
        }
        fn fill_bytes(&mut self, v: &mut [u8]) {
            let ret = unsafe {
                SecRandomCopyBytes(kSecRandomDefault, v.len() as size_t, v.as_mut_ptr())
            };
            if ret == -1 {
                panic!("couldn't generate random bytes: {}", io::Error::last_os_error());
            }
        }
    }
}

#[cfg(windows)]
mod imp {
    extern crate winapi;
    extern crate advapi32;

    use std::io;
    use std::mem;
    use std::ptr;
    use Rng;
    use self::winapi::{CRYPT_SILENT, CRYPT_VERIFYCONTEXT, DWORD, HCRYPTPROV, PROV_RSA_FULL};
    use self::advapi32::{CryptAcquireContextA, CryptGenRandom, CryptReleaseContext};

    pub struct OsRng {
        hcryptprov: HCRYPTPROV
    }

    impl OsRng {
        pub fn new() -> io::Result<OsRng> {
            let mut hcp = 0;
            let ret = unsafe {
                CryptAcquireContextA(&mut hcp, ptr::null(), ptr::null(),
                                     PROV_RSA_FULL,
                                     CRYPT_VERIFYCONTEXT | CRYPT_SILENT)
            };

            if ret == 0 {
                Err(io::Error::last_os_error())
            } else {
                Ok(OsRng { hcryptprov: hcp })
            }
        }
    }

    impl Rng for OsRng {
        fn next_u32(&mut self) -> u32 {
            let mut v = [0u8; 4];
            self.fill_bytes(&mut v);
            unsafe { mem::transmute(v) }
        }
        fn next_u64(&mut self) -> u64 {
            let mut v = [0u8; 8];
            self.fill_bytes(&mut v);
            unsafe { mem::transmute(v) }
        }
        fn fill_bytes(&mut self, v: &mut [u8]) {
            let ret = unsafe {
                CryptGenRandom(self.hcryptprov, v.len() as DWORD,
                               v.as_mut_ptr())
            };
            if ret == 0 {
                panic!("couldn't generate random bytes: {}",
                       io::Error::last_os_error());
            }
        }
    }

    impl Drop for OsRng {
        fn drop(&mut self) {
            let ret = unsafe {
                CryptReleaseContext(self.hcryptprov, 0)
            };
            if ret == 0 {
                panic!("couldn't release context: {}",
                       io::Error::last_os_error());
            }
        }
    }
}

#[cfg(target_os = "nacl")]
mod imp {
    extern crate libc;

    use std::io;
    use std::mem;
    use Rng;

    pub struct OsRng(extern fn(dest: *mut libc::c_void,
                               bytes: libc::size_t,
                               read: *mut libc::size_t) -> libc::c_int);

    extern {
        fn nacl_interface_query(name: *const libc::c_char,
                                table: *mut libc::c_void,
                                table_size: libc::size_t) -> libc::size_t;
    }

    const INTERFACE: &'static [u8] = b"nacl-irt-random-0.1\0";

    #[repr(C)]
    struct NaClIRTRandom {
        get_random_bytes: Option<extern fn(dest: *mut libc::c_void,
                                           bytes: libc::size_t,
                                           read: *mut libc::size_t) -> libc::c_int>,
    }

    impl OsRng {
        pub fn new() -> io::Result<OsRng> {
            let mut iface = NaClIRTRandom {
                get_random_bytes: None,
            };
            let result = unsafe {
                nacl_interface_query(INTERFACE.as_ptr() as *const _,
                                     mem::transmute(&mut iface),
                                     mem::size_of::<NaClIRTRandom>() as libc::size_t)
            };
            if result != 0 {
                assert!(iface.get_random_bytes.is_some());
                let result = OsRng(iface.get_random_bytes.take().unwrap());
                Ok(result)
            } else {
                let error = io::ErrorKind::NotFound;
                let error = io::Error::new(error, "IRT random interface missing");
                Err(error)
            }
        }
    }

    impl Rng for OsRng {
        fn next_u32(&mut self) -> u32 {
            let mut v = [0u8; 4];
            self.fill_bytes(&mut v);
            unsafe { mem::transmute(v) }
        }
        fn next_u64(&mut self) -> u64 {
            let mut v = [0u8; 8];
            self.fill_bytes(&mut v);
            unsafe { mem::transmute(v) }
        }
        fn fill_bytes(&mut self, v: &mut [u8]) {
            let mut read = 0;
            loop {
                let mut r: libc::size_t = 0;
                let len = v.len();
                let error = (self.0)(v[read..].as_mut_ptr() as *mut _,
                                     (len - read) as libc::size_t,
                                     &mut r as *mut _);
                assert!(error == 0, "`get_random_bytes` failed!");
                read += r as usize;

                if read >= v.len() { break; }
            }
        }
    }
}

#[cfg(test)]
mod test {
    use std::sync::mpsc::channel;
    use Rng;
    use OsRng;
    use std::thread;

    #[test]
    fn test_os_rng() {
        let mut r = OsRng::new().unwrap();

        r.next_u32();
        r.next_u64();

        let mut v = [0u8; 1000];
        r.fill_bytes(&mut v);
    }

    #[test]
    fn test_os_rng_tasks() {

        let mut txs = vec!();
        for _ in 0..20 {
            let (tx, rx) = channel();
            txs.push(tx);

            thread::spawn(move|| {
                // wait until all the tasks are ready to go.
                rx.recv().unwrap();

                // deschedule to attempt to interleave things as much
                // as possible (XXX: is this a good test?)
                let mut r = OsRng::new().unwrap();
                thread::yield_now();
                let mut v = [0u8; 1000];

                for _ in 0..100 {
                    r.next_u32();
                    thread::yield_now();
                    r.next_u64();
                    thread::yield_now();
                    r.fill_bytes(&mut v);
                    thread::yield_now();
                }
            });
        }

        // start all the tasks
        for tx in txs.iter() {
            tx.send(()).unwrap();
        }
    }
}
<|endoftext|>


<commit_before>use crate::grammar::ast::{Parens, Expression};
use crate::grammar::model::{HasFragment, Fragment};

impl<'s> Parens<'s> {
    fn new(frag: Fragment<'s>, inner: Box<Expression<'s>>) -> Self {
        Self {frag, inner}
    }

}

impl<'s> HasFragment<'s> for Parens<'s> {
    fn get_fragment(&self) -> Fragment<'s> {self.frag}
}<commit_msg>whitespace commit<commit_after>use crate::grammar::ast::{Parens, Expression};
use crate::grammar::model::{HasFragment, Fragment};

impl<'s> Parens<'s> {
    fn new(frag: Fragment<'s>, inner: Box<Expression<'s>>) -> Self {
        Self {frag, inner}
    }
}

impl<'s> HasFragment<'s> for Parens<'s> {
    fn get_fragment(&self) -> Fragment<'s> {self.frag}
}<|endoftext|>


<commit_before>use std::{io, io::prelude::Write};

use super::OutputFormatter;
use crate::{
    bench::fmt_bench_samples,
    console::{ConsoleTestState, OutputLocation},
    test_result::TestResult,
    time,
    types::TestDesc,
};

pub(crate) struct PrettyFormatter<T> {
    out: OutputLocation<T>,
    use_color: bool,
    time_options: Option<time::TestTimeOptions>,

    /// Number of columns to fill when aligning names
    max_name_len: usize,

    is_multithreaded: bool,
}

impl<T: Write> PrettyFormatter<T> {
    pub fn new(
        out: OutputLocation<T>,
        use_color: bool,
        max_name_len: usize,
        is_multithreaded: bool,
        time_options: Option<time::TestTimeOptions>,
    ) -> Self {
        PrettyFormatter { out, use_color, max_name_len, is_multithreaded, time_options }
    }

    #[cfg(test)]
    pub fn output_location(&self) -> &OutputLocation<T> {
        &self.out
    }

    pub fn write_ok(&mut self) -> io::Result<()> {
        self.write_short_result("ok", term::color::GREEN)
    }

    pub fn write_failed(&mut self) -> io::Result<()> {
        self.write_short_result("FAILED", term::color::RED)
    }

    pub fn write_ignored(&mut self) -> io::Result<()> {
        self.write_short_result("ignored", term::color::YELLOW)
    }

    pub fn write_allowed_fail(&mut self) -> io::Result<()> {
        self.write_short_result("FAILED (allowed)", term::color::YELLOW)
    }

    pub fn write_time_failed(&mut self) -> io::Result<()> {
        self.write_short_result("FAILED (time limit exceeded)", term::color::RED)
    }

    pub fn write_bench(&mut self) -> io::Result<()> {
        self.write_pretty("bench", term::color::CYAN)
    }

    pub fn write_short_result(
        &mut self,
        result: &str,
        color: term::color::Color,
    ) -> io::Result<()> {
        self.write_pretty(result, color)
    }

    pub fn write_pretty(&mut self, word: &str, color: term::color::Color) -> io::Result<()> {
        match self.out {
            OutputLocation::Pretty(ref mut term) => {
                if self.use_color {
                    term.fg(color)?;
                }
                term.write_all(word.as_bytes())?;
                if self.use_color {
                    term.reset()?;
                }
                term.flush()
            }
            OutputLocation::Raw(ref mut stdout) => {
                stdout.write_all(word.as_bytes())?;
                stdout.flush()
            }
        }
    }

    pub fn write_plain<S: AsRef<str>>(&mut self, s: S) -> io::Result<()> {
        let s = s.as_ref();
        self.out.write_all(s.as_bytes())?;
        self.out.flush()
    }

    fn write_time(
        &mut self,
        desc: &TestDesc,
        exec_time: Option<&time::TestExecTime>,
    ) -> io::Result<()> {
        if let (Some(opts), Some(time)) = (self.time_options, exec_time) {
            let time_str = format!(" <{}>", time);

            let color = if opts.colored {
                if opts.is_critical(desc, time) {
                    Some(term::color::RED)
                } else if opts.is_warn(desc, time) {
                    Some(term::color::YELLOW)
                } else {
                    None
                }
            } else {
                None
            };

            match color {
                Some(color) => self.write_pretty(&time_str, color)?,
                None => self.write_plain(&time_str)?,
            }
        }

        Ok(())
    }

    fn write_results(
        &mut self,
        inputs: &Vec<(TestDesc, Vec<u8>)>,
        results_type: &str,
    ) -> io::Result<()> {
        let results_out_str = format!("\n{}:\n", results_type);

        self.write_plain(&results_out_str)?;

        let mut results = Vec::new();
        let mut stdouts = String::new();
        for &(ref f, ref stdout) in inputs {
            results.push(f.name.to_string());
            if !stdout.is_empty() {
                stdouts.push_str(&format!("---- {} stdout ----\n", f.name));
                let output = String::from_utf8_lossy(stdout);
                stdouts.push_str(&output);
                stdouts.push('\n');
            }
        }
        if !stdouts.is_empty() {
            self.write_plain("\n")?;
            self.write_plain(&stdouts)?;
        }

        self.write_plain(&results_out_str)?;
        results.sort();
        for name in &results {
            self.write_plain(&format!("    {}\n", name))?;
        }
        Ok(())
    }

    pub fn write_successes(&mut self, state: &ConsoleTestState) -> io::Result<()> {
        self.write_results(&state.not_failures, "successes")
    }

    pub fn write_failures(&mut self, state: &ConsoleTestState) -> io::Result<()> {
        self.write_results(&state.failures, "failures")
    }

    pub fn write_time_failures(&mut self, state: &ConsoleTestState) -> io::Result<()> {
        self.write_results(&state.time_failures, "failures (time limit exceeded)")
    }

    fn write_test_name(&mut self, desc: &TestDesc) -> io::Result<()> {
        let name = desc.padded_name(self.max_name_len, desc.name.padding());
        self.write_plain(&format!("test {} ... ", name))?;

        Ok(())
    }
}

impl<T: Write> OutputFormatter for PrettyFormatter<T> {
    fn write_run_start(&mut self, test_count: usize) -> io::Result<()> {
        let noun = if test_count != 1 { "tests" } else { "test" };
        self.write_plain(&format!("\nrunning {} {}\n", test_count, noun))
    }

    fn write_test_start(&mut self, desc: &TestDesc) -> io::Result<()> {
        // When running tests concurrently, we should not print
        // the test's name as the result will be mis-aligned.
        // When running the tests serially, we print the name here so
        // that the user can see which test hangs.
        if !self.is_multithreaded {
            self.write_test_name(desc)?;
        }

        Ok(())
    }

    fn write_result(
        &mut self,
        desc: &TestDesc,
        result: &TestResult,
        exec_time: Option<&time::TestExecTime>,
        _: &[u8],
        _: &ConsoleTestState,
    ) -> io::Result<()> {
        if self.is_multithreaded {
            self.write_test_name(desc)?;
        }

        match *result {
            TestResult::TrOk => self.write_ok()?,
            TestResult::TrFailed | TestResult::TrFailedMsg(_) => self.write_failed()?,
            TestResult::TrIgnored => self.write_ignored()?,
            TestResult::TrAllowedFail => self.write_allowed_fail()?,
            TestResult::TrBench(ref bs) => {
                self.write_bench()?;
                self.write_plain(&format!(": {}", fmt_bench_samples(bs)))?;
            }
            TestResult::TrTimedFail => self.write_time_failed()?,
        }

        self.write_time(desc, exec_time)?;
        self.write_plain("\n")
    }

    fn write_timeout(&mut self, desc: &TestDesc) -> io::Result<()> {
        if self.is_multithreaded {
            self.write_test_name(desc)?;
        }

        self.write_plain(&format!(
            "test {} has been running for over {} seconds\n",
            desc.name,
            time::TEST_WARN_TIMEOUT_S
        ))
    }

    fn write_run_finish(&mut self, state: &ConsoleTestState) -> io::Result<bool> {
        if state.options.display_output {
            self.write_successes(state)?;
        }
        let success = state.failed == 0;
        if !success {
            if !state.failures.is_empty() {
                self.write_failures(state)?;
            }

            if !state.time_failures.is_empty() {
                self.write_time_failures(state)?;
            }
        }

        self.write_plain("\ntest result: ")?;

        if success {
            // There's no parallelism at this point so it's safe to use color
            self.write_pretty("ok", term::color::GREEN)?;
        } else {
            self.write_pretty("FAILED", term::color::RED)?;
        }

        let s = if state.allowed_fail > 0 {
            format!(
                ". {} passed; {} failed ({} allowed); {} ignored; {} measured; {} filtered out",
                state.passed,
                state.failed + state.allowed_fail,
                state.allowed_fail,
                state.ignored,
                state.measured,
                state.filtered_out
            )
        } else {
            format!(
                ". {} passed; {} failed; {} ignored; {} measured; {} filtered out",
                state.passed, state.failed, state.ignored, state.measured, state.filtered_out
            )
        };

        self.write_plain(&s)?;

        if let Some(ref exec_time) = state.exec_time {
            let time_str = format!("; finished in {}", exec_time);
            self.write_plain(&time_str)?;
        }

        self.write_plain("\n\n")?;

        Ok(success)
    }
}
<commit_msg>test: Print test name only once on timeout<commit_after>use std::{io, io::prelude::Write};

use super::OutputFormatter;
use crate::{
    bench::fmt_bench_samples,
    console::{ConsoleTestState, OutputLocation},
    test_result::TestResult,
    time,
    types::TestDesc,
};

pub(crate) struct PrettyFormatter<T> {
    out: OutputLocation<T>,
    use_color: bool,
    time_options: Option<time::TestTimeOptions>,

    /// Number of columns to fill when aligning names
    max_name_len: usize,

    is_multithreaded: bool,
}

impl<T: Write> PrettyFormatter<T> {
    pub fn new(
        out: OutputLocation<T>,
        use_color: bool,
        max_name_len: usize,
        is_multithreaded: bool,
        time_options: Option<time::TestTimeOptions>,
    ) -> Self {
        PrettyFormatter { out, use_color, max_name_len, is_multithreaded, time_options }
    }

    #[cfg(test)]
    pub fn output_location(&self) -> &OutputLocation<T> {
        &self.out
    }

    pub fn write_ok(&mut self) -> io::Result<()> {
        self.write_short_result("ok", term::color::GREEN)
    }

    pub fn write_failed(&mut self) -> io::Result<()> {
        self.write_short_result("FAILED", term::color::RED)
    }

    pub fn write_ignored(&mut self) -> io::Result<()> {
        self.write_short_result("ignored", term::color::YELLOW)
    }

    pub fn write_allowed_fail(&mut self) -> io::Result<()> {
        self.write_short_result("FAILED (allowed)", term::color::YELLOW)
    }

    pub fn write_time_failed(&mut self) -> io::Result<()> {
        self.write_short_result("FAILED (time limit exceeded)", term::color::RED)
    }

    pub fn write_bench(&mut self) -> io::Result<()> {
        self.write_pretty("bench", term::color::CYAN)
    }

    pub fn write_short_result(
        &mut self,
        result: &str,
        color: term::color::Color,
    ) -> io::Result<()> {
        self.write_pretty(result, color)
    }

    pub fn write_pretty(&mut self, word: &str, color: term::color::Color) -> io::Result<()> {
        match self.out {
            OutputLocation::Pretty(ref mut term) => {
                if self.use_color {
                    term.fg(color)?;
                }
                term.write_all(word.as_bytes())?;
                if self.use_color {
                    term.reset()?;
                }
                term.flush()
            }
            OutputLocation::Raw(ref mut stdout) => {
                stdout.write_all(word.as_bytes())?;
                stdout.flush()
            }
        }
    }

    pub fn write_plain<S: AsRef<str>>(&mut self, s: S) -> io::Result<()> {
        let s = s.as_ref();
        self.out.write_all(s.as_bytes())?;
        self.out.flush()
    }

    fn write_time(
        &mut self,
        desc: &TestDesc,
        exec_time: Option<&time::TestExecTime>,
    ) -> io::Result<()> {
        if let (Some(opts), Some(time)) = (self.time_options, exec_time) {
            let time_str = format!(" <{}>", time);

            let color = if opts.colored {
                if opts.is_critical(desc, time) {
                    Some(term::color::RED)
                } else if opts.is_warn(desc, time) {
                    Some(term::color::YELLOW)
                } else {
                    None
                }
            } else {
                None
            };

            match color {
                Some(color) => self.write_pretty(&time_str, color)?,
                None => self.write_plain(&time_str)?,
            }
        }

        Ok(())
    }

    fn write_results(
        &mut self,
        inputs: &Vec<(TestDesc, Vec<u8>)>,
        results_type: &str,
    ) -> io::Result<()> {
        let results_out_str = format!("\n{}:\n", results_type);

        self.write_plain(&results_out_str)?;

        let mut results = Vec::new();
        let mut stdouts = String::new();
        for &(ref f, ref stdout) in inputs {
            results.push(f.name.to_string());
            if !stdout.is_empty() {
                stdouts.push_str(&format!("---- {} stdout ----\n", f.name));
                let output = String::from_utf8_lossy(stdout);
                stdouts.push_str(&output);
                stdouts.push('\n');
            }
        }
        if !stdouts.is_empty() {
            self.write_plain("\n")?;
            self.write_plain(&stdouts)?;
        }

        self.write_plain(&results_out_str)?;
        results.sort();
        for name in &results {
            self.write_plain(&format!("    {}\n", name))?;
        }
        Ok(())
    }

    pub fn write_successes(&mut self, state: &ConsoleTestState) -> io::Result<()> {
        self.write_results(&state.not_failures, "successes")
    }

    pub fn write_failures(&mut self, state: &ConsoleTestState) -> io::Result<()> {
        self.write_results(&state.failures, "failures")
    }

    pub fn write_time_failures(&mut self, state: &ConsoleTestState) -> io::Result<()> {
        self.write_results(&state.time_failures, "failures (time limit exceeded)")
    }

    fn write_test_name(&mut self, desc: &TestDesc) -> io::Result<()> {
        let name = desc.padded_name(self.max_name_len, desc.name.padding());
        self.write_plain(&format!("test {} ... ", name))?;

        Ok(())
    }
}

impl<T: Write> OutputFormatter for PrettyFormatter<T> {
    fn write_run_start(&mut self, test_count: usize) -> io::Result<()> {
        let noun = if test_count != 1 { "tests" } else { "test" };
        self.write_plain(&format!("\nrunning {} {}\n", test_count, noun))
    }

    fn write_test_start(&mut self, desc: &TestDesc) -> io::Result<()> {
        // When running tests concurrently, we should not print
        // the test's name as the result will be mis-aligned.
        // When running the tests serially, we print the name here so
        // that the user can see which test hangs.
        if !self.is_multithreaded {
            self.write_test_name(desc)?;
        }

        Ok(())
    }

    fn write_result(
        &mut self,
        desc: &TestDesc,
        result: &TestResult,
        exec_time: Option<&time::TestExecTime>,
        _: &[u8],
        _: &ConsoleTestState,
    ) -> io::Result<()> {
        if self.is_multithreaded {
            self.write_test_name(desc)?;
        }

        match *result {
            TestResult::TrOk => self.write_ok()?,
            TestResult::TrFailed | TestResult::TrFailedMsg(_) => self.write_failed()?,
            TestResult::TrIgnored => self.write_ignored()?,
            TestResult::TrAllowedFail => self.write_allowed_fail()?,
            TestResult::TrBench(ref bs) => {
                self.write_bench()?;
                self.write_plain(&format!(": {}", fmt_bench_samples(bs)))?;
            }
            TestResult::TrTimedFail => self.write_time_failed()?,
        }

        self.write_time(desc, exec_time)?;
        self.write_plain("\n")
    }

    fn write_timeout(&mut self, desc: &TestDesc) -> io::Result<()> {
        self.write_plain(&format!(
            "test {} has been running for over {} seconds\n",
            desc.name,
            time::TEST_WARN_TIMEOUT_S
        ))
    }

    fn write_run_finish(&mut self, state: &ConsoleTestState) -> io::Result<bool> {
        if state.options.display_output {
            self.write_successes(state)?;
        }
        let success = state.failed == 0;
        if !success {
            if !state.failures.is_empty() {
                self.write_failures(state)?;
            }

            if !state.time_failures.is_empty() {
                self.write_time_failures(state)?;
            }
        }

        self.write_plain("\ntest result: ")?;

        if success {
            // There's no parallelism at this point so it's safe to use color
            self.write_pretty("ok", term::color::GREEN)?;
        } else {
            self.write_pretty("FAILED", term::color::RED)?;
        }

        let s = if state.allowed_fail > 0 {
            format!(
                ". {} passed; {} failed ({} allowed); {} ignored; {} measured; {} filtered out",
                state.passed,
                state.failed + state.allowed_fail,
                state.allowed_fail,
                state.ignored,
                state.measured,
                state.filtered_out
            )
        } else {
            format!(
                ". {} passed; {} failed; {} ignored; {} measured; {} filtered out",
                state.passed, state.failed, state.ignored, state.measured, state.filtered_out
            )
        };

        self.write_plain(&s)?;

        if let Some(ref exec_time) = state.exec_time {
            let time_str = format!("; finished in {}", exec_time);
            self.write_plain(&time_str)?;
        }

        self.write_plain("\n\n")?;

        Ok(success)
    }
}
<|endoftext|>


<commit_before>extern crate time;
extern crate nock;
extern crate urbit;

use std::env;
use std::rc::Rc;
use std::io::prelude::*;
use std::fs::File;
use nock::Noun::*;
use nock::nock_on;

fn main() {
    let mut file = File::open(env::args().nth(1).expect("Usage scan [urbit.pill]")).unwrap();

    let mut buf = Vec::new();
    file.read_to_end(&mut buf).unwrap();
    println!("Unpacking pill");
    let time_start = time::precise_time_s();
    let noun = urbit::unpack_pill(buf).unwrap();
    println!("Unpacked pill in {:.03} seconds.", time::precise_time_s() - time_start);

    println!("Nocking pill");
    let noun = nock_on(&Atom(0), &noun).unwrap();
    println!("Result: {}", noun);
}
<commit_msg>Use noun folding to get urbit noun atom count<commit_after>extern crate time;
extern crate nock;
extern crate urbit;
extern crate num;

use std::env;
use std::rc::Rc;
use std::io::prelude::*;
use std::fs::File;
use nock::Noun::*;
use nock::FoldState;
use nock::nock_on;
use num::bigint::BigUint;
use num::traits::One;

fn main() {
    let mut file = File::open(env::args().nth(1).expect("Usage scan [urbit.pill]")).unwrap();

    let mut buf = Vec::new();
    file.read_to_end(&mut buf).unwrap();
    println!("Unpacking pill");
    let time_start = time::precise_time_s();
    let noun = urbit::unpack_pill(buf).unwrap();
    println!("Unpacked pill in {:.03} seconds.",
             time::precise_time_s() - time_start);

    let count: BigUint = noun.fold(|x| {
        match x {
            FoldState::Cell(p, q) => p + q,
            _ => One::one(),
        }
    });

    println!("Pill has {} atoms", count);

    println!("Nocking pill");
    let noun = nock_on(&Atom(0), &noun).unwrap();
    println!("Result: {}", noun);
}
<|endoftext|>


<commit_before>//! # fastup: FAncy STatus markUP language
//!
//! ```fastup
//! hello [cceeff: world(0000ff: yes) or {\[n\]o}] <date|hello|world> up
//! ```
//!
//! becomes
//!
//!
//! hello worldyes or [n]o <Error: date accepts no argument> up
//!

extern crate color;
mod parser;
pub use parser::*;

#[cfg(test)]
mod tests {
    use super::*;
    use Node::*;

    fn html_from_document(doc: Document) -> String {
        doc.0.into_iter().map(html_from_node).collect()
    }

    fn html_from_node(node: Node) -> String {
        match node {
            Text(text) => escape_html(&text),
            Foreground(color, doc) => format!("{}", color.clamp_to_888(), html_from_document(doc)),
            Background(color, doc) => format!("{}", color.clamp_to_888(), html_from_document(doc)),
            Bold(doc) => format!("{}", html_from_document(doc)),
            Widget(name, args) => unreachable!(),
        }
    }

    fn escape_html(text: &str) -> String {
        text.chars().map(|ch| -> String {
            match ch {
                '\n' => "
".into(),
                ' '  => " ".into(),
                '>'  => ">".into(),
                '<'  => "<".into(),
                c    => c.to_string(),
            }
        }).collect()
    }

    fn expand_widgets(doc: Document) -> Document {
        doc.0.into_iter().map(|node| -> Node {
            match node {
                Text(_) => node,
                Foreground(color, doc) => Foreground(color, expand_widgets(doc)),
                Background(color, doc) => Background(color, expand_widgets(doc)),
                Bold(doc) => Bold(expand_widgets(doc)),
                Widget(..) => expand_widget(node),
            }
        }).collect::<Vec<Node>>().into()
    }

    fn expand_widget(widget: Node) -> Node {
        match widget {
            Widget(name, _) => parse(&format!("[ff0000: (ffffff: {{\\<Error: {} accepts no argument\\>}})]", name)).unwrap().0.into_iter().next().unwrap(),
            _ => unreachable!(),
        }
    }

    const TEST_SOURCE: &'static str = r"hello [cceeff: world(0000ff: yes) or {\[n\]o}] <date|hello|world> up";

    #[test]
    fn generate_html() {
        let doc = parse(TEST_SOURCE).unwrap();
        let doc = expand_widgets(doc);
        let html = html_from_document(doc);
        let correct = r#"hello worldyes or [n]o <Error: date accepts no argument> up"#;
        assert_eq!(html, correct);
    }
}

<commit_msg>clean up: more idiomatic<commit_after>//! # fastup: FAncy STatus markUP language
//!
//! ```fastup
//! hello [cceeff: world(0000ff: yes) or {\[n\]o}] <date|hello|world> up
//! ```
//!
//! becomes
//!
//!
//! hello worldyes or [n]o <Error: date accepts no argument> up
//!

extern crate color;
mod parser;
pub use parser::*;

#[cfg(test)]
mod tests {
    use super::*;
    use Node::*;

    fn html_from_document(doc: Document) -> String {
        doc.0.into_iter().map(html_from_node).collect()
    }

    fn html_from_node(node: Node) -> String {
        match node {
            Text(text) => escape_html(&text),
            Foreground(color, doc) => format!("{}", color.clamp_to_888(), html_from_document(doc)),
            Background(color, doc) => format!("{}", color.clamp_to_888(), html_from_document(doc)),
            Bold(doc) => format!("{}", html_from_document(doc)),
            Widget(name, args) => unreachable!(),
        }
    }

    fn escape_html(text: &str) -> String {
        text.chars().map(|ch| -> String {
            match ch {
                '\n' => "
".into(),
                ' '  => " ".into(),
                '>'  => ">".into(),
                '<'  => "<".into(),
                c    => c.to_string(),
            }
        }).collect()
    }

    fn expand_widgets(doc: Document) -> Document {
        doc.0.into_iter().map(|node| -> Node {
            match node {
                Text(..) => node,
                Foreground(color, doc) => Foreground(color, expand_widgets(doc)),
                Background(color, doc) => Background(color, expand_widgets(doc)),
                Bold(doc) => Bold(expand_widgets(doc)),
                Widget(..) => expand_widget(node),
            }
        }).collect::<Vec<Node>>().into()
    }

    fn expand_widget(widget: Node) -> Node {
        match widget {
            Widget(name, _) => parse(&format!("[ff0000: (ffffff: {{\\<Error: {} accepts no argument\\>}})]", name)).unwrap().0.into_iter().next().unwrap(),
            _ => unreachable!(),
        }
    }

    const TEST_SOURCE: &'static str = r"hello [cceeff: world(0000ff: yes) or {\[n\]o}] <date|hello|world> up";

    #[test]
    fn generate_html() {
        let doc = parse(TEST_SOURCE).unwrap();
        let doc = expand_widgets(doc);
        let html = html_from_document(doc);
        let correct = r#"hello worldyes or [n]o <Error: date accepts no argument> up"#;
        assert_eq!(html, correct);
    }
}

<|endoftext|>


<commit_before>#![allow(dead_code, unused_imports)]

extern crate fitsio_sys as sys;
extern crate libc;

use std::ptr;
use std::ffi;

mod stringutils;

/// Error type
#[derive(Debug, PartialEq, Eq)]
pub struct FitsError {
    status: i32,
    message: String,
}

pub type Result<T> = std::result::Result<T, FitsError>;

/// Hdu description type
///
/// Any way of describing a HDU - number or string which either
/// changes the hdu by absolute number, or by name.
pub trait DescribesHdu {
    fn change_hdu(&self, fptr: &FitsFile) -> Result<()>;
}

impl DescribesHdu for usize {
    fn change_hdu(&self, f: &FitsFile) -> Result<()> {
        let mut _hdu_type = 0;
        let mut status = 0;
        unsafe {
            sys::ffmahd(f.fptr, (*self + 1) as i32, &mut _hdu_type, &mut status);
        }
        match status {
            0 => Ok(()),
            _ => {
                Err(FitsError {
                    status: status,
                    message: stringutils::status_to_string(status).unwrap(),
                })
            }
        }
    }
}

impl<'a> DescribesHdu for &'a str {
    fn change_hdu(&self, f: &FitsFile) -> Result<()> {
        let mut _hdu_type = 0;
        let mut status = 0;
        let c_hdu_name = ffi::CString::new(*self).unwrap();

        unsafe {
            sys::ffmnhd(f.fptr,
                        sys::HduType::ANY_HDU as libc::c_int,
                        c_hdu_name.into_raw(),
                        0,
                        &mut status);
        }

        match status {
            0 => Ok(()),
            _ => {
                Err(FitsError {
                    status: status,
                    message: stringutils::status_to_string(status).unwrap(),
                })
            }
        }
    }
}

/// Trait applied to types which can be read from a FITS header
///
/// This is currently:
///
/// * i32
/// * i64
/// * f32
/// * f64
/// * String
pub trait ReadsKey {
    fn read_key(f: &FitsFile, name: &str) -> Result<Self> where Self: std::marker::Sized;
}

macro_rules! reads_key_impl {
    ($t:ty, $func:ident) => (
        impl ReadsKey for $t {
            fn read_key(f: &FitsFile, name: &str) -> Result<Self> {
                let c_name = ffi::CString::new(name).unwrap();
                let mut status = 0;
                let mut value: Self = Self::default();

                unsafe {
                    sys::$func(f.fptr,
                           c_name.into_raw(),
                           &mut value,
                           ptr::null_mut(),
                           &mut status);
                }

                match status {
                    0 => Ok(value),
                    s => {
                        Err(FitsError {
                            status: s,
                            message: stringutils::status_to_string(s).unwrap(),
                        })
                    }
                }
            }
        }
    )
}

reads_key_impl!(i32, ffgkyl);
reads_key_impl!(i64, ffgkyj);
reads_key_impl!(f32, ffgkye);
reads_key_impl!(f64, ffgkyd);

impl ReadsKey for String {
    fn read_key(f: &FitsFile, name: &str) -> Result<Self> {
        let c_name = ffi::CString::new(name).unwrap();
        let mut status = 0;
        let mut value: Vec<libc::c_char> = vec![0; sys::MAX_VALUE_LENGTH];

        unsafe {
            sys::ffgkys(f.fptr,
                        c_name.into_raw(),
                        value.as_mut_ptr(),
                        ptr::null_mut(),
                        &mut status);
        }

        match status {
            0 => {
                let value: Vec<u8> = value.iter()
                    .map(|&x| x as u8)
                    .filter(|&x| x != 0)
                    .collect();
                Ok(String::from_utf8(value).unwrap())
            }
            status => {
                Err(FitsError {
                    status: status,
                    message: stringutils::status_to_string(status).unwrap(),
                })
            }
        }

    }
}

#[derive(PartialEq, Eq, Debug)]
pub enum HduInfo {
    ImageInfo {
        dimensions: usize,
        shape: Vec<usize>,
    },
    TableInfo {
        column_names: Vec<String>,
        column_types: Vec<sys::DataType>,
        num_rows: usize,
    },
}

pub struct FitsFile {
    fptr: *mut sys::fitsfile,
    pub filename: String,
}

fn typechar_to_data_type<T: Into<String>>(typechar: T) -> sys::DataType {
    match typechar.into().as_str() {
        "X" => sys::DataType::TBIT,
        "B" => sys::DataType::TBYTE,
        "L" => sys::DataType::TLOGICAL,
        "A" => sys::DataType::TSTRING,
        "I" => sys::DataType::TSHORT,
        "J" => sys::DataType::TLONG,
        "E" => sys::DataType::TFLOAT,
        "D" => sys::DataType::TDOUBLE,
        "C" => sys::DataType::TCOMPLEX,
        "M" => sys::DataType::TDBLCOMPLEX,
        other => panic!("Unhandled case: {}", other),
    }
}

unsafe fn fetch_hdu_info(fptr: *mut sys::fitsfile) -> Result<HduInfo> {
    let mut status = 0;
    let mut hdu_type = 0;

    sys::ffghdt(fptr, &mut hdu_type, &mut status);
    let hdu_type =
        match hdu_type {
            0 => {
                let mut dimensions = 0;
                sys::ffgidm(fptr, &mut dimensions, &mut status);

                let mut shape = vec![0; dimensions as usize];
                sys::ffgisz(fptr, dimensions, shape.as_mut_ptr(), &mut status);

                HduInfo::ImageInfo {
                    dimensions: dimensions as usize,
                    shape: shape.iter().map(|v| *v as usize).collect(),
                }
            }
            1 | 2 => {
                let mut num_rows = 0;
                sys::ffgnrw(fptr, &mut num_rows, &mut status);

                let mut num_cols = 0;
                sys::ffgncl(fptr, &mut num_cols, &mut status);
                let mut column_names = Vec::with_capacity(num_cols as usize);
                let mut column_types = Vec::with_capacity(num_cols as usize);

                for i in 0..num_cols {
                    let mut name_buffer: Vec<libc::c_char> = vec![0; 71];
                    let mut type_buffer: Vec<libc::c_char> = vec![0; 71];
                    sys::ffgbcl(fptr,
                                (i + 1) as i32,
                                name_buffer.as_mut_ptr(),
                                ptr::null_mut(),
                                type_buffer.as_mut_ptr(),
                                ptr::null_mut(),
                                ptr::null_mut(),
                                ptr::null_mut(),
                                ptr::null_mut(),
                                ptr::null_mut(),
                                &mut status);
                    column_names.push(stringutils::buf_to_string(&name_buffer).unwrap());
                    column_types.push(typechar_to_data_type(stringutils::buf_to_string(&type_buffer)
                            .unwrap()));
                }

                HduInfo::TableInfo {
                    column_names: column_names,
                    column_types: column_types,
                    num_rows: num_rows as usize,
                }
            }
            _ => panic!("Invalid hdu type found"),
        };

    match status {
        0 => Ok(hdu_type),
        _ => {
            Err(FitsError {
                status: status,
                message: stringutils::status_to_string(status).unwrap(),
            })
        }
    }
}

impl FitsFile {
    pub fn open(filename: &str) -> Result<Self> {
        let mut fptr = ptr::null_mut();
        let mut status = 0;
        let c_filename = ffi::CString::new(filename).unwrap();

        unsafe {
            sys::ffopen(&mut fptr as *mut *mut sys::fitsfile,
                        c_filename.as_ptr(),
                        sys::FileOpenMode::READONLY as libc::c_int,
                        &mut status);
        }

        match status {
            0 => {
                Ok(FitsFile {
                    fptr: fptr,
                    filename: filename.to_string(),
                })
            }
            status => {
                Err(FitsError {
                    status: status,
                    message: stringutils::status_to_string(status).unwrap(),
                })
            }
        }

    }

    pub fn create(path: &str) -> Result<Self> {
        let mut fptr = ptr::null_mut();
        let mut status = 0;
        let c_filename = ffi::CString::new(path).unwrap();

        unsafe {
            sys::ffinit(&mut fptr as *mut *mut sys::fitsfile,
                        c_filename.as_ptr(),
                        &mut status);
        }

        match status {
            0 => {
                Ok(FitsFile {
                    fptr: fptr,
                    filename: path.to_string(),
                })
            }
            status => {
                Err(FitsError {
                    status: status,
                    message: stringutils::status_to_string(status).unwrap(),
                })
            }
        }
    }

    pub fn change_hdu<T: DescribesHdu>(&self, hdu_description: T) -> Result<()> {
        hdu_description.change_hdu(self)
    }

    pub fn hdu_type(&self) -> Result<sys::HduType> {
        let mut status = 0;
        let mut hdu_type = 0;
        unsafe {
            sys::ffghdt(self.fptr, &mut hdu_type, &mut status);
        }
        match status {
            0 => {
                match hdu_type {
                    0 => Ok(sys::HduType::IMAGE_HDU),
                    2 => Ok(sys::HduType::BINARY_TBL),
                    _ => unimplemented!(),
                }
            },
            _ => Err(FitsError {
                status: status,
                message: stringutils::status_to_string(status).unwrap(),
            }),
        }
    }

    fn hdu_number(&self) -> usize {
        let mut hdu_num = 0;
        unsafe {
            sys::ffghdn(self.fptr, &mut hdu_num);
        }
        (hdu_num - 1) as usize
    }

    pub fn read_key<T: ReadsKey>(&self, name: &str) -> Result<T> {
        T::read_key(self, name)
    }

    fn fetch_hdu_info(&self) -> Result<HduInfo> {
        unsafe { fetch_hdu_info(self.fptr) }
    }
}

impl Drop for FitsFile {
    fn drop(&mut self) {
        let mut status = 0;
        unsafe {
            sys::ffclos(self.fptr, &mut status);
        }
    }
}

#[cfg(test)]
mod test {
    extern crate tempdir;
    use super::*;
    use sys;
    use super::typechar_to_data_type;

    #[test]
    fn typechar_conversions() {
        let input = vec![
            "X",
            "B",
            "L",
            "A",
            "I",
            "J",
            "E",
            "D",
            "C",
            "M",
        ];
        let expected = vec![
            sys::DataType::TBIT,
            sys::DataType::TBYTE,
            sys::DataType::TLOGICAL,
            sys::DataType::TSTRING,
            sys::DataType::TSHORT,
            sys::DataType::TLONG,
            sys::DataType::TFLOAT,
            sys::DataType::TDOUBLE,
            sys::DataType::TCOMPLEX,
            sys::DataType::TDBLCOMPLEX,
        ];

        input.iter()
            .zip(expected)
            .map(|(&i, e)| {
                assert_eq!(typechar_to_data_type(i), e);
            })
            .collect::<Vec<_>>();
    }

    #[test]
    fn opening_an_existing_file() {
        match FitsFile::open("../testdata/full_example.fits") {
            Ok(_) => {}
            Err(e) => panic!("{:?}", e),
        }
    }

    #[test]
    fn creating_a_new_file() {
        let tdir = tempdir::TempDir::new("fitsio-").unwrap();
        let tdir_path = tdir.path();
        let filename = tdir_path.join("test.fits");
        assert!(!filename.exists());

        match FitsFile::create(filename.to_str().unwrap()) {
            Ok(_) => assert!(filename.exists()),
            Err(e) => panic!("Error: {:?}", e),
        }
    }

    #[test]
    fn fetching_a_hdu() {
        let f = FitsFile::open("../testdata/full_example.fits").unwrap();
        for i in 0..2 {
            f.change_hdu(i).unwrap();
            assert_eq!(f.hdu_number(), i);
        }

        match f.change_hdu(2) {
            Err(e) => assert_eq!(e.status, 107),
            _ => panic!("Error checking for failure"),
        }

        f.change_hdu("TESTEXT").unwrap();
        assert_eq!(f.hdu_number(), 1);
    }

    #[test]
    fn reading_header_keys() {
        let f = FitsFile::open("../testdata/full_example.fits").unwrap();
        match f.read_key::<i64>("INTTEST") {
            Ok(value) => assert_eq!(value, 42),
            Err(e) => panic!("Error reading key: {:?}", e),
        }

        match f.read_key::<f64>("DBLTEST") {
            Ok(value) => assert_eq!(value, 0.09375),
            Err(e) => panic!("Error reading key: {:?}", e),
        }

        match f.read_key::<String>("TEST") {
            Ok(value) => assert_eq!(value, "value"),
            Err(e) => panic!("Error reading key: {:?}", e),
        }
    }

    #[test]
    fn getting_hdu_type() {
        let f = FitsFile::open("../testdata/full_example.fits").unwrap();
        assert_eq!(f.hdu_type().unwrap(), sys::HduType::IMAGE_HDU);

        f.change_hdu("TESTEXT").unwrap();
        assert_eq!(f.hdu_type().unwrap(), sys::HduType::BINARY_TBL);
    }
}
<commit_msg>Writing default header for new file<commit_after>#![allow(dead_code, unused_imports)]

extern crate fitsio_sys as sys;
extern crate libc;

use std::ptr;
use std::ffi;

mod stringutils;

/// Error type
#[derive(Debug, PartialEq, Eq)]
pub struct FitsError {
    status: i32,
    message: String,
}

pub type Result<T> = std::result::Result<T, FitsError>;

/// Hdu description type
///
/// Any way of describing a HDU - number or string which either
/// changes the hdu by absolute number, or by name.
pub trait DescribesHdu {
    fn change_hdu(&self, fptr: &FitsFile) -> Result<()>;
}

impl DescribesHdu for usize {
    fn change_hdu(&self, f: &FitsFile) -> Result<()> {
        let mut _hdu_type = 0;
        let mut status = 0;
        unsafe {
            sys::ffmahd(f.fptr, (*self + 1) as i32, &mut _hdu_type, &mut status);
        }
        match status {
            0 => Ok(()),
            _ => {
                Err(FitsError {
                    status: status,
                    message: stringutils::status_to_string(status).unwrap(),
                })
            }
        }
    }
}

impl<'a> DescribesHdu for &'a str {
    fn change_hdu(&self, f: &FitsFile) -> Result<()> {
        let mut _hdu_type = 0;
        let mut status = 0;
        let c_hdu_name = ffi::CString::new(*self).unwrap();

        unsafe {
            sys::ffmnhd(f.fptr,
                        sys::HduType::ANY_HDU as libc::c_int,
                        c_hdu_name.into_raw(),
                        0,
                        &mut status);
        }

        match status {
            0 => Ok(()),
            _ => {
                Err(FitsError {
                    status: status,
                    message: stringutils::status_to_string(status).unwrap(),
                })
            }
        }
    }
}

/// Trait applied to types which can be read from a FITS header
///
/// This is currently:
///
/// * i32
/// * i64
/// * f32
/// * f64
/// * String
pub trait ReadsKey {
    fn read_key(f: &FitsFile, name: &str) -> Result<Self> where Self: std::marker::Sized;
}

macro_rules! reads_key_impl {
    ($t:ty, $func:ident) => (
        impl ReadsKey for $t {
            fn read_key(f: &FitsFile, name: &str) -> Result<Self> {
                let c_name = ffi::CString::new(name).unwrap();
                let mut status = 0;
                let mut value: Self = Self::default();

                unsafe {
                    sys::$func(f.fptr,
                           c_name.into_raw(),
                           &mut value,
                           ptr::null_mut(),
                           &mut status);
                }

                match status {
                    0 => Ok(value),
                    s => {
                        Err(FitsError {
                            status: s,
                            message: stringutils::status_to_string(s).unwrap(),
                        })
                    }
                }
            }
        }
    )
}

reads_key_impl!(i32, ffgkyl);
reads_key_impl!(i64, ffgkyj);
reads_key_impl!(f32, ffgkye);
reads_key_impl!(f64, ffgkyd);

impl ReadsKey for String {
    fn read_key(f: &FitsFile, name: &str) -> Result<Self> {
        let c_name = ffi::CString::new(name).unwrap();
        let mut status = 0;
        let mut value: Vec<libc::c_char> = vec![0; sys::MAX_VALUE_LENGTH];

        unsafe {
            sys::ffgkys(f.fptr,
                        c_name.into_raw(),
                        value.as_mut_ptr(),
                        ptr::null_mut(),
                        &mut status);
        }

        match status {
            0 => {
                let value: Vec<u8> = value.iter()
                    .map(|&x| x as u8)
                    .filter(|&x| x != 0)
                    .collect();
                Ok(String::from_utf8(value).unwrap())
            }
            status => {
                Err(FitsError {
                    status: status,
                    message: stringutils::status_to_string(status).unwrap(),
                })
            }
        }

    }
}

#[derive(PartialEq, Eq, Debug)]
pub enum HduInfo {
    ImageInfo {
        dimensions: usize,
        shape: Vec<usize>,
    },
    TableInfo {
        column_names: Vec<String>,
        column_types: Vec<sys::DataType>,
        num_rows: usize,
    },
}

pub struct FitsFile {
    fptr: *mut sys::fitsfile,
    pub filename: String,
}

fn typechar_to_data_type<T: Into<String>>(typechar: T) -> sys::DataType {
    match typechar.into().as_str() {
        "X" => sys::DataType::TBIT,
        "B" => sys::DataType::TBYTE,
        "L" => sys::DataType::TLOGICAL,
        "A" => sys::DataType::TSTRING,
        "I" => sys::DataType::TSHORT,
        "J" => sys::DataType::TLONG,
        "E" => sys::DataType::TFLOAT,
        "D" => sys::DataType::TDOUBLE,
        "C" => sys::DataType::TCOMPLEX,
        "M" => sys::DataType::TDBLCOMPLEX,
        other => panic!("Unhandled case: {}", other),
    }
}

unsafe fn fetch_hdu_info(fptr: *mut sys::fitsfile) -> Result<HduInfo> {
    let mut status = 0;
    let mut hdu_type = 0;

    sys::ffghdt(fptr, &mut hdu_type, &mut status);
    let hdu_type = match hdu_type {
        0 => {
            let mut dimensions = 0;
            sys::ffgidm(fptr, &mut dimensions, &mut status);

            let mut shape = vec![0; dimensions as usize];
            sys::ffgisz(fptr, dimensions, shape.as_mut_ptr(), &mut status);

            HduInfo::ImageInfo {
                dimensions: dimensions as usize,
                shape: shape.iter().map(|v| *v as usize).collect(),
            }
        }
        1 | 2 => {
            let mut num_rows = 0;
            sys::ffgnrw(fptr, &mut num_rows, &mut status);

            let mut num_cols = 0;
            sys::ffgncl(fptr, &mut num_cols, &mut status);
            let mut column_names = Vec::with_capacity(num_cols as usize);
            let mut column_types = Vec::with_capacity(num_cols as usize);

            for i in 0..num_cols {
                let mut name_buffer: Vec<libc::c_char> = vec![0; 71];
                let mut type_buffer: Vec<libc::c_char> = vec![0; 71];
                sys::ffgbcl(fptr,
                            (i + 1) as i32,
                            name_buffer.as_mut_ptr(),
                            ptr::null_mut(),
                            type_buffer.as_mut_ptr(),
                            ptr::null_mut(),
                            ptr::null_mut(),
                            ptr::null_mut(),
                            ptr::null_mut(),
                            ptr::null_mut(),
                            &mut status);
                column_names.push(stringutils::buf_to_string(&name_buffer).unwrap());
                column_types.push(typechar_to_data_type(stringutils::buf_to_string(&type_buffer)
                            .unwrap()));
            }

            HduInfo::TableInfo {
                column_names: column_names,
                column_types: column_types,
                num_rows: num_rows as usize,
            }
        }
        _ => panic!("Invalid hdu type found"),
    };

    match status {
        0 => Ok(hdu_type),
        _ => {
            Err(FitsError {
                status: status,
                message: stringutils::status_to_string(status).unwrap(),
            })
        }
    }
}

impl FitsFile {
    pub fn open(filename: &str) -> Result<Self> {
        let mut fptr = ptr::null_mut();
        let mut status = 0;
        let c_filename = ffi::CString::new(filename).unwrap();

        unsafe {
            sys::ffopen(&mut fptr as *mut *mut sys::fitsfile,
                        c_filename.as_ptr(),
                        sys::FileOpenMode::READONLY as libc::c_int,
                        &mut status);
        }

        match status {
            0 => {
                Ok(FitsFile {
                    fptr: fptr,
                    filename: filename.to_string(),
                })
            }
            status => {
                Err(FitsError {
                    status: status,
                    message: stringutils::status_to_string(status).unwrap(),
                })
            }
        }

    }

    pub fn create(path: &str) -> Result<Self> {
        let mut fptr = ptr::null_mut();
        let mut status = 0;
        let c_filename = ffi::CString::new(path).unwrap();

        unsafe {
            sys::ffinit(&mut fptr as *mut *mut sys::fitsfile,
                        c_filename.as_ptr(),
                        &mut status);
        }

        match status {
            0 => {
                let f = FitsFile {
                    fptr: fptr,
                    filename: path.to_string(),
                };
                f.add_empty_primary().unwrap();
                Ok(f)
            }
            status => {
                Err(FitsError {
                    status: status,
                    message: stringutils::status_to_string(status).unwrap(),
                })
            }
        }
    }

    fn add_empty_primary(&self) -> Result<()> {
        let mut status = 0;
        unsafe {
            sys::ffphps(self.fptr, 8, 0, ptr::null_mut(), &mut status);
        }
        match status {
            0 => Ok(()),
            _ => {
                Err(FitsError {
                    status: status,
                    message: stringutils::status_to_string(status).unwrap(),
                })
            }
        }
    }

    pub fn change_hdu<T: DescribesHdu>(&self, hdu_description: T) -> Result<()> {
        hdu_description.change_hdu(self)
    }

    pub fn hdu_type(&self) -> Result<sys::HduType> {
        let mut status = 0;
        let mut hdu_type = 0;
        unsafe {
            sys::ffghdt(self.fptr, &mut hdu_type, &mut status);
        }
        match status {
            0 => {
                match hdu_type {
                    0 => Ok(sys::HduType::IMAGE_HDU),
                    2 => Ok(sys::HduType::BINARY_TBL),
                    _ => unimplemented!(),
                }
            }
            _ => {
                Err(FitsError {
                    status: status,
                    message: stringutils::status_to_string(status).unwrap(),
                })
            }
        }
    }

    fn hdu_number(&self) -> usize {
        let mut hdu_num = 0;
        unsafe {
            sys::ffghdn(self.fptr, &mut hdu_num);
        }
        (hdu_num - 1) as usize
    }

    pub fn read_key<T: ReadsKey>(&self, name: &str) -> Result<T> {
        T::read_key(self, name)
    }

    fn fetch_hdu_info(&self) -> Result<HduInfo> {
        unsafe { fetch_hdu_info(self.fptr) }
    }
}

impl Drop for FitsFile {
    fn drop(&mut self) {
        let mut status = 0;
        unsafe {
            sys::ffclos(self.fptr, &mut status);
        }
    }
}

#[cfg(test)]
mod test {
    extern crate tempdir;
    use super::*;
    use sys;
    use super::typechar_to_data_type;

    #[test]
    fn typechar_conversions() {
        let input = vec![
            "X",
            "B",
            "L",
            "A",
            "I",
            "J",
            "E",
            "D",
            "C",
            "M",
        ];
        let expected = vec![
            sys::DataType::TBIT,
            sys::DataType::TBYTE,
            sys::DataType::TLOGICAL,
            sys::DataType::TSTRING,
            sys::DataType::TSHORT,
            sys::DataType::TLONG,
            sys::DataType::TFLOAT,
            sys::DataType::TDOUBLE,
            sys::DataType::TCOMPLEX,
            sys::DataType::TDBLCOMPLEX,
        ];

        input.iter()
            .zip(expected)
            .map(|(&i, e)| {
                assert_eq!(typechar_to_data_type(i), e);
            })
            .collect::<Vec<_>>();
    }

    #[test]
    fn opening_an_existing_file() {
        match FitsFile::open("../testdata/full_example.fits") {
            Ok(_) => {}
            Err(e) => panic!("{:?}", e),
        }
    }

    #[test]
    fn creating_a_new_file() {
        let tdir = tempdir::TempDir::new("fitsio-").unwrap();
        let tdir_path = tdir.path();
        let filename = tdir_path.join("test.fits");
        assert!(!filename.exists());

        FitsFile::create(filename.to_str().unwrap())
            .map(|f| {
                assert!(filename.exists());

                // Ensure the empty primary has been written
                let naxis: i64 = f.read_key("NAXIS").unwrap();
                assert_eq!(naxis, 0);
            })
            .unwrap();
    }

    #[test]
    fn fetching_a_hdu() {
        let f = FitsFile::open("../testdata/full_example.fits").unwrap();
        for i in 0..2 {
            f.change_hdu(i).unwrap();
            assert_eq!(f.hdu_number(), i);
        }

        match f.change_hdu(2) {
            Err(e) => assert_eq!(e.status, 107),
            _ => panic!("Error checking for failure"),
        }

        f.change_hdu("TESTEXT").unwrap();
        assert_eq!(f.hdu_number(), 1);
    }

    #[test]
    fn reading_header_keys() {
        let f = FitsFile::open("../testdata/full_example.fits").unwrap();
        match f.read_key::<i64>("INTTEST") {
            Ok(value) => assert_eq!(value, 42),
            Err(e) => panic!("Error reading key: {:?}", e),
        }

        match f.read_key::<f64>("DBLTEST") {
            Ok(value) => assert_eq!(value, 0.09375),
            Err(e) => panic!("Error reading key: {:?}", e),
        }

        match f.read_key::<String>("TEST") {
            Ok(value) => assert_eq!(value, "value"),
            Err(e) => panic!("Error reading key: {:?}", e),
        }
    }

    #[test]
    fn getting_hdu_type() {
        let f = FitsFile::open("../testdata/full_example.fits").unwrap();
        assert_eq!(f.hdu_type().unwrap(), sys::HduType::IMAGE_HDU);

        f.change_hdu("TESTEXT").unwrap();
        assert_eq!(f.hdu_type().unwrap(), sys::HduType::BINARY_TBL);
    }
}
<|endoftext|>


<commit_before>fn main() {
    println!("Hello, world!");

    #[derive(Show)]
    struct Circle {
        x : f64,
        y : f64,
        radius: f64,
    }

    impl Circle {
        fn area(&self) -> f64 {
            std::f64::consts::PI * ( self.radius * self.radius )
        }

        fn new(x: f64, y: f64, r: f64) -> Circle {
            Circle {x: x, y: y, radius: r}
        }
    }

    {
        let c = Circle { x: 0.0, y: 0.0, radius: 3.4 };
        println!("{}", c.area());
    }

    {
        let c = Circle::new(0.0, 0.0, 3.4);
        println!("{:?}, {}", c, c.area());
    }
}
<commit_msg>fix derive Debuf and core<commit_after>#![feature(core)]
fn main() {
    println!("Hello, world!");

    #[derive(Debug)]
    struct Circle {
        x : f64,
        y : f64,
        radius: f64,
    }

    impl Circle {
        fn area(&self) -> f64 {
            std::f64::consts::PI * ( self.radius * self.radius )
        }

        fn new(x: f64, y: f64, r: f64) -> Circle {
            Circle {x: x, y: y, radius: r}
        }
    }

    {
        let c = Circle { x: 0.0, y: 0.0, radius: 3.4 };
        println!("{}", c.area());
    }

    {
        let c = Circle::new(0.0, 0.0, 3.4);
        println!("{:?}, {}", c, c.area());
    }
}
<|endoftext|>


<commit_before>use crate::{BeaconChain, BeaconChainTypes};
use lmd_ghost::LmdGhost;
use state_processing::common::get_attesting_indices;
use std::sync::Arc;
use store::{Error as StoreError, Store};
use types::{
    Attestation, BeaconBlock, BeaconState, BeaconStateError, Epoch, EthSpec, Hash256, Slot,
};

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

#[derive(Debug, PartialEq)]
pub enum Error {
    MissingBlock(Hash256),
    MissingState(Hash256),
    BackendError(String),
    BeaconStateError(BeaconStateError),
    StoreError(StoreError),
}

pub struct ForkChoice<T: BeaconChainTypes> {
    store: Arc<T::Store>,
    backend: T::LmdGhost,
    /// Used for resolving the `0x00..00` alias back to genesis.
    ///
    /// Does not necessarily need to be the _actual_ genesis, it suffices to be the finalized root
    /// whenever the struct was instantiated.
    genesis_block_root: Hash256,
}

impl<T: BeaconChainTypes> ForkChoice<T> {
    /// Instantiate a new fork chooser.
    ///
    /// "Genesis" does not necessarily need to be the absolute genesis, it can be some finalized
    /// block.
    pub fn new(
        store: Arc<T::Store>,
        genesis_block: &BeaconBlock<T::EthSpec>,
        genesis_block_root: Hash256,
    ) -> Self {
        Self {
            store: store.clone(),
            backend: T::LmdGhost::new(store, genesis_block, genesis_block_root),
            genesis_block_root,
        }
    }

    pub fn find_head(&self, chain: &BeaconChain<T>) -> Result<Hash256> {
        let start_slot = |epoch: Epoch| epoch.start_slot(T::EthSpec::slots_per_epoch());

        // From the specification:
        //
        // Let justified_head be the descendant of finalized_head with the highest epoch that has
        // been justified for at least 1 epoch ... If no such descendant exists,
        // set justified_head to finalized_head.
        let (start_state, start_block_root, start_block_slot) = {
            let state = &chain.head().beacon_state;

            let (block_root, block_slot) =
                if state.current_epoch() + 1 > state.current_justified_checkpoint.epoch {
                    (
                        state.current_justified_checkpoint.root,
                        start_slot(state.current_justified_checkpoint.epoch),
                    )
                } else {
                    (
                        state.finalized_checkpoint.root,
                        start_slot(state.finalized_checkpoint.epoch),
                    )
                };

            let block = chain
                .store
                .get::<BeaconBlock<T::EthSpec>>(&block_root)?
                .ok_or_else(|| Error::MissingBlock(block_root))?;

            // Resolve the `0x00.. 00` alias back to genesis
            let block_root = if block_root == Hash256::zero() {
                self.genesis_block_root
            } else {
                block_root
            };

            let state = chain
                .store
                .get::<BeaconState<T::EthSpec>>(&block.state_root)?
                .ok_or_else(|| Error::MissingState(block.state_root))?;

            (state, block_root, block_slot)
        };

        // A function that returns the weight for some validator index.
        let weight = |validator_index: usize| -> Option<u64> {
            start_state
                .validators
                .get(validator_index)
                .map(|v| v.effective_balance)
        };

        self.backend
            .find_head(start_block_slot, start_block_root, weight)
            .map_err(Into::into)
    }

    /// Process all attestations in the given `block`.
    ///
    /// Assumes the block (and therefore it's attestations) are valid. It is a logic error to
    /// provide an invalid block.
    pub fn process_block(
        &self,
        state: &BeaconState<T::EthSpec>,
        block: &BeaconBlock<T::EthSpec>,
        block_root: Hash256,
    ) -> Result<()> {
        // Note: we never count the block as a latest message, only attestations.
        //
        // I (Paul H) do not have an explicit reference to this, but I derive it from this
        // document:
        //
        // https://github.com/ethereum/eth2.0-specs/blob/v0.7.0/specs/core/0_fork-choice.md
        for attestation in &block.body.attestations {
            let block = self
                .store
                .get::<BeaconBlock<T::EthSpec>>(&attestation.data.beacon_block_root)?
                .ok_or_else(|| Error::MissingBlock(attestation.data.beacon_block_root))?;

            self.process_attestation(state, attestation, &block)?;
        }

        // This does not apply a vote to the block, it just makes fork choice aware of the block so
        // it can still be identified as the head even if it doesn't have any votes.
        //
        // A case where a block without any votes can be the head is where it is the only child of
        // a block that has the majority of votes applied to it.
        self.backend.process_block(block, block_root)?;

        Ok(())
    }

    /// Process an attestation which references `block` in `attestation.data.beacon_block_root`.
    ///
    /// Assumes the attestation is valid.
    pub fn process_attestation(
        &self,
        state: &BeaconState<T::EthSpec>,
        attestation: &Attestation<T::EthSpec>,
        block: &BeaconBlock<T::EthSpec>,
    ) -> Result<()> {
        let block_hash = attestation.data.beacon_block_root;

        // Ignore any attestations to the zero hash.
        //
        // This is an edge case that results from the spec aliasing the zero hash to the genesis
        // block. Attesters may attest to the zero hash if they have never seen a block.
        //
        // We have two options here:
        //
        //  1. Apply all zero-hash attestations to the zero hash.
        //  2. Ignore all attestations to the zero hash.
        //
        // (1) becomes weird once we hit finality and fork choice drops the genesis block. (2) is
        // fine because votes to the genesis block are not useful; all validators implicitly attest
        // to genesis just by being present in the chain.
        //
        // Additionally, don't add any block hash to fork choice unless we have imported the block.
        if block_hash != Hash256::zero() {
            let validator_indices =
                get_attesting_indices(state, &attestation.data, &attestation.aggregation_bits)?;

            for validator_index in validator_indices {
                self.backend
                    .process_attestation(validator_index, block_hash, block.slot)?;
            }
        }

        Ok(())
    }

    /// Determines whether or not the given attestation contains a latest message.
    pub fn should_process_attestation(
        &self,
        state: &BeaconState<T::EthSpec>,
        attestation: &Attestation<T::EthSpec>,
    ) -> Result<bool> {
        let validator_indices =
            get_attesting_indices(state, &attestation.data, &attestation.aggregation_bits)?;

        let block_slot = state.get_attestation_data_slot(&attestation.data)?;

        Ok(validator_indices
            .iter()
            .find(|&&v| match self.backend.latest_message(v) {
                Some((_, slot)) => block_slot > slot,
                None => true,
            })
            .is_some())
    }

    // Returns the latest message for a given validator
    pub fn latest_message(&self, validator_index: usize) -> Option<(Hash256, Slot)> {
        self.backend.latest_message(validator_index)
    }

    /// Inform the fork choice that the given block (and corresponding root) have been finalized so
    /// it may prune it's storage.
    ///
    /// `finalized_block_root` must be the root of `finalized_block`.
    pub fn process_finalization(
        &self,
        finalized_block: &BeaconBlock<T::EthSpec>,
        finalized_block_root: Hash256,
    ) -> Result<()> {
        self.backend
            .update_finalized_root(finalized_block, finalized_block_root)
            .map_err(Into::into)
    }
}

impl From<BeaconStateError> for Error {
    fn from(e: BeaconStateError) -> Error {
        Error::BeaconStateError(e)
    }
}

impl From<StoreError> for Error {
    fn from(e: StoreError) -> Error {
        Error::StoreError(e)
    }
}

impl From<String> for Error {
    fn from(e: String) -> Error {
        Error::BackendError(e)
    }
}
<commit_msg>Ignore unknown blocks at fork choice<commit_after>use crate::{BeaconChain, BeaconChainTypes};
use lmd_ghost::LmdGhost;
use state_processing::common::get_attesting_indices;
use std::sync::Arc;
use store::{Error as StoreError, Store};
use types::{
    Attestation, BeaconBlock, BeaconState, BeaconStateError, Epoch, EthSpec, Hash256, Slot,
};

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

#[derive(Debug, PartialEq)]
pub enum Error {
    MissingBlock(Hash256),
    MissingState(Hash256),
    BackendError(String),
    BeaconStateError(BeaconStateError),
    StoreError(StoreError),
}

pub struct ForkChoice<T: BeaconChainTypes> {
    store: Arc<T::Store>,
    backend: T::LmdGhost,
    /// Used for resolving the `0x00..00` alias back to genesis.
    ///
    /// Does not necessarily need to be the _actual_ genesis, it suffices to be the finalized root
    /// whenever the struct was instantiated.
    genesis_block_root: Hash256,
}

impl<T: BeaconChainTypes> ForkChoice<T> {
    /// Instantiate a new fork chooser.
    ///
    /// "Genesis" does not necessarily need to be the absolute genesis, it can be some finalized
    /// block.
    pub fn new(
        store: Arc<T::Store>,
        genesis_block: &BeaconBlock<T::EthSpec>,
        genesis_block_root: Hash256,
    ) -> Self {
        Self {
            store: store.clone(),
            backend: T::LmdGhost::new(store, genesis_block, genesis_block_root),
            genesis_block_root,
        }
    }

    pub fn find_head(&self, chain: &BeaconChain<T>) -> Result<Hash256> {
        let start_slot = |epoch: Epoch| epoch.start_slot(T::EthSpec::slots_per_epoch());

        // From the specification:
        //
        // Let justified_head be the descendant of finalized_head with the highest epoch that has
        // been justified for at least 1 epoch ... If no such descendant exists,
        // set justified_head to finalized_head.
        let (start_state, start_block_root, start_block_slot) = {
            let state = &chain.head().beacon_state;

            let (block_root, block_slot) =
                if state.current_epoch() + 1 > state.current_justified_checkpoint.epoch {
                    (
                        state.current_justified_checkpoint.root,
                        start_slot(state.current_justified_checkpoint.epoch),
                    )
                } else {
                    (
                        state.finalized_checkpoint.root,
                        start_slot(state.finalized_checkpoint.epoch),
                    )
                };

            let block = chain
                .store
                .get::<BeaconBlock<T::EthSpec>>(&block_root)?
                .ok_or_else(|| Error::MissingBlock(block_root))?;

            // Resolve the `0x00.. 00` alias back to genesis
            let block_root = if block_root == Hash256::zero() {
                self.genesis_block_root
            } else {
                block_root
            };

            let state = chain
                .store
                .get::<BeaconState<T::EthSpec>>(&block.state_root)?
                .ok_or_else(|| Error::MissingState(block.state_root))?;

            (state, block_root, block_slot)
        };

        // A function that returns the weight for some validator index.
        let weight = |validator_index: usize| -> Option<u64> {
            start_state
                .validators
                .get(validator_index)
                .map(|v| v.effective_balance)
        };

        self.backend
            .find_head(start_block_slot, start_block_root, weight)
            .map_err(Into::into)
    }

    /// Process all attestations in the given `block`.
    ///
    /// Assumes the block (and therefore it's attestations) are valid. It is a logic error to
    /// provide an invalid block.
    pub fn process_block(
        &self,
        state: &BeaconState<T::EthSpec>,
        block: &BeaconBlock<T::EthSpec>,
        block_root: Hash256,
    ) -> Result<()> {
        // Note: we never count the block as a latest message, only attestations.
        //
        // I (Paul H) do not have an explicit reference to this, but I derive it from this
        // document:
        //
        // https://github.com/ethereum/eth2.0-specs/blob/v0.7.0/specs/core/0_fork-choice.md
        for attestation in &block.body.attestations {
            // If the `data.beacon_block_root` block is not known to us, simply ignore the latest
            // vote.
            if let Some(block) = self
                .store
                .get::<BeaconBlock<T::EthSpec>>(&attestation.data.beacon_block_root)?
            {
                self.process_attestation(state, attestation, &block)?;
            }
        }

        // This does not apply a vote to the block, it just makes fork choice aware of the block so
        // it can still be identified as the head even if it doesn't have any votes.
        //
        // A case where a block without any votes can be the head is where it is the only child of
        // a block that has the majority of votes applied to it.
        self.backend.process_block(block, block_root)?;

        Ok(())
    }

    /// Process an attestation which references `block` in `attestation.data.beacon_block_root`.
    ///
    /// Assumes the attestation is valid.
    pub fn process_attestation(
        &self,
        state: &BeaconState<T::EthSpec>,
        attestation: &Attestation<T::EthSpec>,
        block: &BeaconBlock<T::EthSpec>,
    ) -> Result<()> {
        let block_hash = attestation.data.beacon_block_root;

        // Ignore any attestations to the zero hash.
        //
        // This is an edge case that results from the spec aliasing the zero hash to the genesis
        // block. Attesters may attest to the zero hash if they have never seen a block.
        //
        // We have two options here:
        //
        //  1. Apply all zero-hash attestations to the zero hash.
        //  2. Ignore all attestations to the zero hash.
        //
        // (1) becomes weird once we hit finality and fork choice drops the genesis block. (2) is
        // fine because votes to the genesis block are not useful; all validators implicitly attest
        // to genesis just by being present in the chain.
        //
        // Additionally, don't add any block hash to fork choice unless we have imported the block.
        if block_hash != Hash256::zero() {
            let validator_indices =
                get_attesting_indices(state, &attestation.data, &attestation.aggregation_bits)?;

            for validator_index in validator_indices {
                self.backend
                    .process_attestation(validator_index, block_hash, block.slot)?;
            }
        }

        Ok(())
    }

    /// Determines whether or not the given attestation contains a latest message.
    pub fn should_process_attestation(
        &self,
        state: &BeaconState<T::EthSpec>,
        attestation: &Attestation<T::EthSpec>,
    ) -> Result<bool> {
        let validator_indices =
            get_attesting_indices(state, &attestation.data, &attestation.aggregation_bits)?;

        let block_slot = state.get_attestation_data_slot(&attestation.data)?;

        Ok(validator_indices
            .iter()
            .find(|&&v| match self.backend.latest_message(v) {
                Some((_, slot)) => block_slot > slot,
                None => true,
            })
            .is_some())
    }

    // Returns the latest message for a given validator
    pub fn latest_message(&self, validator_index: usize) -> Option<(Hash256, Slot)> {
        self.backend.latest_message(validator_index)
    }

    /// Inform the fork choice that the given block (and corresponding root) have been finalized so
    /// it may prune it's storage.
    ///
    /// `finalized_block_root` must be the root of `finalized_block`.
    pub fn process_finalization(
        &self,
        finalized_block: &BeaconBlock<T::EthSpec>,
        finalized_block_root: Hash256,
    ) -> Result<()> {
        self.backend
            .update_finalized_root(finalized_block, finalized_block_root)
            .map_err(Into::into)
    }
}

impl From<BeaconStateError> for Error {
    fn from(e: BeaconStateError) -> Error {
        Error::BeaconStateError(e)
    }
}

impl From<StoreError> for Error {
    fn from(e: StoreError) -> Error {
        Error::StoreError(e)
    }
}

impl From<String> for Error {
    fn from(e: String) -> Error {
        Error::BackendError(e)
    }
}
<|endoftext|>