<commit_before>// Copyright 2019 Developers of the Rand project.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Interface to the random number generator of the operating system.
//!
//! # Platform sources
//!
//! | OS               | interface
//! |------------------|---------------------------------------------------------
//! | Linux, Android   | [`getrandom`][1] system call if available, otherwise [`/dev/urandom`][2] after successfully polling `/dev/random`
//! | Windows          | [`RtlGenRandom`][3]
//! | macOS            | [`getentropy()`][19] if available, otherwise [`/dev/random`][20] (identical to `/dev/urandom`)
//! | iOS              | [`SecRandomCopyBytes`][4]
//! | FreeBSD          | [`getrandom()`][21] if available, otherwise [`kern.arandom`][5]
//! | OpenBSD          | [`getentropy`][6]
//! | NetBSD           | [`/dev/urandom`][7] after successfully polling `/dev/random`
//! | Dragonfly BSD    | [`/dev/random`][8]
//! | Solaris, illumos | [`getrandom`][9] system call if available, otherwise [`/dev/random`][10]
//! | Fuchsia OS       | [`cprng_draw`][11]
//! | Redox            | [`rand:`][12]
//! | CloudABI         | [`cloudabi_sys_random_get`][13]
//! | Haiku            | `/dev/random` (identical to `/dev/urandom`)
//! | L4RE, SGX, UEFI  | [RDRAND][18]
//! | Hermit           | [RDRAND][18] as [`sys_rand`][22] is currently broken.
//! | Web browsers     | [`Crypto.getRandomValues`][14] (see [Support for WebAssembly and ams.js][14])
//! | Node.js          | [`crypto.randomBytes`][15] (see [Support for WebAssembly and ams.js][16])
//! | WASI             | [`__wasi_random_get`][17]
//!
//! Getrandom doesn't have a blanket implementation for all Unix-like operating
//! systems that reads from `/dev/urandom`. This ensures all supported operating
//! systems are using the recommended interface and respect maximum buffer
//! sizes.
//!
//! ## Unsupported targets
//!
//! By default, compiling `getrandom` for an unsupported target will result in
//! a compilation error. If you want to build an application which uses `getrandom`
//! for such target, you can either:
//! - Use [`[replace]`][replace] or [`[patch]`][patch] section in your `Cargo.toml`
//! to switch to a custom implementation with a support of your target.
//! - Enable the `dummy` feature to have getrandom use an implementation that always
//! fails at run-time on unsupported targets.
//!
//! [replace]: https://doc.rust-lang.org/cargo/reference/manifest.html#the-replace-section
//! [patch]: https://doc.rust-lang.org/cargo/reference/manifest.html#the-patch-section
//!
//! ## Support for WebAssembly and asm.js
//!
//! Getrandom supports all of Rust's current `wasm32` targets, and it works with
//! both Node.js and web browsers. The three Emscripten targets
//! `asmjs-unknown-emscripten`, `wasm32-unknown-emscripten`, and
//! `wasm32-experimental-emscripten` use Emscripten's `/dev/random` emulation.
//! The WASI target `wasm32-wasi` uses the [`__wasi_random_get`][17] function
//! defined by the WASI standard.
//!
//! Getrandom also supports `wasm32-unknown-unknown` by directly calling
//! JavaScript methods. Rust currently has two ways to do this: [bindgen] and
//! [stdweb]. Getrandom supports using either one by enabling the
//! `wasm-bindgen` or `stdweb` crate features. Note that if both features are
//! enabled, `wasm-bindgen` will be used. If neither feature is enabled, calls
//! to `getrandom` will always fail at runtime.
//!
//! [bindgen]: https://github.com/rust-lang/rust-bindgen
//! [stdweb]: https://github.com/koute/stdweb
//!
//! ## Early boot
//!
//! It is possible that early in the boot process the OS hasn't had enough time
//! yet to collect entropy to securely seed its RNG, especially on virtual
//! machines.
//!
//! Some operating systems always block the thread until the RNG is securely
//! seeded. This can take anywhere from a few seconds to more than a minute.
//! Others make a best effort to use a seed from before the shutdown and don't
//! document much.
//!
//! A few, Linux, NetBSD and Solaris, offer a choice between blocking and
//! getting an error; in these cases we always choose to block.
//!
//! On Linux (when the `genrandom` system call is not available) and on NetBSD
//! reading from `/dev/urandom` never blocks, even when the OS hasn't collected
//! enough entropy yet. To avoid returning low-entropy bytes, we first read from
//! `/dev/random` and only switch to `/dev/urandom` once this has succeeded.
//!
//! # Error handling
//!
//! We always choose failure over returning insecure "random" bytes. In general,
//! on supported platforms, failure is highly unlikely, though not impossible.
//! If an error does occur, then it is likely that it will occur on every call to
//! `getrandom`, hence after the first successful call one can be reasonably
//! confident that no errors will occur.
//!
//! On unsupported platforms, `getrandom` always fails. See the [`Error`] type
//! for more information on what data is returned on failure.
//!
//! [1]: http://man7.org/linux/man-pages/man2/getrandom.2.html
//! [2]: http://man7.org/linux/man-pages/man4/urandom.4.html
//! [3]: https://docs.microsoft.com/en-us/windows/desktop/api/ntsecapi/nf-ntsecapi-rtlgenrandom
//! [4]: https://developer.apple.com/documentation/security/1399291-secrandomcopybytes?language=objc
//! [5]: https://www.freebsd.org/cgi/man.cgi?query=random&sektion=4
//! [6]: https://man.openbsd.org/getentropy.2
//! [7]: http://netbsd.gw.com/cgi-bin/man-cgi?random+4+NetBSD-current
//! [8]: https://leaf.dragonflybsd.org/cgi/web-man?command=random&section=4
//! [9]: https://docs.oracle.com/cd/E88353_01/html/E37841/getrandom-2.html
//! [10]: https://docs.oracle.com/cd/E86824_01/html/E54777/random-7d.html
//! [11]: https://fuchsia.dev/fuchsia-src/zircon/syscalls/cprng_draw
//! [12]: https://github.com/redox-os/randd/blob/master/src/main.rs
//! [13]: https://github.com/nuxinl/cloudabi#random_get
//! [14]: https://www.w3.org/TR/WebCryptoAPI/#Crypto-method-getRandomValues
//! [15]: https://nodejs.org/api/crypto.html#crypto_crypto_randombytes_size_callback
//! [16]: #support-for-webassembly-and-amsjs
//! [17]: https://github.com/WebAssembly/WASI/blob/master/design/WASI-core.md#__wasi_random_get
//! [18]: https://software.intel.com/en-us/articles/intel-digital-random-number-generator-drng-software-implementation-guide
//! [19]: https://www.unix.com/man-page/mojave/2/getentropy/
//! [20]: https://www.unix.com/man-page/mojave/4/random/
//! [21]: https://www.freebsd.org/cgi/man.cgi?query=getrandom&manpath=FreeBSD+12.0-stable
//! [22]: https://github.com/hermitcore/libhermit-rs/blob/09c38b0371cee6f56a541400ba453e319e43db53/src/syscalls/random.rs#L21

#![doc(
    html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk.png",
    html_favicon_url = "https://www.rust-lang.org/favicon.ico",
    html_root_url = "https://rust-random.github.io/rand/"
)]
#![no_std]
#![cfg_attr(feature = "stdweb", recursion_limit = "128")]
#![warn(rust_2018_idioms, unused_lifetimes, missing_docs)]

#[macro_use]
extern crate cfg_if;

cfg_if! {
    if #[cfg(feature = "log")] {
        #[allow(unused)]
        #[macro_use]
        extern crate log;
    } else {
        #[allow(unused)]
        macro_rules! error {
            ($($x:tt)*) => {};
        }
        #[allow(unused)]
        macro_rules! warn {
            ($($x:tt)*) => {};
        }
        #[allow(unused)]
        macro_rules! info {
            ($($x:tt)*) => {};
        }
    }
}

mod error;
pub use crate::error::Error;

#[allow(dead_code)]
mod util;

cfg_if! {
    // Unlike the other Unix, Fuchsia and iOS don't use the libc to make any calls.
    if #[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "emscripten",
                 target_os = "freebsd", target_os = "haiku",     target_os = "illumos",
                 target_os = "linux",   target_os = "macos",     target_os = "netbsd",
                 target_os = "openbsd", target_os = "redox",     target_os = "solaris"))] {
        #[allow(dead_code)]
        mod util_libc;
        // Keep std-only trait definitions for backwards compatiblity
        mod error_impls;
    } else if #[cfg(feature = "std")] {
        mod error_impls;
    }
}

// These targets read from a file as a fallback method.
#[cfg(any(
    target_os = "android",
    target_os = "linux",
    target_os = "macos",
    target_os = "solaris",
    target_os = "illumos",
))]
mod use_file;

// System-specific implementations.
//
// These should all provide getrandom_inner with the same signature as getrandom.
cfg_if! {
    if #[cfg(target_os = "android")] {
        #[path = "linux_android.rs"] mod imp;
    } else if #[cfg(target_os = "cloudabi")] {
        #[path = "cloudabi.rs"] mod imp;
    } else if #[cfg(target_os = "dragonfly")] {
        #[path = "use_file.rs"] mod imp;
    } else if #[cfg(target_os = "emscripten")] {
        #[path = "use_file.rs"] mod imp;
    } else if #[cfg(target_os = "freebsd")] {
        #[path = "freebsd.rs"] mod imp;
    } else if #[cfg(target_os = "fuchsia")] {
        #[path = "fuchsia.rs"] mod imp;
    } else if #[cfg(target_os = "haiku")] {
        #[path = "use_file.rs"] mod imp;
    } else if #[cfg(target_os = "illumos")] {
        #[path = "solaris_illumos.rs"] mod imp;
    } else if #[cfg(target_os = "ios")] {
        #[path = "ios.rs"] mod imp;
    } else if #[cfg(target_os = "linux")] {
        #[path = "linux_android.rs"] mod imp;
    } else if #[cfg(target_os = "macos")] {
        #[path = "macos.rs"] mod imp;
    } else if #[cfg(target_os = "netbsd")] {
        #[path = "use_file.rs"] mod imp;
    } else if #[cfg(target_os = "openbsd")] {
        #[path = "openbsd.rs"] mod imp;
    } else if #[cfg(target_os = "redox")] {
        #[path = "use_file.rs"] mod imp;
    } else if #[cfg(target_os = "solaris")] {
        #[path = "solaris_illumos.rs"] mod imp;
    } else if #[cfg(target_os = "wasi")] {
        #[path = "wasi.rs"] mod imp;
    } else if #[cfg(all(windows, getrandom_uwp))] {
        #[path = "windows_uwp.rs"] mod imp;
    } else if #[cfg(windows)] {
        #[path = "windows.rs"] mod imp;
    } else if #[cfg(all(target_arch = "x86_64", any(
                  target_os = "hermit",
                  target_os = "l4re",
                  target_os = "uefi",
                  target_env = "sgx",
              )))] {
        #[path = "rdrand.rs"] mod imp;
    } else if #[cfg(all(target_arch = "wasm32", target_os = "unknown"))] {
        cfg_if! {
            if #[cfg(feature = "wasm-bindgen")] {
                #[path = "wasm32_bindgen.rs"] mod imp;
            } else if #[cfg(feature = "stdweb")] {
                #[path = "wasm32_stdweb.rs"] mod imp;
            } else {
                // Always have an implementation for wasm32-unknown-unknown.
                // See https://github.com/rust-random/getrandom/issues/87
                #[path = "dummy.rs"] mod imp;
            }
        }
    } else if #[cfg(feature = "dummy")] {
        #[path = "dummy.rs"] mod imp;
    } else {
        compile_error!("\
            target is not supported, for more information see: \
            https://docs.rs/getrandom/#unsupported-targets\
        ");
    }
}

/// Fill `dest` with random bytes from the system's preferred random number
/// source.
///
/// This function returns an error on any failure, including partial reads. We
/// make no guarantees regarding the contents of `dest` on error. If `dest` is
/// empty, `getrandom` immediately returns success, making no calls to the
/// underlying operating system.
///
/// Blocking is possible, at least during early boot; see module documentation.
///
/// In general, `getrandom` will be fast enough for interactive usage, though
/// significantly slower than a user-space CSPRNG; for the latter consider
/// [`rand::thread_rng`](https://docs.rs/rand/*/rand/fn.thread_rng.html).
pub fn getrandom(dest: &mut [u8]) -> Result<(), error::Error> {
    if dest.is_empty() {
        return Ok(())
    }
    imp::getrandom_inner(dest)
}
<commit_msg>Run Rustfmt and make formatting error fail the CI<commit_after>// Copyright 2019 Developers of the Rand project.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Interface to the random number generator of the operating system.
//!
//! # Platform sources
//!
//! | OS               | interface
//! |------------------|---------------------------------------------------------
//! | Linux, Android   | [`getrandom`][1] system call if available, otherwise [`/dev/urandom`][2] after successfully polling `/dev/random`
//! | Windows          | [`RtlGenRandom`][3]
//! | macOS            | [`getentropy()`][19] if available, otherwise [`/dev/random`][20] (identical to `/dev/urandom`)
//! | iOS              | [`SecRandomCopyBytes`][4]
//! | FreeBSD          | [`getrandom()`][21] if available, otherwise [`kern.arandom`][5]
//! | OpenBSD          | [`getentropy`][6]
//! | NetBSD           | [`/dev/urandom`][7] after successfully polling `/dev/random`
//! | Dragonfly BSD    | [`/dev/random`][8]
//! | Solaris, illumos | [`getrandom`][9] system call if available, otherwise [`/dev/random`][10]
//! | Fuchsia OS       | [`cprng_draw`][11]
//! | Redox            | [`rand:`][12]
//! | CloudABI         | [`cloudabi_sys_random_get`][13]
//! | Haiku            | `/dev/random` (identical to `/dev/urandom`)
//! | L4RE, SGX, UEFI  | [RDRAND][18]
//! | Hermit           | [RDRAND][18] as [`sys_rand`][22] is currently broken.
//! | Web browsers     | [`Crypto.getRandomValues`][14] (see [Support for WebAssembly and ams.js][14])
//! | Node.js          | [`crypto.randomBytes`][15] (see [Support for WebAssembly and ams.js][16])
//! | WASI             | [`__wasi_random_get`][17]
//!
//! Getrandom doesn't have a blanket implementation for all Unix-like operating
//! systems that reads from `/dev/urandom`. This ensures all supported operating
//! systems are using the recommended interface and respect maximum buffer
//! sizes.
//!
//! ## Unsupported targets
//!
//! By default, compiling `getrandom` for an unsupported target will result in
//! a compilation error. If you want to build an application which uses `getrandom`
//! for such target, you can either:
//! - Use [`[replace]`][replace] or [`[patch]`][patch] section in your `Cargo.toml`
//! to switch to a custom implementation with a support of your target.
//! - Enable the `dummy` feature to have getrandom use an implementation that always
//! fails at run-time on unsupported targets.
//!
//! [replace]: https://doc.rust-lang.org/cargo/reference/manifest.html#the-replace-section
//! [patch]: https://doc.rust-lang.org/cargo/reference/manifest.html#the-patch-section
//!
//! ## Support for WebAssembly and asm.js
//!
//! Getrandom supports all of Rust's current `wasm32` targets, and it works with
//! both Node.js and web browsers. The three Emscripten targets
//! `asmjs-unknown-emscripten`, `wasm32-unknown-emscripten`, and
//! `wasm32-experimental-emscripten` use Emscripten's `/dev/random` emulation.
//! The WASI target `wasm32-wasi` uses the [`__wasi_random_get`][17] function
//! defined by the WASI standard.
//!
//! Getrandom also supports `wasm32-unknown-unknown` by directly calling
//! JavaScript methods. Rust currently has two ways to do this: [bindgen] and
//! [stdweb]. Getrandom supports using either one by enabling the
//! `wasm-bindgen` or `stdweb` crate features. Note that if both features are
//! enabled, `wasm-bindgen` will be used. If neither feature is enabled, calls
//! to `getrandom` will always fail at runtime.
//!
//! [bindgen]: https://github.com/rust-lang/rust-bindgen
//! [stdweb]: https://github.com/koute/stdweb
//!
//! ## Early boot
//!
//! It is possible that early in the boot process the OS hasn't had enough time
//! yet to collect entropy to securely seed its RNG, especially on virtual
//! machines.
//!
//! Some operating systems always block the thread until the RNG is securely
//! seeded. This can take anywhere from a few seconds to more than a minute.
//! Others make a best effort to use a seed from before the shutdown and don't
//! document much.
//!
//! A few, Linux, NetBSD and Solaris, offer a choice between blocking and
//! getting an error; in these cases we always choose to block.
//!
//! On Linux (when the `genrandom` system call is not available) and on NetBSD
//! reading from `/dev/urandom` never blocks, even when the OS hasn't collected
//! enough entropy yet. To avoid returning low-entropy bytes, we first read from
//! `/dev/random` and only switch to `/dev/urandom` once this has succeeded.
//!
//! # Error handling
//!
//! We always choose failure over returning insecure "random" bytes. In general,
//! on supported platforms, failure is highly unlikely, though not impossible.
//! If an error does occur, then it is likely that it will occur on every call to
//! `getrandom`, hence after the first successful call one can be reasonably
//! confident that no errors will occur.
//!
//! On unsupported platforms, `getrandom` always fails. See the [`Error`] type
//! for more information on what data is returned on failure.
//!
//! [1]: http://man7.org/linux/man-pages/man2/getrandom.2.html
//! [2]: http://man7.org/linux/man-pages/man4/urandom.4.html
//! [3]: https://docs.microsoft.com/en-us/windows/desktop/api/ntsecapi/nf-ntsecapi-rtlgenrandom
//! [4]: https://developer.apple.com/documentation/security/1399291-secrandomcopybytes?language=objc
//! [5]: https://www.freebsd.org/cgi/man.cgi?query=random&sektion=4
//! [6]: https://man.openbsd.org/getentropy.2
//! [7]: http://netbsd.gw.com/cgi-bin/man-cgi?random+4+NetBSD-current
//! [8]: https://leaf.dragonflybsd.org/cgi/web-man?command=random&section=4
//! [9]: https://docs.oracle.com/cd/E88353_01/html/E37841/getrandom-2.html
//! [10]: https://docs.oracle.com/cd/E86824_01/html/E54777/random-7d.html
//! [11]: https://fuchsia.dev/fuchsia-src/zircon/syscalls/cprng_draw
//! [12]: https://github.com/redox-os/randd/blob/master/src/main.rs
//! [13]: https://github.com/nuxinl/cloudabi#random_get
//! [14]: https://www.w3.org/TR/WebCryptoAPI/#Crypto-method-getRandomValues
//! [15]: https://nodejs.org/api/crypto.html#crypto_crypto_randombytes_size_callback
//! [16]: #support-for-webassembly-and-amsjs
//! [17]: https://github.com/WebAssembly/WASI/blob/master/design/WASI-core.md#__wasi_random_get
//! [18]: https://software.intel.com/en-us/articles/intel-digital-random-number-generator-drng-software-implementation-guide
//! [19]: https://www.unix.com/man-page/mojave/2/getentropy/
//! [20]: https://www.unix.com/man-page/mojave/4/random/
//! [21]: https://www.freebsd.org/cgi/man.cgi?query=getrandom&manpath=FreeBSD+12.0-stable
//! [22]: https://github.com/hermitcore/libhermit-rs/blob/09c38b0371cee6f56a541400ba453e319e43db53/src/syscalls/random.rs#L21

#![doc(
    html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk.png",
    html_favicon_url = "https://www.rust-lang.org/favicon.ico",
    html_root_url = "https://rust-random.github.io/rand/"
)]
#![no_std]
#![cfg_attr(feature = "stdweb", recursion_limit = "128")]
#![warn(rust_2018_idioms, unused_lifetimes, missing_docs)]

#[macro_use]
extern crate cfg_if;

cfg_if! {
    if #[cfg(feature = "log")] {
        #[allow(unused)]
        #[macro_use]
        extern crate log;
    } else {
        #[allow(unused)]
        macro_rules! error {
            ($($x:tt)*) => {};
        }
        #[allow(unused)]
        macro_rules! warn {
            ($($x:tt)*) => {};
        }
        #[allow(unused)]
        macro_rules! info {
            ($($x:tt)*) => {};
        }
    }
}

mod error;
pub use crate::error::Error;

#[allow(dead_code)]
mod util;

cfg_if! {
    // Unlike the other Unix, Fuchsia and iOS don't use the libc to make any calls.
    if #[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "emscripten",
                 target_os = "freebsd", target_os = "haiku",     target_os = "illumos",
                 target_os = "linux",   target_os = "macos",     target_os = "netbsd",
                 target_os = "openbsd", target_os = "redox",     target_os = "solaris"))] {
        #[allow(dead_code)]
        mod util_libc;
        // Keep std-only trait definitions for backwards compatiblity
        mod error_impls;
    } else if #[cfg(feature = "std")] {
        mod error_impls;
    }
}

// These targets read from a file as a fallback method.
#[cfg(any(
    target_os = "android",
    target_os = "linux",
    target_os = "macos",
    target_os = "solaris",
    target_os = "illumos",
))]
mod use_file;

// System-specific implementations.
//
// These should all provide getrandom_inner with the same signature as getrandom.
cfg_if! {
    if #[cfg(target_os = "android")] {
        #[path = "linux_android.rs"] mod imp;
    } else if #[cfg(target_os = "cloudabi")] {
        #[path = "cloudabi.rs"] mod imp;
    } else if #[cfg(target_os = "dragonfly")] {
        #[path = "use_file.rs"] mod imp;
    } else if #[cfg(target_os = "emscripten")] {
        #[path = "use_file.rs"] mod imp;
    } else if #[cfg(target_os = "freebsd")] {
        #[path = "freebsd.rs"] mod imp;
    } else if #[cfg(target_os = "fuchsia")] {
        #[path = "fuchsia.rs"] mod imp;
    } else if #[cfg(target_os = "haiku")] {
        #[path = "use_file.rs"] mod imp;
    } else if #[cfg(target_os = "illumos")] {
        #[path = "solaris_illumos.rs"] mod imp;
    } else if #[cfg(target_os = "ios")] {
        #[path = "ios.rs"] mod imp;
    } else if #[cfg(target_os = "linux")] {
        #[path = "linux_android.rs"] mod imp;
    } else if #[cfg(target_os = "macos")] {
        #[path = "macos.rs"] mod imp;
    } else if #[cfg(target_os = "netbsd")] {
        #[path = "use_file.rs"] mod imp;
    } else if #[cfg(target_os = "openbsd")] {
        #[path = "openbsd.rs"] mod imp;
    } else if #[cfg(target_os = "redox")] {
        #[path = "use_file.rs"] mod imp;
    } else if #[cfg(target_os = "solaris")] {
        #[path = "solaris_illumos.rs"] mod imp;
    } else if #[cfg(target_os = "wasi")] {
        #[path = "wasi.rs"] mod imp;
    } else if #[cfg(all(windows, getrandom_uwp))] {
        #[path = "windows_uwp.rs"] mod imp;
    } else if #[cfg(windows)] {
        #[path = "windows.rs"] mod imp;
    } else if #[cfg(all(target_arch = "x86_64", any(
                  target_os = "hermit",
                  target_os = "l4re",
                  target_os = "uefi",
                  target_env = "sgx",
              )))] {
        #[path = "rdrand.rs"] mod imp;
    } else if #[cfg(all(target_arch = "wasm32", target_os = "unknown"))] {
        cfg_if! {
            if #[cfg(feature = "wasm-bindgen")] {
                #[path = "wasm32_bindgen.rs"] mod imp;
            } else if #[cfg(feature = "stdweb")] {
                #[path = "wasm32_stdweb.rs"] mod imp;
            } else {
                // Always have an implementation for wasm32-unknown-unknown.
                // See https://github.com/rust-random/getrandom/issues/87
                #[path = "dummy.rs"] mod imp;
            }
        }
    } else if #[cfg(feature = "dummy")] {
        #[path = "dummy.rs"] mod imp;
    } else {
        compile_error!("\
            target is not supported, for more information see: \
            https://docs.rs/getrandom/#unsupported-targets\
        ");
    }
}

/// Fill `dest` with random bytes from the system's preferred random number
/// source.
///
/// This function returns an error on any failure, including partial reads. We
/// make no guarantees regarding the contents of `dest` on error. If `dest` is
/// empty, `getrandom` immediately returns success, making no calls to the
/// underlying operating system.
///
/// Blocking is possible, at least during early boot; see module documentation.
///
/// In general, `getrandom` will be fast enough for interactive usage, though
/// significantly slower than a user-space CSPRNG; for the latter consider
/// [`rand::thread_rng`](https://docs.rs/rand/*/rand/fn.thread_rng.html).
pub fn getrandom(dest: &mut [u8]) -> Result<(), error::Error> {
    if dest.is_empty() {
        return Ok(());
    }
    imp::getrandom_inner(dest)
}
<|endoftext|>


<commit_before>//! # Handlebars
//! Handlebars is a modern and extensible templating solution originally created in the JavaScript world. It's used by many popular frameworks like [Ember.js](http://emberjs.com) and Chaplin. It's also ported to some other platforms such as [Java](https://github.com/jknack/handlebars.java).
//!
//! And this is handlebars Rust implementation, designed for server-side page generation. It's a general-purpose library so you use it for any kind of text generation.
//!
//! ## Why (this) Handlebars?
//!
//! Handlebars is a real-world templating system that you can use to build
//! your application without pain.
//!
//! ### Features
//!
//! #### Isolation of Rust and HTML
//!
//! This library doesn't attempt to use some macro magic to allow you to
//! write your template within your rust code. I admit that it's fun to do
//! that but it doesn't fit real-world use case.
//!
//! #### Limited but essential control structure built-in
//!
//! Only essential control directive `if` and `each` were built-in. This
//! prevents you to put too much application logic into your template.
//!
//! #### Extensible helper system
//!
//! You can write your own helper with Rust! It can be a block helper or
//! inline helper. Put you logic into the helper and don't repeat
//! yourself.
//!
//! A helper can be as a simple as a Rust function like:
//!
//! ```ignore
//! fn hex_helper (h: &Helper, _: &Handlebars, rc: &mut RenderContext) -> Result<(), RenderError> {
//!     let param = h.param(0).unwrap();
//!     let rendered = format!("{:x}", param.value().render());
//!     try!(rc.writer.write(rendered.into_bytes().as_ref()));
//!     Ok(())
//! }
//!
//! /// register the helper
//! handlebars.register_helper("hex", Box::new(hex_helper));
//! ```
//!
//! #### Template inheritance
//!
//! Every time I look into a templating system, I will investigate its
//! support for [template
//! inheritance](https://docs.djangoproject.com/en/1.9/ref/templates/language/#template-inh
//! eritance).
//!
//! Template include is not enough. In most case you will need a skeleton
//! of page as parent (header, footer, etc.), and embed you page into this
//! parent.
//!
//! You can find a real example for template inheritance in
//! `examples/partials.rs`, and templates used by this file.
//!
//! ### Limitations
//!
//! * This implementation is **not fully compatible** with the original
//!   javascript version
//! * As a static typed language, it's a little verbose to use handlebars
//! * You will have to make your data `ToJson`-able, so we can render
//!   it. If you are on nightly channel, we have [a syntax
//!   extension](https://github.com/sunng87/tojson_macros) to generate
//!   default `ToJson` implementation for you. If you use
//!   [serde](https://github.com/serde-rs/serde), you can enable
//!   `serde_type` feature of handlebars-rust and add `#[Serialize]` for
//!   your types.
//!
//! ## Usage
//!
//! ### Template Creation and Registration
//!
//! Templates are created from String and registered to `Handlebars` with a name.
//!
//! ```
//!
//! extern crate handlebars;
//!
//! use handlebars::Handlebars;
//!
//! fn main() {
//!   let mut handlebars = Handlebars::new();
//!   let source = "hello {{world}}";
//!
//!   //compile returns an Option, we use unwrap() to deref it directly here
//!   handlebars.register_template_string("helloworld", source.to_string())
//!           .ok().unwrap();
//! }
//! ```
//!
//! On registeration, the template is parsed and cached in the registry. So further
//! usage will benifite from the one-time work. Also features like include, inheritance
//! that involves template reference requires you to register those template first so
//! the registry can find it.
//!
//! If you template is small or just to expirement, you can use `template_render` APIs
//! without registeration.
//!
//! ### Rendering Something
//!
//! I should say that rendering is a little tricky. Since handlebars is originally a JavaScript templating framework. It supports dynamic features like duck-typing, truthy/falsey values. But for a static language like Rust, this is a little difficult. As a solution, I'm using the `serialize::json::Json` internally for data rendering, which seems good by far.
//!
//! That means, if you want to render something, you have to ensure that it implements the `rustc_serialize::json::ToJson` trait. Luckily, most built-in types already have trait. However, if you want to render your custom struct, you need to implement this trait manually, or use [tojson_macros](https://github.com/sunng87/tojson_macros) to generate default `ToJson` implementation.
//!
//! You can use default `render` function to render a template into `String`. From 0.9, there's `renderw` to render text into anything of `std::io::Write`.
//!
//! From 0.13, we also support serde types by a feature flag `serde_type`.
//!
//! ```ignore
//! extern crate rustc_serialize;
//! extern crate handlebars;
//!
//! use rustc_serialize::json::{Json, ToJson};
//! use std::collections::BTreeMap;
//!
//! use handlebars::Handlebars;
//!
//! struct Person {
//!   name: String,
//!   age: i16,
//! }
//!
//! impl ToJson for Person {
//!   fn to_json(&self) -> Json {
//!     let mut m: BTreeMap<String, Json> = BTreeMap::new();
//!     m.insert("name".to_string(), self.name.to_json());
//!     m.insert("age".to_string(), self.age.to_json());
//!     m.to_json()
//!   }
//! }
//!
//! fn main() {
//!   let source = "Hello, {{name}}";
//!
//!   let mut handlebars = Handlebars::new();
//!   handlebars.register_template_string("hello", source.to_string())
//!           .ok().unwrap();
//!
//!   let data = Person {
//!       name: "Ning Sun".to_string(),
//!       age: 27
//!   };
//!   let result = handlebars.render("hello", &data);
//! }
//! ```
//!
//! Or if you don't need the template to be cached or referenced by other ones, you can
//! simply render it without registering.
//!
//! ```ignore
//! fn main() {
//!   let source = "Hello, {{name}}";
//!
//!   let mut handlebars = Handlebars::new();
//!
//!   let data = Person {
//!       name: "Ning Sun".to_string(),
//!       age: 27
//!   };
//!   let result = handlebars.template_render("Hello, {{name}}", &data);
//! }
//! ```
//!
//!
//! #### Escaping
//!
//! As per the handlebars spec, output using `{{expression}}` is escaped by default (to be precise, the characters `&"<>` are replaced by their respective html / xml entities). However, since the use cases of a rust template engine are probably a bit more diverse than those of a JavaScript one, this implementation allows the user to supply a custom escape function to be used instead. For more information see the `EscapeFn` type and `Handlebars::register_escape_fn()` method.
//!
//! ### Custom Helper
//!
//! Handlebars is nothing without helpers. You can also create your own helpers with rust. Helpers in handlebars-rust are custom struct implements the `HelperDef` trait, concretely, the `call` function. For your convenience, most of stateless helpers can be implemented as bare functions.
//!
//! ```
//!
//! extern crate handlebars;
//!
//! use std::io::Write;
//! use handlebars::{Handlebars, HelperDef, RenderError, RenderContext, Helper, Context, JsonRender};
//!
//! // implement by a structure impls HelperDef
//! #[derive(Clone, Copy)]
//! struct SimpleHelper;
//!
//! impl HelperDef for SimpleHelper {
//!   fn call(&self, h: &Helper, _: &Handlebars, rc: &mut RenderContext) -> Result<(), RenderError> {
//!     let param = h.param(0).unwrap();
//!
//!     try!(rc.writer.write("Ny helper dumps: ".as_bytes()));
//!     try!(rc.writer.write(param.value().render().into_bytes().as_ref()));
//!     Ok(())
//!   }
//! }
//!
//! // implement via bare function
//! fn another_simple_helper (h: &Helper, _: &Handlebars, rc: &mut RenderContext) -> Result<(), RenderError> {
//!     let param = h.param(0).unwrap();
//!
//!     try!(rc.writer.write("My second helper dumps: ".as_bytes()));
//!     try!(rc.writer.write(param.value().render().into_bytes().as_ref()));
//!     Ok(())
//! }
//!
//!
//! fn main() {
//!   let mut handlebars = Handlebars::new();
//!   handlebars.register_helper("simple-helper", Box::new(SimpleHelper));
//!   handlebars.register_helper("another-simple-helper", Box::new(another_simple_helper));
//!   // via closure
//!   handlebars.register_helper("closure-helper",
//!       Box::new(|h: &Helper, r: &Handlebars, rc: &mut RenderContext| -> Result<(), RenderError>{
//!         try!(rc.writer.write("...".as_bytes()));
//!         Ok(())
//!       }));
//!
//!   //...
//! }
//! ```
//!
//! #### Arguments of HelpDef
//!
//! You can get data from the `Helper` argument about the template information:
//!
//! * `name()` for the helper name. This is known to you for most situation but if you are defining `helperMissing` or `blockHelperMissing`, this is important.
//! * `params()` is a vector of String as params in helper, like `{{#somehelper param1 param2 param3}}`.
//! * `hash()` is a map of String key and Json value, defined in helper as `{{@somehelper a=1 b="2" c=true}}`.
//! * `template()` gives you the nested template of block helper.
//! * `inverse()` gives you the inversed template of it, inversed template is the template behind `{{else}}`.
//!
//! You can learn more about helpers by looking into source code of built-in helpers.
//!
//! #### Built-in Helpers
//!
//! * `{{#raw}} ... {{/raw}}` escape handlebars expression within the block
//! * `{{#if ...}} ... {{else}} ... {{/if}}` if-else block
//! * `{{#unless ...}} ... {{else}} .. {{/unless}}` if-not-else block
//! * `{{#each ...}} ... {{/each}}` iterates over an array or object. Handlebar-rust doesn't support mustach iteration syntax so use this instead.
//! * `{{#with ...}} ... {{/with}}` change current context. Similar to {{#each}}, used for replace corresponding mustach syntax.
//! * `{{lookup ... ...}}` get value from array by `@index` or `@key`
//! * `{{#partial ...}} ... {{/partial}}` template reuse, used to replace block with same name
//! * `{{#block ...}} ... {{/block}}` template reuse, used to be replaced by partial with same name, with default content if partial not found.
//! * `{{> ...}}` include template with name
//! * `{{log ...}}` log value with rust logger, default level: INFO. Currently you cannot change the level.
//!

#![allow(dead_code)]
#![recursion_limit = "200"]

#[macro_use]
extern crate log;
#[macro_use]
extern crate quick_error;
#[macro_use]
extern crate pest;
#[macro_use]
extern crate lazy_static;

extern crate regex;

#[cfg(test)]
#[macro_use]
extern crate maplit;

#[cfg(all(feature = "rustc_ser_type", not(feature = "serde_type")))]
extern crate rustc_serialize as serialize;

#[cfg(feature = "serde_type")]
extern crate serde_json;

pub use self::template::Template;
pub use self::error::{TemplateError, TemplateFileError, TemplateRenderError};
pub use self::registry::{EscapeFn, no_escape, html_escape, Registry as Handlebars};
pub use self::render::{Renderable, RenderError, RenderContext, Helper, ContextJson};
pub use self::helpers::HelperDef;
pub use self::context::{Context, JsonRender, JsonTruthy};

mod grammar;
mod template;
mod error;
mod registry;
mod render;
mod helpers;
mod context;
mod support;
mod directives;
#[cfg(feature="partial4")]
mod partial;
<commit_msg>(feat) reexport some decorator/directive api<commit_after>//! # Handlebars
//! Handlebars is a modern and extensible templating solution originally created in the JavaScript world. It's used by many popular frameworks like [Ember.js](http://emberjs.com) and Chaplin. It's also ported to some other platforms such as [Java](https://github.com/jknack/handlebars.java).
//!
//! And this is handlebars Rust implementation, designed for server-side page generation. It's a general-purpose library so you use it for any kind of text generation.
//!
//! ## Why (this) Handlebars?
//!
//! Handlebars is a real-world templating system that you can use to build
//! your application without pain.
//!
//! ### Features
//!
//! #### Isolation of Rust and HTML
//!
//! This library doesn't attempt to use some macro magic to allow you to
//! write your template within your rust code. I admit that it's fun to do
//! that but it doesn't fit real-world use case.
//!
//! #### Limited but essential control structure built-in
//!
//! Only essential control directive `if` and `each` were built-in. This
//! prevents you to put too much application logic into your template.
//!
//! #### Extensible helper system
//!
//! You can write your own helper with Rust! It can be a block helper or
//! inline helper. Put you logic into the helper and don't repeat
//! yourself.
//!
//! A helper can be as a simple as a Rust function like:
//!
//! ```ignore
//! fn hex_helper (h: &Helper, _: &Handlebars, rc: &mut RenderContext) -> Result<(), RenderError> {
//!     let param = h.param(0).unwrap();
//!     let rendered = format!("{:x}", param.value().render());
//!     try!(rc.writer.write(rendered.into_bytes().as_ref()));
//!     Ok(())
//! }
//!
//! /// register the helper
//! handlebars.register_helper("hex", Box::new(hex_helper));
//! ```
//!
//! #### Template inheritance
//!
//! Every time I look into a templating system, I will investigate its
//! support for [template
//! inheritance](https://docs.djangoproject.com/en/1.9/ref/templates/language/#template-inh
//! eritance).
//!
//! Template include is not enough. In most case you will need a skeleton
//! of page as parent (header, footer, etc.), and embed you page into this
//! parent.
//!
//! You can find a real example for template inheritance in
//! `examples/partials.rs`, and templates used by this file.
//!
//! ### Limitations
//!
//! * This implementation is **not fully compatible** with the original
//!   javascript version
//! * As a static typed language, it's a little verbose to use handlebars
//! * You will have to make your data `ToJson`-able, so we can render
//!   it. If you are on nightly channel, we have [a syntax
//!   extension](https://github.com/sunng87/tojson_macros) to generate
//!   default `ToJson` implementation for you. If you use
//!   [serde](https://github.com/serde-rs/serde), you can enable
//!   `serde_type` feature of handlebars-rust and add `#[Serialize]` for
//!   your types.
//!
//! ## Usage
//!
//! ### Template Creation and Registration
//!
//! Templates are created from String and registered to `Handlebars` with a name.
//!
//! ```
//!
//! extern crate handlebars;
//!
//! use handlebars::Handlebars;
//!
//! fn main() {
//!   let mut handlebars = Handlebars::new();
//!   let source = "hello {{world}}";
//!
//!   //compile returns an Option, we use unwrap() to deref it directly here
//!   handlebars.register_template_string("helloworld", source.to_string())
//!           .ok().unwrap();
//! }
//! ```
//!
//! On registeration, the template is parsed and cached in the registry. So further
//! usage will benifite from the one-time work. Also features like include, inheritance
//! that involves template reference requires you to register those template first so
//! the registry can find it.
//!
//! If you template is small or just to expirement, you can use `template_render` APIs
//! without registeration.
//!
//! ### Rendering Something
//!
//! I should say that rendering is a little tricky. Since handlebars is originally a JavaScript templating framework. It supports dynamic features like duck-typing, truthy/falsey values. But for a static language like Rust, this is a little difficult. As a solution, I'm using the `serialize::json::Json` internally for data rendering, which seems good by far.
//!
//! That means, if you want to render something, you have to ensure that it implements the `rustc_serialize::json::ToJson` trait. Luckily, most built-in types already have trait. However, if you want to render your custom struct, you need to implement this trait manually, or use [tojson_macros](https://github.com/sunng87/tojson_macros) to generate default `ToJson` implementation.
//!
//! You can use default `render` function to render a template into `String`. From 0.9, there's `renderw` to render text into anything of `std::io::Write`.
//!
//! From 0.13, we also support serde types by a feature flag `serde_type`.
//!
//! ```ignore
//! extern crate rustc_serialize;
//! extern crate handlebars;
//!
//! use rustc_serialize::json::{Json, ToJson};
//! use std::collections::BTreeMap;
//!
//! use handlebars::Handlebars;
//!
//! struct Person {
//!   name: String,
//!   age: i16,
//! }
//!
//! impl ToJson for Person {
//!   fn to_json(&self) -> Json {
//!     let mut m: BTreeMap<String, Json> = BTreeMap::new();
//!     m.insert("name".to_string(), self.name.to_json());
//!     m.insert("age".to_string(), self.age.to_json());
//!     m.to_json()
//!   }
//! }
//!
//! fn main() {
//!   let source = "Hello, {{name}}";
//!
//!   let mut handlebars = Handlebars::new();
//!   handlebars.register_template_string("hello", source.to_string())
//!           .ok().unwrap();
//!
//!   let data = Person {
//!       name: "Ning Sun".to_string(),
//!       age: 27
//!   };
//!   let result = handlebars.render("hello", &data);
//! }
//! ```
//!
//! Or if you don't need the template to be cached or referenced by other ones, you can
//! simply render it without registering.
//!
//! ```ignore
//! fn main() {
//!   let source = "Hello, {{name}}";
//!
//!   let mut handlebars = Handlebars::new();
//!
//!   let data = Person {
//!       name: "Ning Sun".to_string(),
//!       age: 27
//!   };
//!   let result = handlebars.template_render("Hello, {{name}}", &data);
//! }
//! ```
//!
//!
//! #### Escaping
//!
//! As per the handlebars spec, output using `{{expression}}` is escaped by default (to be precise, the characters `&"<>` are replaced by their respective html / xml entities). However, since the use cases of a rust template engine are probably a bit more diverse than those of a JavaScript one, this implementation allows the user to supply a custom escape function to be used instead. For more information see the `EscapeFn` type and `Handlebars::register_escape_fn()` method.
//!
//! ### Custom Helper
//!
//! Handlebars is nothing without helpers. You can also create your own helpers with rust. Helpers in handlebars-rust are custom struct implements the `HelperDef` trait, concretely, the `call` function. For your convenience, most of stateless helpers can be implemented as bare functions.
//!
//! ```
//!
//! extern crate handlebars;
//!
//! use std::io::Write;
//! use handlebars::{Handlebars, HelperDef, RenderError, RenderContext, Helper, Context, JsonRender};
//!
//! // implement by a structure impls HelperDef
//! #[derive(Clone, Copy)]
//! struct SimpleHelper;
//!
//! impl HelperDef for SimpleHelper {
//!   fn call(&self, h: &Helper, _: &Handlebars, rc: &mut RenderContext) -> Result<(), RenderError> {
//!     let param = h.param(0).unwrap();
//!
//!     try!(rc.writer.write("Ny helper dumps: ".as_bytes()));
//!     try!(rc.writer.write(param.value().render().into_bytes().as_ref()));
//!     Ok(())
//!   }
//! }
//!
//! // implement via bare function
//! fn another_simple_helper (h: &Helper, _: &Handlebars, rc: &mut RenderContext) -> Result<(), RenderError> {
//!     let param = h.param(0).unwrap();
//!
//!     try!(rc.writer.write("My second helper dumps: ".as_bytes()));
//!     try!(rc.writer.write(param.value().render().into_bytes().as_ref()));
//!     Ok(())
//! }
//!
//!
//! fn main() {
//!   let mut handlebars = Handlebars::new();
//!   handlebars.register_helper("simple-helper", Box::new(SimpleHelper));
//!   handlebars.register_helper("another-simple-helper", Box::new(another_simple_helper));
//!   // via closure
//!   handlebars.register_helper("closure-helper",
//!       Box::new(|h: &Helper, r: &Handlebars, rc: &mut RenderContext| -> Result<(), RenderError>{
//!         try!(rc.writer.write("...".as_bytes()));
//!         Ok(())
//!       }));
//!
//!   //...
//! }
//! ```
//!
//! #### Arguments of HelpDef
//!
//! You can get data from the `Helper` argument about the template information:
//!
//! * `name()` for the helper name. This is known to you for most situation but if you are defining `helperMissing` or `blockHelperMissing`, this is important.
//! * `params()` is a vector of String as params in helper, like `{{#somehelper param1 param2 param3}}`.
//! * `hash()` is a map of String key and Json value, defined in helper as `{{@somehelper a=1 b="2" c=true}}`.
//! * `template()` gives you the nested template of block helper.
//! * `inverse()` gives you the inversed template of it, inversed template is the template behind `{{else}}`.
//!
//! You can learn more about helpers by looking into source code of built-in helpers.
//!
//! #### Built-in Helpers
//!
//! * `{{#raw}} ... {{/raw}}` escape handlebars expression within the block
//! * `{{#if ...}} ... {{else}} ... {{/if}}` if-else block
//! * `{{#unless ...}} ... {{else}} .. {{/unless}}` if-not-else block
//! * `{{#each ...}} ... {{/each}}` iterates over an array or object. Handlebar-rust doesn't support mustach iteration syntax so use this instead.
//! * `{{#with ...}} ... {{/with}}` change current context. Similar to {{#each}}, used for replace corresponding mustach syntax.
//! * `{{lookup ... ...}}` get value from array by `@index` or `@key`
//! * `{{#partial ...}} ... {{/partial}}` template reuse, used to replace block with same name
//! * `{{#block ...}} ... {{/block}}` template reuse, used to be replaced by partial with same name, with default content if partial not found.
//! * `{{> ...}}` include template with name
//! * `{{log ...}}` log value with rust logger, default level: INFO. Currently you cannot change the level.
//!

#![allow(dead_code)]
#![recursion_limit = "200"]

#[macro_use]
extern crate log;
#[macro_use]
extern crate quick_error;
#[macro_use]
extern crate pest;
#[macro_use]
extern crate lazy_static;

extern crate regex;

#[cfg(test)]
#[macro_use]
extern crate maplit;

#[cfg(all(feature = "rustc_ser_type", not(feature = "serde_type")))]
extern crate rustc_serialize as serialize;

#[cfg(feature = "serde_type")]
extern crate serde_json;

pub use self::template::Template;
pub use self::error::{TemplateError, TemplateFileError, TemplateRenderError};
pub use self::registry::{EscapeFn, no_escape, html_escape, Registry as Handlebars};
pub use self::render::{Renderable, RenderError, RenderContext, Helper, ContextJson,
                       Directive as Decorator};
pub use self::helpers::HelperDef;
pub use self::directives::DirectiveDef as DecoratorDef;
pub use self::context::{Context, JsonRender, JsonTruthy};

mod grammar;
mod template;
mod error;
mod registry;
mod render;
mod helpers;
mod context;
mod support;
mod directives;
#[cfg(feature="partial4")]
mod partial;
<|endoftext|>


<commit_before>//! Read from multiple input streams.
//!
//! A `FileInput` implements the `std::io::Read` trait and reads the contents of each file
//! specified (`-` means standard input), or standard input if none are given.
//!
//! An example that prints out all the lines in each of the two files specified:
//!
//! ```
//! use std::io::{BufRead,BufReader};
//! use fileinput::FileInput;
//!
//! let filenames = vec!["testdata/1", "testdata/2"];
//! let fileinput = FileInput::new(&filenames);
//! let mut reader = BufReader::new(fileinput);
//!
//! for line in reader.lines() {
//!     println!("{}", line.unwrap());
//! }
//! ```
use std::fs::File;
use std::io;
use std::io::{Read,stdin};
use std::borrow::Borrow;

/// A file source.
#[derive(Debug, Eq, PartialEq, Clone)]
pub enum Source {
    /// Read from the process's standard in.
    Stdin,
    /// Read from the specified file.
    File(String),
}

fn make_source_vec<T>(filenames: &[T]) -> Vec<Source> where T: Borrow<str> {
    if filenames.is_empty() {
        return vec![Source::Stdin];
    }

    let mut sources = Vec::with_capacity(filenames.len());
    for filename in filenames {
        sources.push(match filename.borrow() {
            "-" => Source::Stdin,
            filename => Source::File(filename.to_string()),
        });
    }
    sources
}

struct State {
    source: Source,
    reader: Box<Read>,
}

/// A wrapper which reads from multiple streams.
pub struct FileInput {
    sources: Vec<Source>,
    state: Option<State>,
}

impl FileInput {
    /// Constructs a new `FileInput` that will read from the files specified.
    pub fn new<T>(paths: &[T]) -> FileInput where T: Borrow<str> {
        FileInput {
            sources: make_source_vec(paths),
            state: None,
        }
    }

    /// Returns the current source being read from.
    ///
    /// This function will return `None` if no reading has been done yet or all the inputs have
    /// been drained.
    pub fn source(&self) -> Option<Source> {
        self.state.as_ref().map(|s| s.source.clone())
    }

    fn open_next_file(&mut self) -> io::Result<()> {
        let next_source = self.sources.remove(0);
        let reader: Box<Read> = match &next_source {
            &Source::Stdin => Box::new(stdin()),
            &Source::File(ref path) => Box::new(try!(File::open(path))),
        };

        self.state = Some(State {
            source: next_source,
            reader: reader,
        });

        Ok(())
    }
}

impl Read for FileInput {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        loop {
            if self.state.is_none() {
                if self.sources.is_empty() {
                    return Ok(0);
                }

                try!(self.open_next_file());
            }

            let bytes_read = try!(self.state.as_mut().unwrap().reader.read(buf));

            if bytes_read == 0 {
                self.state = None;
                continue;
            }

            return Ok(bytes_read);
        }
    }
}

#[cfg(test)]
mod test {
    mod source_vec {
        use super::super::{make_source_vec,Source};

        #[test]
        fn empty_list_makes_stdin() {
            let names: Vec<String> = vec![];
            let paths = make_source_vec(&names);
            assert_eq!(paths, [Source::Stdin]);
        }

        #[test]
        fn dash_makes_stdin() {
            let names = vec!["-"];
            let paths = make_source_vec(&names);
            assert_eq!(paths, [Source::Stdin]);
        }

        #[test]
        fn filename_makes_path() {
            let names = vec!["example-file"];
            let paths = make_source_vec(&names);
            assert_eq!(paths, [Source::File("example-file".to_string())]);
        }

        #[test]
        fn mixed() {
            let names = vec!["one", "two", "-", "three"];
            let paths = make_source_vec(&names);
            assert_eq!(paths, [Source::File("one".to_string()), Source::File("two".to_string()), Source::Stdin, Source::File("three".to_string())]);
        }
    }

    mod fileinput {
        use super::super::*;
        use std::io::{Read,ErrorKind,BufRead,BufReader};

        #[test]
        fn read_files() {
            let paths = vec!["testdata/1", "testdata/2"];
            let mut fileinput = FileInput::new(&paths);
            let mut buffer = String::new();

            fileinput.read_to_string(&mut buffer).unwrap();

            assert_eq!(buffer, "One.\nTwo.\nTwo.\n");
        }

        #[test]
        fn skip_empty_file() {
            let paths = vec!["testdata/1", "testdata/empty", "testdata/2"];
            let mut fileinput = FileInput::new(&paths);
            let mut buffer = String::new();

            fileinput.read_to_string(&mut buffer).unwrap();

            assert_eq!(buffer, "One.\nTwo.\nTwo.\n");
        }

        #[test]
        fn get_source() {
            let paths = vec!["testdata/1", "testdata/2"];
            let fileinput = FileInput::new(&paths);
            let mut reader = BufReader::new(fileinput);
            let mut buffer = String::new();

            assert_eq!(reader.get_ref().source(), None);
            reader.read_line(&mut buffer).unwrap();
            assert_eq!(reader.get_ref().source(), Some(Source::File("testdata/1".to_string())));
            reader.read_line(&mut buffer).unwrap();
            assert_eq!(reader.get_ref().source(), Some(Source::File("testdata/2".to_string())));
            reader.read_line(&mut buffer).unwrap();
            reader.read_line(&mut buffer).unwrap();
            assert_eq!(reader.get_ref().source(), None);
        }

        #[test]
        fn error_on_nonexistent_file() {
            let paths = vec!["testdata/NOPE"];
            let mut fileinput = FileInput::new(&paths);
            let mut buffer = String::new();
            let result = fileinput.read_to_string(&mut buffer);

            assert_eq!(result.unwrap_err().kind(), ErrorKind::NotFound);
        }
    }
}
<commit_msg>Use "Self" for less repetition.<commit_after>//! Read from multiple input streams.
//!
//! A `FileInput` implements the `std::io::Read` trait and reads the contents of each file
//! specified (`-` means standard input), or standard input if none are given.
//!
//! An example that prints out all the lines in each of the two files specified:
//!
//! ```
//! use std::io::{BufRead,BufReader};
//! use fileinput::FileInput;
//!
//! let filenames = vec!["testdata/1", "testdata/2"];
//! let fileinput = FileInput::new(&filenames);
//! let mut reader = BufReader::new(fileinput);
//!
//! for line in reader.lines() {
//!     println!("{}", line.unwrap());
//! }
//! ```
use std::fs::File;
use std::io;
use std::io::{Read,stdin};
use std::borrow::Borrow;

/// A file source.
#[derive(Debug, Eq, PartialEq, Clone)]
pub enum Source {
    /// Read from the process's standard in.
    Stdin,
    /// Read from the specified file.
    File(String),
}

fn make_source_vec<T>(filenames: &[T]) -> Vec<Source> where T: Borrow<str> {
    if filenames.is_empty() {
        return vec![Source::Stdin];
    }

    let mut sources = Vec::with_capacity(filenames.len());
    for filename in filenames {
        sources.push(match filename.borrow() {
            "-" => Source::Stdin,
            filename => Source::File(filename.to_string()),
        });
    }
    sources
}

struct State {
    source: Source,
    reader: Box<Read>,
}

/// A wrapper which reads from multiple streams.
pub struct FileInput {
    sources: Vec<Source>,
    state: Option<State>,
}

impl FileInput {
    /// Constructs a new `FileInput` that will read from the files specified.
    pub fn new<T>(paths: &[T]) -> Self where T: Borrow<str> {
        FileInput {
            sources: make_source_vec(paths),
            state: None,
        }
    }

    /// Returns the current source being read from.
    ///
    /// This function will return `None` if no reading has been done yet or all the inputs have
    /// been drained.
    pub fn source(&self) -> Option<Source> {
        self.state.as_ref().map(|s| s.source.clone())
    }

    fn open_next_file(&mut self) -> io::Result<()> {
        let next_source = self.sources.remove(0);
        let reader: Box<Read> = match &next_source {
            &Source::Stdin => Box::new(stdin()),
            &Source::File(ref path) => Box::new(try!(File::open(path))),
        };

        self.state = Some(State {
            source: next_source,
            reader: reader,
        });

        Ok(())
    }
}

impl Read for FileInput {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        loop {
            if self.state.is_none() {
                if self.sources.is_empty() {
                    return Ok(0);
                }

                try!(self.open_next_file());
            }

            let bytes_read = try!(self.state.as_mut().unwrap().reader.read(buf));

            if bytes_read == 0 {
                self.state = None;
                continue;
            }

            return Ok(bytes_read);
        }
    }
}

#[cfg(test)]
mod test {
    mod source_vec {
        use super::super::{make_source_vec,Source};

        #[test]
        fn empty_list_makes_stdin() {
            let names: Vec<String> = vec![];
            let paths = make_source_vec(&names);
            assert_eq!(paths, [Source::Stdin]);
        }

        #[test]
        fn dash_makes_stdin() {
            let names = vec!["-"];
            let paths = make_source_vec(&names);
            assert_eq!(paths, [Source::Stdin]);
        }

        #[test]
        fn filename_makes_path() {
            let names = vec!["example-file"];
            let paths = make_source_vec(&names);
            assert_eq!(paths, [Source::File("example-file".to_string())]);
        }

        #[test]
        fn mixed() {
            let names = vec!["one", "two", "-", "three"];
            let paths = make_source_vec(&names);
            assert_eq!(paths, [Source::File("one".to_string()), Source::File("two".to_string()), Source::Stdin, Source::File("three".to_string())]);
        }
    }

    mod fileinput {
        use super::super::*;
        use std::io::{Read,ErrorKind,BufRead,BufReader};

        #[test]
        fn read_files() {
            let paths = vec!["testdata/1", "testdata/2"];
            let mut fileinput = FileInput::new(&paths);
            let mut buffer = String::new();

            fileinput.read_to_string(&mut buffer).unwrap();

            assert_eq!(buffer, "One.\nTwo.\nTwo.\n");
        }

        #[test]
        fn skip_empty_file() {
            let paths = vec!["testdata/1", "testdata/empty", "testdata/2"];
            let mut fileinput = FileInput::new(&paths);
            let mut buffer = String::new();

            fileinput.read_to_string(&mut buffer).unwrap();

            assert_eq!(buffer, "One.\nTwo.\nTwo.\n");
        }

        #[test]
        fn get_source() {
            let paths = vec!["testdata/1", "testdata/2"];
            let fileinput = FileInput::new(&paths);
            let mut reader = BufReader::new(fileinput);
            let mut buffer = String::new();

            assert_eq!(reader.get_ref().source(), None);
            reader.read_line(&mut buffer).unwrap();
            assert_eq!(reader.get_ref().source(), Some(Source::File("testdata/1".to_string())));
            reader.read_line(&mut buffer).unwrap();
            assert_eq!(reader.get_ref().source(), Some(Source::File("testdata/2".to_string())));
            reader.read_line(&mut buffer).unwrap();
            reader.read_line(&mut buffer).unwrap();
            assert_eq!(reader.get_ref().source(), None);
        }

        #[test]
        fn error_on_nonexistent_file() {
            let paths = vec!["testdata/NOPE"];
            let mut fileinput = FileInput::new(&paths);
            let mut buffer = String::new();
            let result = fileinput.read_to_string(&mut buffer);

            assert_eq!(result.unwrap_err().kind(), ErrorKind::NotFound);
        }
    }
}
<|endoftext|>


<commit_before>//#![feature(step_trait)]

extern crate bit_vec ;
extern crate itertools ;
extern crate core ;

use bit_vec::BitVec;
use std::cmp ;
use itertools::Itertools ;
//use core::iter::Step ;

const SIGNBIT : i32 = 1<<31;

pub fn modneg( x : i64, y : usize) -> usize {
	(( x % y as i64) + y as i64) as usize % y
	}

pub fn int_sqrt(n :usize) -> usize {
	((n as f64).sqrt()).floor() as usize
}

#[inline]
pub fn cnt_query(mut pos : usize, counter : &[i32]) -> u32 {
	let mut acc = counter[pos] & !SIGNBIT  ;
	pos += 1 ;
	pos &= pos - 1 ;
	while pos  != 0 {
		acc+=counter[pos - 1] & !SIGNBIT;
		pos &= pos - 1;
	}
acc as u32
}

#[inline]
pub fn cnt_update(mut pos : usize, counter : &mut[i32], interval_length : usize ) {
    counter[pos] |= SIGNBIT;
    loop {
      counter[pos] -= 1;
      pos |= pos + 1;
      if pos >=interval_length {break;}
    }
    }

#[inline]
pub fn interval_clear(index : usize , offsets : &mut[usize],  counter : &mut[i32], interval_length : usize, prime : usize) {
//   (offsets[index]..interval_length).step(prime).foreach(|j| {    	if counter[j] > 0 { cnt_update( j, counter, interval_length ) ; } }) ;

   let mut j  = offsets[index] ;
   while j < interval_length {
   	if counter[j] > 0 { cnt_update( j, counter, interval_length ) ; }
   	j += prime ;
   	}
   offsets[index] = modneg( offsets[index] as i64 - interval_length as i64 , prime );
   }

 pub fn initialize_arrays(ll : usize, mu : &mut[isize], pi : &mut [usize],  primes : &mut [usize]) -> usize {
 	 	for j in  2..mu.len() {
 		if mu[j] == 1 {
 			let mut i = j ; while i <= ll  {
 			 mu[i] = match mu[i] {
 				1 => 1 - 2 * j as isize,
 				_ => -mu[i] ,
 			} ;
 		i += 2 * j - 1 ;	}
 		}
 	}
 	let mut j = 2 ; while j * j <= ll << 1 {
 		if mu[j] == 1- 2*j as isize {
 			let mut i = 2 * j * j - 2 * j + 1 ;
 			while i <= ll  {
 			mu[i] = 0 ;
 			i += 4 * j * j - 4 * j + 1 ;
 			}
 		}
 		j += 1 ;
 	}
primes[1] = 2 ;
let mut pix = 1 ;
for (i , elem) in mu.iter().enumerate().dropping(2) {
if *elem == 1 - 2 * i as isize {
 	pix += 1;
 	primes[pix] = 2 * i as usize -1 ;
}
pi[i as usize]=pix;
 }
//println!("{:?}",mu) ;
 pix
 }

 pub fn ordinary_leaves(n : usize, count : &mut i64, mu : &[isize], m : u64) {
  for i in (1..n + 1).step(2) {
  	let term1 = (mu[(i+1) >> 1]).signum() as i64;
  	*count +=  term1 * (m as i64/ i as i64) ;
 }
for i in (2..n + 1).step(4) {
	let term2 = (mu[((i >> 1)  + 1) >> 1]).signum() as i64 ;
		*count -= term2 * (m as i64 / i as i64) ;
}
}

 pub fn rphi(term: i64,  mut a : usize, bit : i8 , primes: &[usize],  total : &mut i64 )  {
	loop {
		if a == 1 {
			*total +=  bit as i64 * term ;
			break;
			}
		else if term < primes[a] as i64 {
			*total += bit as i64 ;
			break;
				}
		else {
			a -= 1;
			rphi(term / (primes[a] as i64), a, -bit, primes, total) ;
					}
				}
		}

pub fn special_leaves_type_1_substitute(b : usize, primes : &[usize], n : usize, mu : &[isize], m : u64) -> i64 {
let pp = primes[b + 1]    ; let mut acc = 0 ;
let mut j = cmp::max((n / pp) , pp ) as usize +1 ;
if j % 2 == 0 { j += 1;}
let mut i = j ; while i <= n {
//for i in (j..(n+1)).step_by(2) {
let muval1 = (mu[ ( i+1) >> 1 ] ).signum() ;
if (mu[(i + 1) >> 1]).abs() > pp as isize && muval1 != 0 {
let term = (m / (i * pp) as u64) as i64;
let mut total : i64 = 0;
rphi(term, b + 1, 1, primes, &mut total)  ;
acc += muval1 as i64 * total ;
}
i+=2 ; }
acc
}
#[inline]
pub fn special_leaves_type_1( b : usize , interval : usize ,  m1 : &mut [usize] , n : usize, pp : usize , m : u64 ,
 	 interval_boundaries : &[usize], mu : &[isize], count : &mut i64, phi : &[u64], counter : &[i32]) {
if m1[b] % 2 == 0 { m1[b] -= 1;}
let criterion = n / pp ;
while m1[b] > criterion {
   let y  = (m / (m1[b] as u64 * pp as u64 )) as usize  ; //print!("y = {} ",y) ;
   if y > interval_boundaries[interval + 1] - 2 { return ;}
   let   muvalue = mu[((m1[b]+1) >> 1) as usize];
   if muvalue.abs() > pp as isize  {
        *count -=
        muvalue.signum() as i64 * (phi[b] as i64 + cnt_query((y + 1 - interval_boundaries[interval]), counter) as i64);
   }
   m1[b] -= 2 ;
      }
  }

 #[inline]
pub fn special_leaves_type_2_initialize( b : usize, pb : usize, m : u64, t : &mut[usize], n : usize, pi : &[usize], a : usize, d2 : &mut[usize], count : &mut i64 )  {
let    term = (m / (pb as u64 * pb as u64)) as usize;
t[b] = match  term {
   term if term <= pb =>  b + 2,
   term if term < n =>  pi[(term + 1) >> 1] + 1,
   _ => a + 1,
       };
d2[b] = t[b] - 1 ;
*count += (a - d2[b]) as i64 ;
}
#[inline]
pub fn hard(b :  usize , interval : usize, y : usize, interval_boundaries : &[usize], count : &mut i64, counter : &[i32], d2 : &mut [usize]) -> ( bool, u32) {
    if y + 1 >= interval_boundaries[interval+1] {return (true,0); }
    *count+= cnt_query((y + 1 - interval_boundaries[interval]),counter) as i64;
    d2[b] -= 1;
   ( false, 1 )
    }

 #[inline]
pub fn easy_sparse(b :  usize , interval : usize, y : usize, n : usize, tt : &mut[u8], switch : &mut [bool],interval_boundaries : &[usize],
	 count : &mut i64, counter : &[i32], d2 : &mut [usize], pi : &[usize] ) -> bool  {
      if y >= n {
         if !switch[b] { switch[b]=true; return true; } else { tt[b] = 2 ; hard(b,interval,y,interval_boundaries,count,counter,d2); }
      }
      else {
         let l = pi[((y + 1) >> 1)] - b + 1;
         *count += l as i64;
         d2[b] -= 1;
      }
    false
    }
#[inline]
pub fn easy_clustered(b :  usize , interval : usize, y : usize, n : usize, tt: &mut[u8], switch : &mut [bool], interval_boundaries : &[usize],
	 count : &mut i64, counter : &[i32], d2 : &mut [usize], m : u64, pi : &[usize], p : &[usize] ) -> bool  {
     if y >= n {
        if !switch[b] { switch[b]=true; return true; } else { tt[b] = 2 ; hard(b,interval,y,interval_boundaries,count,counter,d2); }
      }
     else
      {
     let   l = pi[((y + 1) >> 1)] - b + 1;
     let  term = m / (p[b + 1] as u64 * p[b + l] as u64);
     let  dprime = pi[((term + 1) >> 1) as usize];
     if p[dprime + 1] <= int_sqrt((m / p[b + 1] as u64) as usize) || dprime <= b  {
         tt[b] = 1;
         *count += l as i64;
         d2[b] -= 1 ;
        }
      else
    {
      *count +=  (l as u32 * (d2[b] - dprime) as u32) as i64 ;
      d2[b] = dprime;
      }
    }
    false
      }

  #[inline]
    pub fn special_leaves_type_2(b: usize, interval : usize, d2 : &mut[usize], m : u64, p : &[usize], tt : &mut[u8],
    	 n : usize, switch : &mut[bool], interval_boundaries : &[usize], count : &mut i64, counter : &[i32], pi : &[usize] )  -> u32 {
    let mut s2bprimes= 0;
     loop {
      if d2[b] == b + 1 {return s2bprimes; }
      else
      {
       let y = (m / (p[b + 1] as u64 * p[d2[b]] as u64)) as usize;
       match tt[b] {
          0 => { if easy_clustered(b, interval,y,n,tt,switch,interval_boundaries,count,counter,d2,m,pi,p) { return s2bprimes;} continue ; } ,
          1 => { if easy_sparse(b,interval,y,n,tt,switch,interval_boundaries,count,counter,d2,pi) { return s2bprimes; }  continue ; } ,
          _ => { let h = hard(b,interval,y,interval_boundaries,count,counter,d2) ;
                 if (interval > 0 || counter[1] > 0) && h.0 { return s2bprimes;} s2bprimes += h.1 ; continue ; } ,
}
      }
      }
     }

  #[inline]
 pub fn sieve2( x : usize, y : usize,   p : &[usize],  block : &mut BitVec )
  {
block.clear();
   let mut i : usize = 1;
    while p[i] * p[i] <= y {
    let mut offset = modneg(1-x as i64,p[i]);
       while offset <= 1+(y-x)  {
    	 block.set(offset,true);
         offset += p[i];
        }
       i+=1 ;
    }
}
 #[inline]
 pub fn p2(interval : usize, u : &mut usize, v :  &mut usize, n : usize, w : &mut usize,
   block : &mut BitVec , p : &[usize], m : u64 , interval_boundaries : &[usize], phi2 : &mut i64, counter : &[i32], a : usize  ) -> u32
      {
  let mut p2primes = 0;
loop {
    if *u <= n { *phi2 -= (*v as i64 * (*v - 1) as i64) >> 1 ; return p2primes;}
   	if *u  < *w  {
   		*w = cmp::max(2,*u -n);
   		sieve2(*w,*u+1,p,block) ;
   		    }
	let index = *u-*w+1;
    if !block[index] {
    let y  = (m / (*u as u64)) as usize;
    if y +1 >= interval_boundaries[interval + 1] { return p2primes; }
    *phi2 += (cnt_query((y + 1 - interval_boundaries[interval]), counter) as usize + a) as i64 - 1;
    p2primes += 1;
    *v += 1;
    }
    *u -= 2;
}
}

<commit_msg>redid ordinary leaves with an iterator<commit_after>//#![feature(step_trait)]

extern crate bit_vec ;
extern crate itertools ;
extern crate core ;

use bit_vec::BitVec;
use std::cmp ;
use itertools::Itertools ;
//use core::iter::Step ;

const SIGNBIT : i32 = 1<<31;

pub fn modneg( x : i64, y : usize) -> usize {
	(( x % y as i64) + y as i64) as usize % y
	}

pub fn int_sqrt(n :usize) -> usize {
	((n as f64).sqrt()).floor() as usize
}

#[inline]
pub fn cnt_query(mut pos : usize, counter : &[i32]) -> u32 {
	let mut acc = counter[pos] & !SIGNBIT  ;
	pos += 1 ;
	pos &= pos - 1 ;
	while pos  != 0 {
		acc += counter[pos - 1] & !SIGNBIT;
		pos &= pos - 1;
	}
acc as u32
}

#[inline]
pub fn cnt_update(mut pos : usize, counter : &mut[i32], interval_length : usize ) {
    counter[pos] |= SIGNBIT;
    loop {
      counter[pos] -= 1;
      pos |= pos + 1;
      if pos >=interval_length {break;}
    }
    }

#[inline]
pub fn interval_clear(index : usize , offsets : &mut[usize],  counter : &mut[i32], interval_length : usize, prime : usize) {
//   (offsets[index]..interval_length).step(prime).foreach(|j| {    	if counter[j] > 0 { cnt_update( j, counter, interval_length ) ; } }) ; much slower

   let mut j  = offsets[index] ;
   while j < interval_length {
   	if counter[j] > 0 { cnt_update( j, counter, interval_length ) ; }
   	j += prime ;
   	}
   offsets[index] = modneg( offsets[index] as i64 - interval_length as i64 , prime );
   }

 pub fn initialize_arrays(ll : usize, mu : &mut[isize], pi : &mut [usize],  primes : &mut [usize]) -> usize {
 	 	for j in  2..mu.len() {
 		if mu[j] == 1 {
 			let mut i = j ; while i <= ll  {
 			 mu[i] = match mu[i] {
 				1 => 1 - 2 * j as isize,
 				_ => -mu[i] ,
 			} ;
 		i += 2 * j - 1 ;	}
 		}
 	}
 	let mut j = 2 ; while j * j <= ll << 1 {
 		if mu[j] == 1- 2*j as isize {
 			let mut i = 2 * j * j - 2 * j + 1 ;
 			while i <= ll  {
 			mu[i] = 0 ;
 			i += 4 * j * j - 4 * j + 1 ;
 			}
 		}
 		j += 1 ;
 	}
primes[1] = 2 ;
let mut pix = 1 ;
for (i , elem) in mu.iter().enumerate().dropping(2) {
if *elem == 1 - 2 * i as isize {
 	pix += 1;
 	primes[pix] = 2 * i as usize -1 ;
}
pi[i as usize]=pix;
 }
//println!("{:?}",mu) ;
 pix
 }

/*
 pub fn ordinary_leaves(n : usize, count : &mut i64, mu : &[isize], m : u64) {
  for i in (1..n + 1).step(2) {
  	let term = (mu[(i+1) >> 1]).signum() as i64;
  	*count +=  term * (m as i64/ i as i64) ;
 }
for i in (2..n + 1).step(4) {
	let term = (mu[((i >> 1)  + 1) >> 1]).signum() as i64 ;
	*count -= term * (m as i64 / i as i64) ;
}
}
 */
  pub fn ordinary_leaves(n : usize, count : &mut i64, mu : &[isize], m : u64) {
let mut it = (1..n+1).filter(|&i| i%4 != 0) ;
it.foreach(|i| {if i % 2 == 1
		{let term = (mu[(i+1) >> 1]).signum() as i64;
  	*count +=  term * (m as i64/ i as i64) ;}
		 else { let term = (mu[((i >> 1)  + 1) >> 1]).signum() as i64 ;
	*count -= term * (m as i64 / i as i64) ;} })
}

 pub fn rphi(term: i64,  mut a : usize, bit : i8 , primes: &[usize],  total : &mut i64 )  {
	loop {
		if a == 1 {
			*total +=  bit as i64 * term ;
			break;
			}
		else if term < primes[a] as i64 {
			*total += bit as i64 ;
			break;
				}
		else {
			a -= 1;
			rphi(term / (primes[a] as i64), a, -bit, primes, total) ;
					}
				}
		}

pub fn special_leaves_type_1_substitute(b : usize, primes : &[usize], n : usize, mu : &[isize], m : u64) -> i64 {
let pp = primes[b + 1]    ; let mut acc = 0 ;
let mut j = cmp::max((n / pp) , pp ) as usize +1 ;
if j % 2 == 0 { j += 1;}
let mut i = j ; while i <= n {
//for i in (j..(n+1)).step_by(2) {
let muval1 = (mu[ ( i+1) >> 1 ] ).signum() ;
if (mu[(i + 1) >> 1]).abs() > pp as isize && muval1 != 0 {
let term = (m / (i * pp) as u64) as i64;
let mut total : i64 = 0;
rphi(term, b + 1, 1, primes, &mut total)  ;
acc += muval1 as i64 * total ;
}
i+=2 ; }
acc
}
#[inline]
pub fn special_leaves_type_1( b : usize , interval : usize ,  m1 : &mut [usize] , n : usize, pp : usize , m : u64 ,
 	 interval_boundaries : &[usize], mu : &[isize], count : &mut i64, phi : &[u64], counter : &[i32]) {
if m1[b] % 2 == 0 { m1[b] -= 1;}
let criterion = n / pp ;
while m1[b] > criterion {
   let y  = (m / (m1[b] as u64 * pp as u64 )) as usize  ; //print!("y = {} ",y) ;
   if y > interval_boundaries[interval + 1] - 2 { return ;}
   let   muvalue = mu[((m1[b]+1) >> 1) as usize];
   if muvalue.abs() > pp as isize  {
        *count -=
        muvalue.signum() as i64 * (phi[b] as i64 + cnt_query((y + 1 - interval_boundaries[interval]), counter) as i64);
   }
   m1[b] -= 2 ;
      }
  }

 #[inline]
pub fn special_leaves_type_2_initialize( b : usize, pb : usize, m : u64, t : &mut[usize], n : usize, pi : &[usize], a : usize, d2 : &mut[usize], count : &mut i64 )  {
let    term = (m / (pb as u64 * pb as u64)) as usize;
t[b] = match  term {
   term if term <= pb =>  b + 2,
   term if term < n =>  pi[(term + 1) >> 1] + 1,
   _ => a + 1,
       };
d2[b] = t[b] - 1 ;
*count += (a - d2[b]) as i64 ;
}
#[inline]
pub fn hard(b :  usize , interval : usize, y : usize, interval_boundaries : &[usize], count : &mut i64, counter : &[i32], d2 : &mut [usize]) -> ( bool, u32) {
    if y + 1 >= interval_boundaries[interval+1] {return (true,0); }
    *count+= cnt_query((y + 1 - interval_boundaries[interval]),counter) as i64;
    d2[b] -= 1;
   ( false, 1 )
    }

 #[inline]
pub fn easy_sparse(b :  usize , interval : usize, y : usize, n : usize, tt : &mut[u8], switch : &mut [bool],interval_boundaries : &[usize],
	 count : &mut i64, counter : &[i32], d2 : &mut [usize], pi : &[usize] ) -> bool  {
      if y >= n {
         if !switch[b] { switch[b]=true; return true; } else { tt[b] = 2 ; hard(b,interval,y,interval_boundaries,count,counter,d2); }
      }
      else {
         let l = pi[((y + 1) >> 1)] - b + 1;
         *count += l as i64;
         d2[b] -= 1;
      }
    false
    }
#[inline]
pub fn easy_clustered(b :  usize , interval : usize, y : usize, n : usize, tt: &mut[u8], switch : &mut [bool], interval_boundaries : &[usize],
	 count : &mut i64, counter : &[i32], d2 : &mut [usize], m : u64, pi : &[usize], p : &[usize] ) -> bool  {
     if y >= n {
        if !switch[b] { switch[b]=true; return true; } else { tt[b] = 2 ; hard(b,interval,y,interval_boundaries,count,counter,d2); }
      }
     else
      {
     let   l = pi[((y + 1) >> 1)] - b + 1;
     let  term = m / (p[b + 1] as u64 * p[b + l] as u64);
     let  dprime = pi[((term + 1) >> 1) as usize];
     if p[dprime + 1] <= int_sqrt((m / p[b + 1] as u64) as usize) || dprime <= b  {
         tt[b] = 1;
         *count += l as i64;
         d2[b] -= 1 ;
        }
      else
    {
      *count +=  (l as u32 * (d2[b] - dprime) as u32) as i64 ;
      d2[b] = dprime;
      }
    }
    false
      }

  #[inline]
    pub fn special_leaves_type_2(b: usize, interval : usize, d2 : &mut[usize], m : u64, p : &[usize], tt : &mut[u8],
    	 n : usize, switch : &mut[bool], interval_boundaries : &[usize], count : &mut i64, counter : &[i32], pi : &[usize] )  -> u32 {
    let mut s2bprimes= 0;
     loop {
      if d2[b] == b + 1 {return s2bprimes; }
      else
      {
       let y = (m / (p[b + 1] as u64 * p[d2[b]] as u64)) as usize;
       match tt[b] {
          0 => { if easy_clustered(b, interval,y,n,tt,switch,interval_boundaries,count,counter,d2,m,pi,p) { return s2bprimes;} continue ; } ,
          1 => { if easy_sparse(b,interval,y,n,tt,switch,interval_boundaries,count,counter,d2,pi) { return s2bprimes; }  continue ; } ,
          _ => { let h = hard(b,interval,y,interval_boundaries,count,counter,d2) ;
                 if (interval > 0 || counter[1] > 0) && h.0 { return s2bprimes;} s2bprimes += h.1 ; continue ; } ,
}
      }
      }
     }

  #[inline]
 pub fn sieve2( x : usize, y : usize,   p : &[usize],  block : &mut BitVec )
  {
block.clear();
   let mut i : usize = 1;
    while p[i] * p[i] <= y {
    let mut offset = modneg(1-x as i64,p[i]);
       while offset <= 1+(y-x)  {
    	 block.set(offset,true);
         offset += p[i];
        }
       i+=1 ;
    }
}
 #[inline]
 pub fn p2(interval : usize, u : &mut usize, v :  &mut usize, n : usize, w : &mut usize,
   block : &mut BitVec , p : &[usize], m : u64 , interval_boundaries : &[usize], phi2 : &mut i64, counter : &[i32], a : usize  ) -> u32
      {
  let mut p2primes = 0;
loop {
    if *u <= n { *phi2 -= (*v as i64 * (*v - 1) as i64) >> 1 ; return p2primes;}
   	if *u  < *w  {
   		*w = cmp::max(2,*u -n);
   		sieve2(*w,*u+1,p,block) ;
   		    }
	let index = *u-*w+1;
    if !block[index] {
    let y  = (m / (*u as u64)) as usize;
    if y +1 >= interval_boundaries[interval + 1] { return p2primes; }
    *phi2 += (cnt_query((y + 1 - interval_boundaries[interval]), counter) as usize + a) as i64 - 1;
    p2primes += 1;
    *v += 1;
    }
    *u -= 2;
}
}

<|endoftext|>


<commit_before>#![allow(dead_code)]

//! A wrapper around [cfitsio].
//!
//! This wrapper attempts to add some safety to the [cfitsio] library, and allows `.fits` files to
//! be read from rust.
//!
//! The main entry point to the code is `FitsFile::open` which creates a new file, and
//! `FitsFile::create` which creates a new file.
//!
//! [cfitsio]: https://heasarc.gsfc.nasa.gov/docs/software/fitsio/fitsio.html

pub mod raw;

extern crate libc;

use raw::*;
use libc::{c_int, c_char};
use std::ptr;
use std::ffi;

/// Internal function to get the fits error description from a status code
fn status_to_string(status: c_int) -> Option<String> {
    match status {
        0 => None,
        status => {
            let mut buffer: Vec<c_char> = vec![0; 31];
            unsafe {
                ffgerr(status, buffer.as_mut_ptr());
            }
            let result_str = String::from_utf8(buffer.iter()
                    .map(|&x| x as u8)
                    .filter(|&x| x != 0)
                    .collect())
                .unwrap();
            Some(result_str)
        }
    }
}

/// General type defining what kind of HDU we're talking about
#[derive(Eq, PartialEq, Debug)]
pub enum HduType {
    ImageHDU,
    AsciiTableHDU,
    BinTableHDU,
}

/// Wrapper around a C `fitsfile` pointer.
///
/// This handles [opening][FitsFile::open], [creating][FitsFile::create] and automatically
/// closing (through the `Drop` trait) the file.
///
/// All subsequent file access is controled through this object.
///
/// [FitsFile::open]: #method.open
/// [FitsFile::create]: #method.create
pub struct FitsFile {
    fptr: *mut fitsfile,
    status: c_int,
    pub filename: String,
}

impl FitsFile {
    /// Open a fits file for reading
    ///
    /// * `filename` - Filename to pass to `cfitsio`. Can conform to the
    /// [Extended Filename Syntax][extended-filename-syntax].
    ///
    /// [extended-filename-syntax]:
    ///     https://heasarc.gsfc.nasa.gov/docs/software/fitsio/c/c_user/node82.html
    ///
    /// Examples
    ///
    /// ```
    /// # use fitsio::FitsFile;
    /// # fn main() {
    ///     let f = FitsFile::open("testdata/full_example.fits");
    /// # }
    pub fn open(filename: &str) -> Self {
        let mut fptr = ptr::null_mut();
        let mut status = 0;
        let c_filename = ffi::CString::new(filename).unwrap();

        unsafe {
            ffopen(&mut fptr as *mut *mut fitsfile,
                   c_filename.as_ptr(),
                   0,
                   &mut status);
        }

        return match status {
            0 => {
                FitsFile {
                    fptr: fptr,
                    status: status,
                    filename: filename.to_string(),
                }
            }
            status => {
                panic!("Invalid status code: {}, msg: {}",
                       status,
                       status_to_string(status).unwrap())
            }
        };

    }

    /// Create a new fits file
    ///
    /// An empty primary header is added so when the `drop` method is called, the file is valid.
    ///
    /// # Examples
    ///
    /// ```
    /// # extern crate fitsio;
    /// # extern crate tempdir;
    /// # use fitsio::FitsFile;
    /// # fn main() {
    /// # let tdir = tempdir::TempDir::new("fitsio-").unwrap();
    /// # let tdir_path = tdir.path();
    /// # let filename = tdir_path.join("test.fits");
    /// # let filename = filename.to_str().unwrap();
    /// let _ = FitsFile::create(filename);
    /// # }
    /// ```
    pub fn create(path: &str) -> Self {
        let mut fptr = ptr::null_mut();
        let mut status = 0;
        let c_filename = ffi::CString::new(path).unwrap();

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

        return match status {
            0 => {
                FitsFile {
                    fptr: fptr,
                    status: status,
                    filename: path.to_string(),
                }
            }
            status => {
                panic!("Invalid status code: {}, msg: {}",
                       status,
                       status_to_string(status).unwrap())
            }
        };
    }

    /// Function to check that the status code is ok.
    ///
    /// If the value of `self.status` is not 0 then exit the current process as an error has
    /// occurred.
    fn check(&self) {
        match self.status {
            0 => {}
            status => {
                panic!("Status code {} encountered, msg: {}",
                       status,
                       status_to_string(status).unwrap())
            }
        }
    }

    /// Returns the current HDU number, 0-indexed
    ///
    /// The FITS standard is 1-indexed (thanks Fortran), so this function
    /// converts the indexing to be 0-based so the primary HDU is at
    /// index 0.
    ///
    /// # Examples
    ///
    /// ```
    /// # use fitsio::FitsFile;
    /// let f = FitsFile::open("testdata/full_example.fits");
    /// assert_eq!(f.current_hdu_number(), 0);
    /// ```
    pub fn current_hdu_number(&self) -> u32 {
        let mut hdu_num = 0;
        unsafe {
            ffghdn(self.fptr, &mut hdu_num);
        }
        self.check();
        assert!(hdu_num >= 1);
        (hdu_num - 1) as u32
    }

    /// Change the current HDU
    ///
    /// Examples
    ///
    /// ```
    /// # use fitsio::FitsFile;
    /// # fn main() {
    /// # let mut f = FitsFile::open("testdata/full_example.fits");
    /// assert_eq!(f.current_hdu_number(), 0);
    /// f.change_hdu(1);
    /// assert_eq!(f.current_hdu_number(), 1);
    /// # }
    /// ```
    pub fn change_hdu(&mut self, hdu_num: u32) {
        let mut _hdu_type = 0;
        unsafe {
            ffmahd(self.fptr,
                   (hdu_num + 1) as i32,
                   &mut _hdu_type,
                   &mut self.status);
        }
        self.check();
    }

    /// Get which type of HDU the current HDU is
    ///
    /// Results in one of the `HduType` options.
    ///
    /// # Examples
    ///
    /// ```
    /// # use fitsio::{FitsFile, HduType};
    /// # fn main() {
    /// let mut f = FitsFile::open("testdata/full_example.fits");
    /// // Primary HDUs are always image hdus
    /// assert_eq!(f.get_hdu_type(), HduType::ImageHDU);
    /// # }
    /// ```
    pub fn get_hdu_type(&mut self) -> HduType {
        let mut hdu_type = 3;
        unsafe {
            ffghdt(self.fptr, &mut hdu_type, &mut self.status);
        }
        self.check();

        match hdu_type {
            0 => HduType::ImageHDU,
            1 => HduType::AsciiTableHDU,
            2 => HduType::BinTableHDU,
            _ => panic!("Unknown hdu type: {}", hdu_type),
        }
    }

    /// Return a `FitsHDU` object for the specified HDU
    ///
    /// # Examples
    ///
    /// ```
    /// # use fitsio::{FitsFile, HduType};
    /// # fn main() {
    /// # let mut f = FitsFile::open("testdata/full_example.fits");
    /// let primary_hdu = f.get_hdu(0);
    /// assert_eq!(primary_hdu.hdu_type, HduType::ImageHDU);
    /// # }
    /// ```
    pub fn get_hdu(&mut self, index: usize) -> FitsHDU {
        self.change_hdu(index as u32);
        let hdu_type = self.get_hdu_type();
        FitsHDU {
            fitsfile: self,
            hdu_type: hdu_type,
        }
    }
}

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

/// Wrapper around a FITS HDU
///
/// This struct is the main interface around reading and writing the file contents.
pub struct FitsHDU<'a> {
    fitsfile: &'a FitsFile,
    pub hdu_type: HduType,
}

impl<'a> FitsHDU<'a> {
    /// Read a header key as a string
    ///
    /// The user is responsible for converting the value type from a string to whatever type the
    /// header key contains.
    ///
    /// # Examples
    ///
    /// ```
    /// # use fitsio::FitsFile;
    /// # fn main() {
    /// let mut f = FitsFile::open("testdata/full_example.fits");
    /// let mut primary_hdu = f.get_hdu(0);
    /// // Image is 2-dimensional
    /// let naxis = primary_hdu.get_key("NAXIS").parse::<i32>().unwrap();
    /// assert_eq!(naxis, 2);
    /// # }
    /// ```
    pub fn get_key(&mut self, key: &str) -> String {
        let fptr = &self.fitsfile.fptr;
        let mut value: Vec<c_char> = vec![0; MAX_VALUE_LENGTH];
        let keyname = ffi::CString::new(key).unwrap();
        let mut status = 0;

        unsafe {
            ffgkys(*fptr,
                   keyname.as_ptr(),
                   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();
                return String::from_utf8(value).unwrap();
            }
            _ => {
                panic!("Invalid status code: {}, msg: {}",
                       status,
                       status_to_string(status).unwrap())
            }
        }
    }
}

mod test {
    #[test]
    fn returning_error_messages() {
        use super::status_to_string;

        assert_eq!(status_to_string(105).unwrap(),
                   "couldn't create the named file");
    }

    #[test]
    fn opening_an_existing_file() {
        use super::FitsFile;

        let f = FitsFile::open("testdata/full_example.fits");
        assert_eq!(f.status, 0);
    }

    #[test]
    fn creating_a_new_file() {
        extern crate tempdir;

        use super::FitsFile;
        let tdir = tempdir::TempDir::new("fitsio-").unwrap();
        let tdir_path = tdir.path();
        let filename = tdir_path.join("test.fits");
        assert!(!filename.exists());

        let _ = FitsFile::create(filename.to_str().unwrap());
        assert!(filename.exists());
    }

    #[test]
    fn filename_is_stored() {
        use super::FitsFile;

        let f = FitsFile::open("testdata/full_example.fits");
        assert_eq!(f.filename, "testdata/full_example.fits");
    }

    #[test]
    fn change_hdu() {
        use super::FitsFile;

        let mut f = FitsFile::open("testdata/full_example.fits");
        f.change_hdu(1);
        assert_eq!(f.current_hdu_number(), 1u32);
    }

    #[test]
    fn getting_current_hdu_number() {
        use super::FitsFile;

        let f = FitsFile::open("testdata/full_example.fits");
        assert_eq!(f.current_hdu_number(), 0u32);
    }

    #[test]
    fn getting_hdu_object() {
        use super::{FitsFile, HduType};

        let mut f = FitsFile::open("testdata/full_example.fits");

        {
            let primary_hdu = f.get_hdu(0);
            assert_eq!(primary_hdu.hdu_type, HduType::ImageHDU);
        }

        {
            let table_hdu = f.get_hdu(1);
            assert_eq!(table_hdu.hdu_type, HduType::BinTableHDU);
        }
    }
}
<commit_msg>Read in image shape for HDU<commit_after>#![allow(dead_code)]

//! A wrapper around [cfitsio].
//!
//! This wrapper attempts to add some safety to the [cfitsio] library, and allows `.fits` files to
//! be read from rust.
//!
//! The main entry point to the code is `FitsFile::open` which creates a new file, and
//! `FitsFile::create` which creates a new file.
//!
//! [cfitsio]: https://heasarc.gsfc.nasa.gov/docs/software/fitsio/fitsio.html

pub mod raw;

extern crate libc;

use raw::*;
use libc::{c_int, c_long, c_char};
use std::ptr;
use std::ffi;

/// Internal function to get the fits error description from a status code
fn status_to_string(status: c_int) -> Option<String> {
    match status {
        0 => None,
        status => {
            let mut buffer: Vec<c_char> = vec![0; 31];
            unsafe {
                ffgerr(status, buffer.as_mut_ptr());
            }
            let result_str = String::from_utf8(buffer.iter()
                    .map(|&x| x as u8)
                    .filter(|&x| x != 0)
                    .collect())
                .unwrap();
            Some(result_str)
        }
    }
}

/// General type defining what kind of HDU we're talking about
#[derive(Eq, PartialEq, Debug)]
pub enum HduType {
    ImageHDU,
    AsciiTableHDU,
    BinTableHDU,
}

/// Wrapper around a C `fitsfile` pointer.
///
/// This handles [opening][FitsFile::open], [creating][FitsFile::create] and automatically
/// closing (through the `Drop` trait) the file.
///
/// All subsequent file access is controled through this object.
///
/// [FitsFile::open]: #method.open
/// [FitsFile::create]: #method.create
pub struct FitsFile {
    fptr: *mut fitsfile,
    status: c_int,
    pub filename: String,
}

impl FitsFile {
    /// Open a fits file for reading
    ///
    /// * `filename` - Filename to pass to `cfitsio`. Can conform to the
    /// [Extended Filename Syntax][extended-filename-syntax].
    ///
    /// [extended-filename-syntax]:
    ///     https://heasarc.gsfc.nasa.gov/docs/software/fitsio/c/c_user/node82.html
    ///
    /// Examples
    ///
    /// ```
    /// # use fitsio::FitsFile;
    /// # fn main() {
    ///     let f = FitsFile::open("testdata/full_example.fits");
    /// # }
    pub fn open(filename: &str) -> Self {
        let mut fptr = ptr::null_mut();
        let mut status = 0;
        let c_filename = ffi::CString::new(filename).unwrap();

        unsafe {
            ffopen(&mut fptr as *mut *mut fitsfile,
                   c_filename.as_ptr(),
                   0,
                   &mut status);
        }

        return match status {
            0 => {
                FitsFile {
                    fptr: fptr,
                    status: status,
                    filename: filename.to_string(),
                }
            }
            status => {
                panic!("Invalid status code: {}, msg: {}",
                       status,
                       status_to_string(status).unwrap())
            }
        };

    }

    /// Create a new fits file
    ///
    /// An empty primary header is added so when the `drop` method is called, the file is valid.
    ///
    /// # Examples
    ///
    /// ```
    /// # extern crate fitsio;
    /// # extern crate tempdir;
    /// # use fitsio::FitsFile;
    /// # fn main() {
    /// # let tdir = tempdir::TempDir::new("fitsio-").unwrap();
    /// # let tdir_path = tdir.path();
    /// # let filename = tdir_path.join("test.fits");
    /// # let filename = filename.to_str().unwrap();
    /// let _ = FitsFile::create(filename);
    /// # }
    /// ```
    pub fn create(path: &str) -> Self {
        let mut fptr = ptr::null_mut();
        let mut status = 0;
        let c_filename = ffi::CString::new(path).unwrap();

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

        return match status {
            0 => {
                FitsFile {
                    fptr: fptr,
                    status: status,
                    filename: path.to_string(),
                }
            }
            status => {
                panic!("Invalid status code: {}, msg: {}",
                       status,
                       status_to_string(status).unwrap())
            }
        };
    }

    /// Function to check that the status code is ok.
    ///
    /// If the value of `self.status` is not 0 then exit the current process as an error has
    /// occurred.
    fn check(&self) {
        match self.status {
            0 => {}
            status => {
                panic!("Status code {} encountered, msg: {}",
                       status,
                       status_to_string(status).unwrap())
            }
        }
    }

    /// Returns the current HDU number, 0-indexed
    ///
    /// The FITS standard is 1-indexed (thanks Fortran), so this function
    /// converts the indexing to be 0-based so the primary HDU is at
    /// index 0.
    ///
    /// # Examples
    ///
    /// ```
    /// # use fitsio::FitsFile;
    /// let f = FitsFile::open("testdata/full_example.fits");
    /// assert_eq!(f.current_hdu_number(), 0);
    /// ```
    pub fn current_hdu_number(&self) -> u32 {
        let mut hdu_num = 0;
        unsafe {
            ffghdn(self.fptr, &mut hdu_num);
        }
        self.check();
        assert!(hdu_num >= 1);
        (hdu_num - 1) as u32
    }

    /// Change the current HDU
    ///
    /// Examples
    ///
    /// ```
    /// # use fitsio::FitsFile;
    /// # fn main() {
    /// # let mut f = FitsFile::open("testdata/full_example.fits");
    /// assert_eq!(f.current_hdu_number(), 0);
    /// f.change_hdu(1);
    /// assert_eq!(f.current_hdu_number(), 1);
    /// # }
    /// ```
    pub fn change_hdu(&mut self, hdu_num: u32) {
        let mut _hdu_type = 0;
        unsafe {
            ffmahd(self.fptr,
                   (hdu_num + 1) as i32,
                   &mut _hdu_type,
                   &mut self.status);
        }
        self.check();
    }

    /// Get which type of HDU the current HDU is
    ///
    /// Results in one of the `HduType` options.
    ///
    /// # Examples
    ///
    /// ```
    /// # use fitsio::{FitsFile, HduType};
    /// # fn main() {
    /// let mut f = FitsFile::open("testdata/full_example.fits");
    /// // Primary HDUs are always image hdus
    /// assert_eq!(f.get_hdu_type(), HduType::ImageHDU);
    /// # }
    /// ```
    pub fn get_hdu_type(&mut self) -> HduType {
        let mut hdu_type = 3;
        unsafe {
            ffghdt(self.fptr, &mut hdu_type, &mut self.status);
        }
        self.check();

        match hdu_type {
            0 => HduType::ImageHDU,
            1 => HduType::AsciiTableHDU,
            2 => HduType::BinTableHDU,
            _ => panic!("Unknown hdu type: {}", hdu_type),
        }
    }

    /// Return a `FitsHDU` object for the specified HDU
    ///
    /// # Examples
    ///
    /// ```
    /// # use fitsio::{FitsFile, HduType};
    /// # fn main() {
    /// # let mut f = FitsFile::open("testdata/full_example.fits");
    /// let primary_hdu = f.get_hdu(0);
    /// assert_eq!(primary_hdu.hdu_type, HduType::ImageHDU);
    /// # }
    /// ```
    pub fn get_hdu(&mut self, index: usize) -> FitsHDU {
        self.change_hdu(index as u32);
        let hdu_type = self.get_hdu_type();

        let image_shape = if hdu_type == HduType::ImageHDU {
            let mut naxis = vec![0, 0];
            unsafe {
                ffgisz(self.fptr, 2, naxis.as_mut_ptr(), &mut self.status);
            }
            println!("{:?}", naxis);
            (naxis[0] as usize, naxis[1] as usize)
        } else {
            (0, 0)
        };

        FitsHDU {
            fitsfile: self,
            hdu_type: hdu_type,
            image_shape: image_shape,
        }
    }
}

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

/// Wrapper around a FITS HDU
///
/// This struct is the main interface around reading and writing the file contents.
pub struct FitsHDU<'a> {
    fitsfile: &'a FitsFile,
    pub hdu_type: HduType,
    image_shape: (usize, usize),
}

impl<'a> FitsHDU<'a> {
    /// Read a header key as a string
    ///
    /// The user is responsible for converting the value type from a string to whatever type the
    /// header key contains.
    ///
    /// # Examples
    ///
    /// ```
    /// # use fitsio::FitsFile;
    /// # fn main() {
    /// let mut f = FitsFile::open("testdata/full_example.fits");
    /// let mut primary_hdu = f.get_hdu(0);
    /// // Image is 2-dimensional
    /// let naxis = primary_hdu.get_key("NAXIS").parse::<i32>().unwrap();
    /// assert_eq!(naxis, 2);
    /// # }
    /// ```
    pub fn get_key(&mut self, key: &str) -> String {
        let fptr = &self.fitsfile.fptr;
        let mut value: Vec<c_char> = vec![0; MAX_VALUE_LENGTH];
        let keyname = ffi::CString::new(key).unwrap();
        let mut status = 0;

        unsafe {
            ffgkys(*fptr,
                   keyname.as_ptr(),
                   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();
                return String::from_utf8(value).unwrap();
            }
            _ => {
                panic!("Invalid status code: {}, msg: {}",
                       status,
                       status_to_string(status).unwrap())
            }
        }
    }
}

mod test {
    #[test]
    fn returning_error_messages() {
        use super::status_to_string;

        assert_eq!(status_to_string(105).unwrap(),
                   "couldn't create the named file");
    }

    #[test]
    fn opening_an_existing_file() {
        use super::FitsFile;

        let f = FitsFile::open("testdata/full_example.fits");
        assert_eq!(f.status, 0);
    }

    #[test]
    fn creating_a_new_file() {
        extern crate tempdir;

        use super::FitsFile;
        let tdir = tempdir::TempDir::new("fitsio-").unwrap();
        let tdir_path = tdir.path();
        let filename = tdir_path.join("test.fits");
        assert!(!filename.exists());

        let _ = FitsFile::create(filename.to_str().unwrap());
        assert!(filename.exists());
    }

    #[test]
    fn filename_is_stored() {
        use super::FitsFile;

        let f = FitsFile::open("testdata/full_example.fits");
        assert_eq!(f.filename, "testdata/full_example.fits");
    }

    #[test]
    fn change_hdu() {
        use super::FitsFile;

        let mut f = FitsFile::open("testdata/full_example.fits");
        f.change_hdu(1);
        assert_eq!(f.current_hdu_number(), 1u32);
    }

    #[test]
    fn getting_current_hdu_number() {
        use super::FitsFile;

        let f = FitsFile::open("testdata/full_example.fits");
        assert_eq!(f.current_hdu_number(), 0u32);
    }

    #[test]
    fn getting_hdu_object() {
        use super::{FitsFile, HduType};

        let mut f = FitsFile::open("testdata/full_example.fits");

        {
            let primary_hdu = f.get_hdu(0);
            assert_eq!(primary_hdu.hdu_type, HduType::ImageHDU);
        }

        {
            let table_hdu = f.get_hdu(1);
            assert_eq!(table_hdu.hdu_type, HduType::BinTableHDU);
        }
    }

    #[test]
    fn get_image_dimensions() {
        use super::{FitsFile, HduType};

        let mut f = FitsFile::open("testdata/full_example.fits");
        let mut primary_hdu = f.get_hdu(0);
        assert_eq!(primary_hdu.image_shape, (100, 100));
    }
}
<|endoftext|>