<commit_before>#![unstable(reason = "not public", issue = "none", feature = "fd")]

use crate::cmp;
use crate::io::{self, Initializer, IoSlice, IoSliceMut, Read};
use crate::mem;
use crate::sync::atomic::{AtomicUsize, Ordering};
use crate::sys::cvt;
use crate::sys_common::AsInner;

use libc::{c_int, c_void};

#[derive(Debug)]
pub struct FileDesc {
    fd: c_int,
}

// The maximum read limit on most POSIX-like systems is `SSIZE_MAX`,
// with the man page quoting that if the count of bytes to read is
// greater than `SSIZE_MAX` the result is "unspecified".
//
// On macOS, however, apparently the 64-bit libc is either buggy or
// intentionally showing odd behavior by rejecting any read with a size
// larger than or equal to INT_MAX. To handle both of these the read
// size is capped on both platforms.
#[cfg(target_os = "macos")]
const READ_LIMIT: usize = c_int::MAX as usize - 1;
#[cfg(not(target_os = "macos"))]
const READ_LIMIT: usize = libc::ssize_t::MAX as usize;

fn max_iov() -> usize {
    static LIM: AtomicUsize = AtomicUsize::new(0);

    let mut lim = LIM.load(Ordering::Relaxed);
    if lim == 0 {
        let ret = unsafe { libc::sysconf(libc::_SC_IOV_MAX) };

        // 16 is the minimum value required by POSIX.
        lim = if ret > 0 { ret as usize } else { 16 };
        LIM.store(lim, Ordering::Relaxed);
    }

    lim
}

impl FileDesc {
    pub fn new(fd: c_int) -> FileDesc {
        FileDesc { fd }
    }

    pub fn raw(&self) -> c_int {
        self.fd
    }

    /// Extracts the actual file descriptor without closing it.
    pub fn into_raw(self) -> c_int {
        let fd = self.fd;
        mem::forget(self);
        fd
    }

    pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
        let ret = cvt(unsafe {
            libc::read(self.fd, buf.as_mut_ptr() as *mut c_void, cmp::min(buf.len(), READ_LIMIT))
        })?;
        Ok(ret as usize)
    }

    pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
        let ret = cvt(unsafe {
            libc::readv(
                self.fd,
                bufs.as_ptr() as *const libc::iovec,
                cmp::min(bufs.len(), max_iov()) as c_int,
            )
        })?;
        Ok(ret as usize)
    }

    #[inline]
    pub fn is_read_vectored(&self) -> bool {
        true
    }

    pub fn read_to_end(&self, buf: &mut Vec<u8>) -> io::Result<usize> {
        let mut me = self;
        (&mut me).read_to_end(buf)
    }

    pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
        #[cfg(target_os = "android")]
        use super::android::cvt_pread64;

        #[cfg(not(target_os = "android"))]
        unsafe fn cvt_pread64(
            fd: c_int,
            buf: *mut c_void,
            count: usize,
            offset: i64,
        ) -> io::Result<isize> {
            #[cfg(not(target_os = "linux"))]
            use libc::pread as pread64;
            #[cfg(target_os = "linux")]
            use libc::pread64;
            cvt(pread64(fd, buf, count, offset))
        }

        unsafe {
            cvt_pread64(
                self.fd,
                buf.as_mut_ptr() as *mut c_void,
                cmp::min(buf.len(), READ_LIMIT),
                offset as i64,
            )
            .map(|n| n as usize)
        }
    }

    pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
        let ret = cvt(unsafe {
            libc::write(self.fd, buf.as_ptr() as *const c_void, cmp::min(buf.len(), READ_LIMIT))
        })?;
        Ok(ret as usize)
    }

    pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
        let ret = cvt(unsafe {
            libc::writev(
                self.fd,
                bufs.as_ptr() as *const libc::iovec,
                cmp::min(bufs.len(), max_iov()) as c_int,
            )
        })?;
        Ok(ret as usize)
    }

    #[inline]
    pub fn is_write_vectored(&self) -> bool {
        true
    }

    pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
        #[cfg(target_os = "android")]
        use super::android::cvt_pwrite64;

        #[cfg(not(target_os = "android"))]
        unsafe fn cvt_pwrite64(
            fd: c_int,
            buf: *const c_void,
            count: usize,
            offset: i64,
        ) -> io::Result<isize> {
            #[cfg(not(target_os = "linux"))]
            use libc::pwrite as pwrite64;
            #[cfg(target_os = "linux")]
            use libc::pwrite64;
            cvt(pwrite64(fd, buf, count, offset))
        }

        unsafe {
            cvt_pwrite64(
                self.fd,
                buf.as_ptr() as *const c_void,
                cmp::min(buf.len(), READ_LIMIT),
                offset as i64,
            )
            .map(|n| n as usize)
        }
    }

    #[cfg(target_os = "linux")]
    pub fn get_cloexec(&self) -> io::Result<bool> {
        unsafe { Ok((cvt(libc::fcntl(self.fd, libc::F_GETFD))? & libc::FD_CLOEXEC) != 0) }
    }

    #[cfg(not(any(
        target_env = "newlib",
        target_os = "solaris",
        target_os = "illumos",
        target_os = "emscripten",
        target_os = "fuchsia",
        target_os = "l4re",
        target_os = "linux",
        target_os = "haiku",
        target_os = "redox"
    )))]
    pub fn set_cloexec(&self) -> io::Result<()> {
        unsafe {
            cvt(libc::ioctl(self.fd, libc::FIOCLEX))?;
            Ok(())
        }
    }
    #[cfg(any(
        target_env = "newlib",
        target_os = "solaris",
        target_os = "illumos",
        target_os = "emscripten",
        target_os = "fuchsia",
        target_os = "l4re",
        target_os = "linux",
        target_os = "haiku",
        target_os = "redox"
    ))]
    pub fn set_cloexec(&self) -> io::Result<()> {
        unsafe {
            let previous = cvt(libc::fcntl(self.fd, libc::F_GETFD))?;
            let new = previous | libc::FD_CLOEXEC;
            if new != previous {
                cvt(libc::fcntl(self.fd, libc::F_SETFD, new))?;
            }
            Ok(())
        }
    }

    #[cfg(target_os = "linux")]
    pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
        unsafe {
            let v = nonblocking as c_int;
            cvt(libc::ioctl(self.fd, libc::FIONBIO, &v))?;
            Ok(())
        }
    }

    #[cfg(not(target_os = "linux"))]
    pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
        unsafe {
            let previous = cvt(libc::fcntl(self.fd, libc::F_GETFL))?;
            let new = if nonblocking {
                previous | libc::O_NONBLOCK
            } else {
                previous & !libc::O_NONBLOCK
            };
            if new != previous {
                cvt(libc::fcntl(self.fd, libc::F_SETFL, new))?;
            }
            Ok(())
        }
    }

    pub fn duplicate(&self) -> io::Result<FileDesc> {
        // We want to atomically duplicate this file descriptor and set the
        // CLOEXEC flag, and currently that's done via F_DUPFD_CLOEXEC. This
        // is a POSIX flag that was added to Linux in 2.6.24.
        let fd = cvt(unsafe { libc::fcntl(self.raw(), libc::F_DUPFD_CLOEXEC, 0) })?;
        Ok(FileDesc::new(fd))
    }
}

impl<'a> Read for &'a FileDesc {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        (**self).read(buf)
    }

    #[inline]
    unsafe fn initializer(&self) -> Initializer {
        Initializer::nop()
    }
}

impl AsInner<c_int> for FileDesc {
    fn as_inner(&self) -> &c_int {
        &self.fd
    }
}

impl Drop for FileDesc {
    fn drop(&mut self) {
        // Note that errors are ignored when closing a file descriptor. The
        // reason for this is that if an error occurs we don't actually know if
        // the file descriptor was closed or not, and if we retried (for
        // something like EINTR), we might close another valid file descriptor
        // opened after we closed ours.
        let _ = unsafe { libc::close(self.fd) };
    }
}

#[cfg(test)]
mod tests {
    use super::{FileDesc, IoSlice};

    #[test]
    fn limit_vector_count() {
        let stdout = FileDesc { fd: 1 };
        let bufs = (0..1500).map(|_| IoSlice::new(&[])).collect::<Vec<_>>();

        assert!(stdout.write_vectored(&bufs).is_ok());
    }
}
<commit_msg>Add fallback for cfg(unix) targets that do not define libc::_SC_IOV_MAX.<commit_after>#![unstable(reason = "not public", issue = "none", feature = "fd")]

use crate::cmp;
use crate::io::{self, Initializer, IoSlice, IoSliceMut, Read};
use crate::mem;
#[cfg(not(any(target_os = "redox", target_env = "newlib")))]
use crate::sync::atomic::{AtomicUsize, Ordering};
use crate::sys::cvt;
use crate::sys_common::AsInner;

use libc::{c_int, c_void};

#[derive(Debug)]
pub struct FileDesc {
    fd: c_int,
}

// The maximum read limit on most POSIX-like systems is `SSIZE_MAX`,
// with the man page quoting that if the count of bytes to read is
// greater than `SSIZE_MAX` the result is "unspecified".
//
// On macOS, however, apparently the 64-bit libc is either buggy or
// intentionally showing odd behavior by rejecting any read with a size
// larger than or equal to INT_MAX. To handle both of these the read
// size is capped on both platforms.
#[cfg(target_os = "macos")]
const READ_LIMIT: usize = c_int::MAX as usize - 1;
#[cfg(not(target_os = "macos"))]
const READ_LIMIT: usize = libc::ssize_t::MAX as usize;

#[cfg(not(any(target_os = "redox", target_env = "newlib")))]
fn max_iov() -> usize {
    static LIM: AtomicUsize = AtomicUsize::new(0);

    let mut lim = LIM.load(Ordering::Relaxed);
    if lim == 0 {
        let ret = unsafe { libc::sysconf(libc::_SC_IOV_MAX) };

        // 16 is the minimum value required by POSIX.
        lim = if ret > 0 { ret as usize } else { 16 };
        LIM.store(lim, Ordering::Relaxed);
    }

    lim
}

#[cfg(any(target_os = "redox", target_env = "newlib"))]
fn max_iov() -> usize {
    16 // The minimum value required by POSIX.
}

impl FileDesc {
    pub fn new(fd: c_int) -> FileDesc {
        FileDesc { fd }
    }

    pub fn raw(&self) -> c_int {
        self.fd
    }

    /// Extracts the actual file descriptor without closing it.
    pub fn into_raw(self) -> c_int {
        let fd = self.fd;
        mem::forget(self);
        fd
    }

    pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
        let ret = cvt(unsafe {
            libc::read(self.fd, buf.as_mut_ptr() as *mut c_void, cmp::min(buf.len(), READ_LIMIT))
        })?;
        Ok(ret as usize)
    }

    pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
        let ret = cvt(unsafe {
            libc::readv(
                self.fd,
                bufs.as_ptr() as *const libc::iovec,
                cmp::min(bufs.len(), max_iov()) as c_int,
            )
        })?;
        Ok(ret as usize)
    }

    #[inline]
    pub fn is_read_vectored(&self) -> bool {
        true
    }

    pub fn read_to_end(&self, buf: &mut Vec<u8>) -> io::Result<usize> {
        let mut me = self;
        (&mut me).read_to_end(buf)
    }

    pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
        #[cfg(target_os = "android")]
        use super::android::cvt_pread64;

        #[cfg(not(target_os = "android"))]
        unsafe fn cvt_pread64(
            fd: c_int,
            buf: *mut c_void,
            count: usize,
            offset: i64,
        ) -> io::Result<isize> {
            #[cfg(not(target_os = "linux"))]
            use libc::pread as pread64;
            #[cfg(target_os = "linux")]
            use libc::pread64;
            cvt(pread64(fd, buf, count, offset))
        }

        unsafe {
            cvt_pread64(
                self.fd,
                buf.as_mut_ptr() as *mut c_void,
                cmp::min(buf.len(), READ_LIMIT),
                offset as i64,
            )
            .map(|n| n as usize)
        }
    }

    pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
        let ret = cvt(unsafe {
            libc::write(self.fd, buf.as_ptr() as *const c_void, cmp::min(buf.len(), READ_LIMIT))
        })?;
        Ok(ret as usize)
    }

    pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
        let ret = cvt(unsafe {
            libc::writev(
                self.fd,
                bufs.as_ptr() as *const libc::iovec,
                cmp::min(bufs.len(), max_iov()) as c_int,
            )
        })?;
        Ok(ret as usize)
    }

    #[inline]
    pub fn is_write_vectored(&self) -> bool {
        true
    }

    pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
        #[cfg(target_os = "android")]
        use super::android::cvt_pwrite64;

        #[cfg(not(target_os = "android"))]
        unsafe fn cvt_pwrite64(
            fd: c_int,
            buf: *const c_void,
            count: usize,
            offset: i64,
        ) -> io::Result<isize> {
            #[cfg(not(target_os = "linux"))]
            use libc::pwrite as pwrite64;
            #[cfg(target_os = "linux")]
            use libc::pwrite64;
            cvt(pwrite64(fd, buf, count, offset))
        }

        unsafe {
            cvt_pwrite64(
                self.fd,
                buf.as_ptr() as *const c_void,
                cmp::min(buf.len(), READ_LIMIT),
                offset as i64,
            )
            .map(|n| n as usize)
        }
    }

    #[cfg(target_os = "linux")]
    pub fn get_cloexec(&self) -> io::Result<bool> {
        unsafe { Ok((cvt(libc::fcntl(self.fd, libc::F_GETFD))? & libc::FD_CLOEXEC) != 0) }
    }

    #[cfg(not(any(
        target_env = "newlib",
        target_os = "solaris",
        target_os = "illumos",
        target_os = "emscripten",
        target_os = "fuchsia",
        target_os = "l4re",
        target_os = "linux",
        target_os = "haiku",
        target_os = "redox"
    )))]
    pub fn set_cloexec(&self) -> io::Result<()> {
        unsafe {
            cvt(libc::ioctl(self.fd, libc::FIOCLEX))?;
            Ok(())
        }
    }
    #[cfg(any(
        target_env = "newlib",
        target_os = "solaris",
        target_os = "illumos",
        target_os = "emscripten",
        target_os = "fuchsia",
        target_os = "l4re",
        target_os = "linux",
        target_os = "haiku",
        target_os = "redox"
    ))]
    pub fn set_cloexec(&self) -> io::Result<()> {
        unsafe {
            let previous = cvt(libc::fcntl(self.fd, libc::F_GETFD))?;
            let new = previous | libc::FD_CLOEXEC;
            if new != previous {
                cvt(libc::fcntl(self.fd, libc::F_SETFD, new))?;
            }
            Ok(())
        }
    }

    #[cfg(target_os = "linux")]
    pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
        unsafe {
            let v = nonblocking as c_int;
            cvt(libc::ioctl(self.fd, libc::FIONBIO, &v))?;
            Ok(())
        }
    }

    #[cfg(not(target_os = "linux"))]
    pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
        unsafe {
            let previous = cvt(libc::fcntl(self.fd, libc::F_GETFL))?;
            let new = if nonblocking {
                previous | libc::O_NONBLOCK
            } else {
                previous & !libc::O_NONBLOCK
            };
            if new != previous {
                cvt(libc::fcntl(self.fd, libc::F_SETFL, new))?;
            }
            Ok(())
        }
    }

    pub fn duplicate(&self) -> io::Result<FileDesc> {
        // We want to atomically duplicate this file descriptor and set the
        // CLOEXEC flag, and currently that's done via F_DUPFD_CLOEXEC. This
        // is a POSIX flag that was added to Linux in 2.6.24.
        let fd = cvt(unsafe { libc::fcntl(self.raw(), libc::F_DUPFD_CLOEXEC, 0) })?;
        Ok(FileDesc::new(fd))
    }
}

impl<'a> Read for &'a FileDesc {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        (**self).read(buf)
    }

    #[inline]
    unsafe fn initializer(&self) -> Initializer {
        Initializer::nop()
    }
}

impl AsInner<c_int> for FileDesc {
    fn as_inner(&self) -> &c_int {
        &self.fd
    }
}

impl Drop for FileDesc {
    fn drop(&mut self) {
        // Note that errors are ignored when closing a file descriptor. The
        // reason for this is that if an error occurs we don't actually know if
        // the file descriptor was closed or not, and if we retried (for
        // something like EINTR), we might close another valid file descriptor
        // opened after we closed ours.
        let _ = unsafe { libc::close(self.fd) };
    }
}

#[cfg(test)]
mod tests {
    use super::{FileDesc, IoSlice};

    #[test]
    fn limit_vector_count() {
        let stdout = FileDesc { fd: 1 };
        let bufs = (0..1500).map(|_| IoSlice::new(&[])).collect::<Vec<_>>();

        assert!(stdout.write_vectored(&bufs).is_ok());
    }
}
<|endoftext|>


<commit_before>use std::str::FromStr;

use syntax::ext::base::{Annotatable, ExtCtxt};
use syntax::ast::{Item, ItemKind, MetaItem, FnDecl};
use syntax::codemap::{Span, Spanned, BytePos};
use syntax::ptr::P;

use utils::*;
use rocket::{Method, ContentType};

#[allow(dead_code)]
const DEBUG: bool = true;

pub struct MetaItemParser<'a, 'c: 'a> {
    attr_name: &'a str,
    ctxt: &'a ExtCtxt<'c>,
    meta_item: &'a MetaItem,
    annotated: &'a Annotatable,
    span: Span
}

pub struct ParamIter<'s, 'a, 'c: 'a> {
    ctxt: &'a ExtCtxt<'c>,
    span: Span,
    string: &'s str
}

impl<'a, 'c> MetaItemParser<'a, 'c> {
    pub fn new(ctxt: &'a ExtCtxt<'c>, meta_item: &'a MetaItem,
               annotated: &'a Annotatable, span: &'a Span) -> MetaItemParser<'a, 'c> {
        MetaItemParser {
            attr_name: meta_item.name(),
            ctxt: ctxt,
            meta_item: meta_item,
            annotated: annotated,
            span: span.clone(),
        }
    }

    fn bad_item(&self, expected: &str, got: &str, sp: Span) -> ! {
        let msg_a = format!("Expected a {} item...", expected);
        let msg_b = format!("...but found a {} item instead.", got);
        self.ctxt.span_err(self.span, msg_a.as_str());
        self.ctxt.span_fatal(sp, msg_b.as_str())
    }

    pub fn expect_item(&self) -> &'a P<Item> {
        let bad_item = |name: &str, sp: Span| self.bad_item("regular", name, sp);

        match *self.annotated {
            Annotatable::Item(ref item) => item,
            Annotatable::TraitItem(ref item) => bad_item("trait", item.span),
            Annotatable::ImplItem(ref item) => bad_item("impl", item.span)
        }
    }

    pub fn expect_fn_decl(&self) -> Spanned<&'a FnDecl> {
        let item = self.expect_item();
        let bad_item = |name: &str| self.bad_item("fn_decl", name, item.span);

        let fn_decl: &P<FnDecl> = match item.node {
             ItemKind::Fn(ref decl, _, _, _, _, _) => decl,
             _ => bad_item("other")
         };

        span(fn_decl, item.span)
    }

    fn expect_list(&self) -> &'a Vec<P<MetaItem>> {
        let msg = format!("Bad use. Expected: #[{}(...)]", self.attr_name);
        self.meta_item.expect_list(self.ctxt, msg.as_str())
    }

    pub fn iter_params<'s>(&self, from: &Spanned<&'s str>) -> ParamIter<'s, 'a, 'c> {
        ParamIter {
            ctxt: self.ctxt,
            span: from.span,
            string: from.node
        }
    }
}

impl<'s, 'a, 'c> Iterator for ParamIter<'s, 'a, 'c> {
    type Item = Spanned<&'s str>;

    fn next(&mut self) -> Option<Spanned<&'s str>> {
        // Find the start and end indexes for the next parameter, if any.
        let (start, end) = match (self.string.find('<'), self.string.find('>')) {
            (Some(i), Some(j)) => (i, j),
            _ => return None
        };

        // Ensure we found a valid parameter.
        if end <= start {
            self.ctxt.span_err(self.span, "Parameter list is malformed.");
            return None;
        }

        // Calculate the parameter and the span for the parameter.
        let param = &self.string[(start + 1)..end];
        let mut param_span = self.span;
        param_span.lo = self.span.lo + BytePos(start as u32);
        param_span.hi = self.span.lo + BytePos((end + 1) as u32);

        // Check for nonemptiness and that the characters are correct.
        if param.len() == 0 {
            self.ctxt.span_err(param_span, "Parameter names cannot be empty.");
            None
        } else if param.contains(|c: char| !c.is_alphanumeric()) {
            self.ctxt.span_err(param_span, "Parameters must be alphanumeric.");
            None
        } else {
            self.string = &self.string[(end + 1)..];
            self.span.lo = self.span.lo + BytePos((end + 1) as u32);
            Some(span(param, param_span))
        }
    }
}

pub struct RouteParams {
    pub method: Spanned<Method>,
    pub path: KVSpanned<String>,
    pub form: Option<KVSpanned<String>>,
    pub content_type: Option<KVSpanned<ContentType>>,
}

pub trait RouteDecoratorExt {
    fn bad_method(&self, sp: Span, message: &str);
    fn parse_method(&self, default: Method) -> Spanned<Method>;
    fn parse_route(&self, known_method: Option<Spanned<Method>>) -> RouteParams;
}

impl<'a, 'c> RouteDecoratorExt for MetaItemParser<'a, 'c> {
    fn bad_method(&self, sp: Span, message: &str) {
        let message = format!("{} Valid methods are: [GET, PUT, POST, DELETE, \
            OPTIONS, HEAD, TRACE, CONNECT, PATCH]", message);
        self.ctxt.span_err(sp, message.as_str());
    }

    fn parse_method(&self, default: Method) -> Spanned<Method> {
        let params = self.expect_list();
        if params.len() < 1 {
            self.bad_method(self.span, "HTTP method parameter is missing.");
            self.ctxt.span_fatal(self.span, "At least 2 arguments are required.");
        }

        // Get the method and the rest of the k = v params.
        let method_param = params.first().unwrap();

        // Check that the method parameter is a word (i.e, not a list, k/v pair).
        if !method_param.is_word() {
            self.bad_method(method_param.span,
                "Expected a valid HTTP method at this position.");
            return dummy_span(default);
        }

        // Parse the method from the string. If bad, error and return default.
        Method::from_str(method_param.name()).ok().map_or_else(|| {
            let message = format!("{} is not a valid method.", method_param.name());
            self.bad_method(method_param.span, message.as_str());
            dummy_span(default)
        }, |method| span(method, method_param.span))
    }

    // Parses the MetaItem derived from the route(...) macro.
   fn parse_route(&self, known_method: Option<Spanned<Method>>) -> RouteParams {
        let list = self.expect_list();
        let (method, kv_params) = match known_method {
            Some(method) => (method, &list[..]),
            None => (self.parse_method(Method::Get), list.split_first().unwrap().1)
        };

        // Now grab all of the required and optional parameters.
        let req: [&'static str; 1] = ["path"];
        let opt: [&'static str; 2] = ["form", "content"];
        let kv_pairs = get_key_values(self.ctxt, self.meta_item.span,
                                      &req, &opt, kv_params);

        // Ensure we have a path, just to keep parsing and generating errors.
        let path = kv_pairs.get("path").map_or(KVSpanned::dummy("/".to_string()), |s| {
            s.clone().map(String::from)
        });

        // If there's a form parameter, ensure method is POST.
        let form = kv_pairs.get("form").and_then(|f| {
            if method.node != Method::Post {
                self.ctxt.span_err(f.p_span, "Use of `form` requires POST method...");
                let message = format!("...but {} was found instead.", method.node);
                self.ctxt.span_err(method.span, message.as_str());
            }

            if !(f.node.starts_with('<') && f.node.ends_with('>')) {
                self.ctxt.struct_span_err(f.p_span,
                                        "`form` cannot contain arbitrary text")
                    .help("`form` must be exactly one parameter: \"<param>\"")
                    .emit();
            }

            if f.node.chars().filter(|c| *c == '<' || *c == '>').count() != 2 {
                self.ctxt.span_err(f.p_span,
                                   "`form` must contain exactly one parameter");
            }

            Some(f.clone().map(String::from))
        });

        let content_type = kv_pairs.get("content").and_then(|data| {
            if let Ok(ct) = ContentType::from_str(data.node) {
                if ct.is_ext() {
                    let msg = format!("'{}' is not a known content-type", data.node);
                    self.ctxt.span_warn(data.v_span, &msg);
                }

                Some(data.clone().map(|_| ct))
            } else {
                let msg = format!("'{}' is not a valid content-type", data.node);
                self.ctxt.span_err(data.v_span, &msg);
                None
            }
        });

        debug!("Found data: {:?}", content_type);

        RouteParams {
            method: method,
            path: path,
            form: form,
            content_type: content_type,
        }
    }

}
<commit_msg>Switch to UFCS in param.contains call.<commit_after>use std::str::FromStr;

use syntax::ext::base::{Annotatable, ExtCtxt};
use syntax::ast::{Item, ItemKind, MetaItem, FnDecl};
use syntax::codemap::{Span, Spanned, BytePos};
use syntax::ptr::P;

use utils::*;
use rocket::{Method, ContentType};

#[allow(dead_code)]
const DEBUG: bool = true;

pub struct MetaItemParser<'a, 'c: 'a> {
    attr_name: &'a str,
    ctxt: &'a ExtCtxt<'c>,
    meta_item: &'a MetaItem,
    annotated: &'a Annotatable,
    span: Span
}

pub struct ParamIter<'s, 'a, 'c: 'a> {
    ctxt: &'a ExtCtxt<'c>,
    span: Span,
    string: &'s str
}

impl<'a, 'c> MetaItemParser<'a, 'c> {
    pub fn new(ctxt: &'a ExtCtxt<'c>, meta_item: &'a MetaItem,
               annotated: &'a Annotatable, span: &'a Span) -> MetaItemParser<'a, 'c> {
        MetaItemParser {
            attr_name: meta_item.name(),
            ctxt: ctxt,
            meta_item: meta_item,
            annotated: annotated,
            span: span.clone(),
        }
    }

    fn bad_item(&self, expected: &str, got: &str, sp: Span) -> ! {
        let msg_a = format!("Expected a {} item...", expected);
        let msg_b = format!("...but found a {} item instead.", got);
        self.ctxt.span_err(self.span, msg_a.as_str());
        self.ctxt.span_fatal(sp, msg_b.as_str())
    }

    pub fn expect_item(&self) -> &'a P<Item> {
        let bad_item = |name: &str, sp: Span| self.bad_item("regular", name, sp);

        match *self.annotated {
            Annotatable::Item(ref item) => item,
            Annotatable::TraitItem(ref item) => bad_item("trait", item.span),
            Annotatable::ImplItem(ref item) => bad_item("impl", item.span)
        }
    }

    pub fn expect_fn_decl(&self) -> Spanned<&'a FnDecl> {
        let item = self.expect_item();
        let bad_item = |name: &str| self.bad_item("fn_decl", name, item.span);

        let fn_decl: &P<FnDecl> = match item.node {
             ItemKind::Fn(ref decl, _, _, _, _, _) => decl,
             _ => bad_item("other")
         };

        span(fn_decl, item.span)
    }

    fn expect_list(&self) -> &'a Vec<P<MetaItem>> {
        let msg = format!("Bad use. Expected: #[{}(...)]", self.attr_name);
        self.meta_item.expect_list(self.ctxt, msg.as_str())
    }

    pub fn iter_params<'s>(&self, from: &Spanned<&'s str>) -> ParamIter<'s, 'a, 'c> {
        ParamIter {
            ctxt: self.ctxt,
            span: from.span,
            string: from.node
        }
    }
}

impl<'s, 'a, 'c> Iterator for ParamIter<'s, 'a, 'c> {
    type Item = Spanned<&'s str>;

    fn next(&mut self) -> Option<Spanned<&'s str>> {
        // Find the start and end indexes for the next parameter, if any.
        let (start, end) = match (self.string.find('<'), self.string.find('>')) {
            (Some(i), Some(j)) => (i, j),
            _ => return None
        };

        // Ensure we found a valid parameter.
        if end <= start {
            self.ctxt.span_err(self.span, "Parameter list is malformed.");
            return None;
        }

        // Calculate the parameter and the span for the parameter.
        let param = &self.string[(start + 1)..end];
        let mut param_span = self.span;
        param_span.lo = self.span.lo + BytePos(start as u32);
        param_span.hi = self.span.lo + BytePos((end + 1) as u32);

        // Check for nonemptiness and that the characters are correct.
        if param.len() == 0 {
            self.ctxt.span_err(param_span, "Parameter names cannot be empty.");
            None
        } else if param.contains(char::is_alphanumeric) {
            self.ctxt.span_err(param_span, "Parameters must be alphanumeric.");
            None
        } else {
            self.string = &self.string[(end + 1)..];
            self.span.lo = self.span.lo + BytePos((end + 1) as u32);
            Some(span(param, param_span))
        }
    }
}

pub struct RouteParams {
    pub method: Spanned<Method>,
    pub path: KVSpanned<String>,
    pub form: Option<KVSpanned<String>>,
    pub content_type: Option<KVSpanned<ContentType>>,
}

pub trait RouteDecoratorExt {
    fn bad_method(&self, sp: Span, message: &str);
    fn parse_method(&self, default: Method) -> Spanned<Method>;
    fn parse_route(&self, known_method: Option<Spanned<Method>>) -> RouteParams;
}

impl<'a, 'c> RouteDecoratorExt for MetaItemParser<'a, 'c> {
    fn bad_method(&self, sp: Span, message: &str) {
        let message = format!("{} Valid methods are: [GET, PUT, POST, DELETE, \
            OPTIONS, HEAD, TRACE, CONNECT, PATCH]", message);
        self.ctxt.span_err(sp, message.as_str());
    }

    fn parse_method(&self, default: Method) -> Spanned<Method> {
        let params = self.expect_list();
        if params.len() < 1 {
            self.bad_method(self.span, "HTTP method parameter is missing.");
            self.ctxt.span_fatal(self.span, "At least 2 arguments are required.");
        }

        // Get the method and the rest of the k = v params.
        let method_param = params.first().unwrap();

        // Check that the method parameter is a word (i.e, not a list, k/v pair).
        if !method_param.is_word() {
            self.bad_method(method_param.span,
                "Expected a valid HTTP method at this position.");
            return dummy_span(default);
        }

        // Parse the method from the string. If bad, error and return default.
        Method::from_str(method_param.name()).ok().map_or_else(|| {
            let message = format!("{} is not a valid method.", method_param.name());
            self.bad_method(method_param.span, message.as_str());
            dummy_span(default)
        }, |method| span(method, method_param.span))
    }

    // Parses the MetaItem derived from the route(...) macro.
   fn parse_route(&self, known_method: Option<Spanned<Method>>) -> RouteParams {
        let list = self.expect_list();
        let (method, kv_params) = match known_method {
            Some(method) => (method, &list[..]),
            None => (self.parse_method(Method::Get), list.split_first().unwrap().1)
        };

        // Now grab all of the required and optional parameters.
        let req: [&'static str; 1] = ["path"];
        let opt: [&'static str; 2] = ["form", "content"];
        let kv_pairs = get_key_values(self.ctxt, self.meta_item.span,
                                      &req, &opt, kv_params);

        // Ensure we have a path, just to keep parsing and generating errors.
        let path = kv_pairs.get("path").map_or(KVSpanned::dummy("/".to_string()), |s| {
            s.clone().map(String::from)
        });

        // If there's a form parameter, ensure method is POST.
        let form = kv_pairs.get("form").and_then(|f| {
            if method.node != Method::Post {
                self.ctxt.span_err(f.p_span, "Use of `form` requires POST method...");
                let message = format!("...but {} was found instead.", method.node);
                self.ctxt.span_err(method.span, message.as_str());
            }

            if !(f.node.starts_with('<') && f.node.ends_with('>')) {
                self.ctxt.struct_span_err(f.p_span,
                                        "`form` cannot contain arbitrary text")
                    .help("`form` must be exactly one parameter: \"<param>\"")
                    .emit();
            }

            if f.node.chars().filter(|c| *c == '<' || *c == '>').count() != 2 {
                self.ctxt.span_err(f.p_span,
                                   "`form` must contain exactly one parameter");
            }

            Some(f.clone().map(String::from))
        });

        let content_type = kv_pairs.get("content").and_then(|data| {
            debug!("Found data: {:?}", content_type);
            if let Ok(ct) = ContentType::from_str(data.node) {
                if ct.is_ext() {
                    let msg = format!("'{}' is not a known content-type", data.node);
                    self.ctxt.span_warn(data.v_span, &msg);
                }

                Some(data.clone().map(|_| ct))
            } else {
                let msg = format!("'{}' is not a valid content-type", data.node);
                self.ctxt.span_err(data.v_span, &msg);
                None
            }
        });

        RouteParams {
            method: method,
            path: path,
            form: form,
            content_type: content_type,
        }
    }

}
<|endoftext|>


<commit_before>#[cfg(feature = "parallel")]
extern crate rayon;
#[cfg(feature = "parallel")]
use rayon::prelude::*;

use std::env;
use std::process::Command;
use std::process::Stdio;
use std::path::{Path, PathBuf};
use std::ffi::OsString;

fn x86_triple(os: &str) -> &'static str {
    match os {
        "darwin" => "-fmacho32",
        "windows" => "-fwin32",
        _ => "-felf32"
    }
}

fn x86_64_triple(os: &str) -> &'static str {
    match os {
        "darwin" => "-fmacho64",
        "windows" => "-fwin64",
        _ => "-felf64"
    }
}

fn parse_triple(trip: &str) -> &'static str {
    let parts = trip.split('-').collect::<Vec<_>>();
    // ARCH-VENDOR-OS-ENVIRONMENT
    // or ARCH-VENDOR-OS
    // we don't care about environ so doesn't matter if triple doesn't have it
    if parts.len() < 3 {
        return ""
    }

    match parts[0] {
        "x86_64" => x86_64_triple(parts[2]),
        "x86" | "i386" | "i586" | "i686" => x86_triple(parts[2]),
        _ => ""
    }
}

/// # Example
///
/// ```no_run
/// nasm_rs::compile_library("libfoo.a", &["foo.s", "bar.s"]);
/// ```
pub fn compile_library(output: &str, files: &[&str]) {
    compile_library_args(output, files, &[]);
}

/// # Example
///
/// ```no_run
/// nasm_rs::compile_library_args("libfoo.a", &["foo.s", "bar.s"], &["-Fdwarf"]);
/// ```
pub fn compile_library_args<P: AsRef<Path>>(output: &str, files: &[P], args: &[&str]) {
    let mut b = Build::new();
    for file in files {
        b.file(file);
    }
    for arg in args {
        b.flag(arg);
    }
    b.compile(output);
}

pub struct Build {
    files: Vec<PathBuf>,
    flags: Vec<String>,
    target: Option<String>,
    out_dir: Option<PathBuf>,
    archiver: Option<PathBuf>,
    nasm: Option<PathBuf>,
    debug: bool,
}

impl Build {
    pub fn new() -> Self {
        Self {
            files: Vec::new(),
            flags: Vec::new(),
            archiver: None,
            out_dir: None,
            nasm: None,
            target: None,
            debug: env::var("DEBUG").ok().map_or(false, |d| d != "false"),
        }
    }

    /// Add a file which will be compiled
    ///
    /// e.g. `"foo.s"`
    pub fn file<P: AsRef<Path>>(&mut self, p: P) -> &mut Self {
        self.files.push(p.as_ref().to_owned());
        self
    }

    /// Set multiple files
    pub fn files<P: AsRef<Path>, I: IntoIterator<Item=P>>(&mut self, files: I) -> &mut Self {
        for file in files {
            self.file(file);
        }
        self
    }

    /// Add a directory to the `-I` include path
    pub fn include<P: AsRef<Path>>(&mut self, dir: P) -> &mut Self {
        let mut flag = format!("-I{}", dir.as_ref().display());
        // nasm requires trailing slash, but `Path` may omit it.
        if !flag.ends_with("/") {
            flag += "/";
        }
        self.flags.push(flag);
        self
    }

    /// Pre-define a macro with an optional value
    pub fn define<'a, V: Into<Option<&'a str>>>(&mut self, var: &str, val: V) -> &mut Self {
        let val = val.into();
        let flag = if let Some(val) = val {
            format!("-D{}={}", var, val)
        } else {
            format!("-D{}", var)
        };
        self.flags.push(flag);
        self
    }

    /// Configures whether the assembler will generate debug information.
    ///
    /// This option is automatically scraped from the `DEBUG` environment
    /// variable by build scripts (only enabled when the profile is "debug"), so
    /// it's not required to call this function.
    pub fn debug(&mut self, enable: bool) -> &mut Self {
        self.debug = enable;
        self
    }

    /// Add an arbitrary flag to the invocation of the assembler
    ///
    /// e.g. `"-Fdwarf"`
    pub fn flag(&mut self, flag: &str) -> &mut Self {
        self.flags.push(flag.to_owned());
        self
    }

    /// Configures the target this configuration will be compiling for.
    ///
    /// This option is automatically scraped from the `TARGET` environment
    /// variable by build scripts, so it's not required to call this function.
    pub fn target(&mut self, target: &str) -> &mut Self {
        self.target = Some(target.to_owned());
        self
    }

    /// Configures the output directory where all object files and static libraries will be located.
    ///
    /// This option is automatically scraped from the OUT_DIR environment variable by build scripts,
    /// so it's not required to call this function.
    pub fn out_dir<P: AsRef<Path>>(&mut self, out_dir: P) -> &mut Self {
        self.out_dir = Some(out_dir.as_ref().to_owned());
        self
    }

    /// Configures the tool used to assemble archives.
    ///
    /// This option is automatically determined from the target platform or a
    /// number of environment variables, so it's not required to call this
    /// function.
    pub fn archiver<P: AsRef<Path>>(&mut self, archiver: P) -> &mut Self {
        self.archiver = Some(archiver.as_ref().to_owned());
        self
    }

    /// Configures path to `nasm` command
    pub fn nasm<P: AsRef<Path>>(&mut self, nasm: P) -> &mut Self {
        self.nasm = Some(nasm.as_ref().to_owned());
        self
    }

    /// Run the compiler, generating the file output
    ///
    /// The name output should be the base name of the library,
    /// without file extension, and without "lib" prefix.
    ///
    /// The output file will have target-specific name,
    /// such as `lib*.a` (non-MSVC) or `*.lib` (MSVC).
    pub fn compile(&mut self, lib_name: &str) {
        // Trim name for backwards comatibility
        let lib_name = if lib_name.starts_with("lib") && lib_name.ends_with(".a") {
            &lib_name[3..lib_name.len() - 2]
        } else {
            lib_name.trim_end_matches(".lib")
        };

        let target = self.get_target();
        let output = if target.ends_with("-msvc") {
            format!("{}.lib", lib_name)
        } else {
            format!("lib{}.a", lib_name)
        };

        let dst = &self.get_out_dir();
        let objects = self.compile_objects();
        self.archive(&dst, &output, &objects[..]);

        println!("cargo:rustc-flags=-L {}",
                 dst.display());
    }

    /// Run the compiler, generating .o files
    ///
    /// The files can be linked in a separate step, e.g. passed to `cc`
    pub fn compile_objects(&mut self) -> Vec<PathBuf> {
        let target = self.get_target();

        let nasm = self.find_nasm();
        let args = self.get_args(&target);

        let src = &PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR must be set"));
        let dst = &self.get_out_dir();

        self.make_iter().map(|file| {
            self.compile_file(&nasm, file.as_ref(), &args, src, dst)
        }).collect::<Vec<_>>()
    }

    fn get_args(&self, target: &str) -> Vec<&str> {
        let mut args = vec![parse_triple(&target)];

        if self.debug {
            args.push("-g");
        }

        for arg in &self.flags {
            args.push(arg);
        }

        args
    }

    #[cfg(feature = "parallel")]
    fn make_iter(&self) -> rayon::slice::Iter<PathBuf> {
        self.files.par_iter()
    }

    #[cfg(not(feature = "parallel"))]
    fn make_iter(&self) -> std::slice::Iter<PathBuf> {
        self.files.iter()
    }

    fn compile_file(&self, nasm: &Path, file: &Path, new_args: &[&str], src: &Path, dst: &Path) -> PathBuf {
        let obj = dst.join(file).with_extension("o");
        let mut cmd = Command::new(nasm);
        cmd.args(&new_args[..]);
        std::fs::create_dir_all(&obj.parent().unwrap()).unwrap();

        run(cmd.arg(src.join(file)).arg("-o").arg(&obj));
        obj
    }

    fn archive(&self, out_dir: &Path, lib: &str, objs: &[PathBuf]) {
        let ar = if cfg!(target_env = "msvc") {
            self.archiver.clone().unwrap_or_else(|| "lib".into())
        } else {
            self.archiver.clone()
            .or_else(|| env::var_os("AR").map(|a| a.into()))
            .unwrap_or_else(|| "ar".into())
        };
        if cfg!(target_env = "msvc") {
            let mut out_param = OsString::new();
            out_param.push("/OUT:");
            out_param.push(out_dir.join(lib).as_os_str());
            run(Command::new(ar).arg(out_param).args(objs));
        } else {
            run(Command::new(ar).arg("crus").arg(out_dir.join(lib)).args(objs));
        }
    }

    fn get_out_dir(&self) -> PathBuf {
        self.out_dir.clone()
            .unwrap_or_else(|| PathBuf::from(env::var_os("OUT_DIR").expect("OUT_DIR must be set")))
    }

    fn get_target(&self) -> String {
        self.target.clone()
            .unwrap_or_else(|| env::var("TARGET").expect("TARGET must be set"))
    }

    fn find_nasm(&mut self) -> PathBuf {
        match self.nasm.clone() {
            Some(path) => path,
            None => {
                let nasm_path = PathBuf::from("nasm");
                match is_nasm_new_enough(&nasm_path) {
                    Ok(_) => nasm_path,
                    Err(version) => {
                        panic!("This version of NASM is too old: {}", version);
                    },
                }
            },
        }
    }
}

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

/// Returns version string if nasm is too old,
/// or error message string if it's unusable.
fn is_nasm_new_enough(nasm_path: &Path) -> Result<(), String> {
    match get_output(Command::new(nasm_path).arg("-v")) {
        Ok(version) => {
            if version.contains("NASM version 0.") {
                Err(version)?
            } else {
                Ok(())
            }
        },
        Err(err) => Err(err)?,
    }
}

fn run(cmd: &mut Command) {
    println!("running: {:?}", cmd);

    let status = match cmd.stdout(Stdio::inherit()).stderr(Stdio::inherit()).status() {
        Ok(status) => status,

        Err(e) => panic!("failed to spawn process: {}", e),
    };

    if !status.success() {
        panic!("nonzero exit status: {}", status);
    }
}

#[test]
fn test_build() {
    let mut build = Build::new();
    build.file("test");
    build.archiver("ar");
    build.include("./");
    build.include("dir");
    build.define("foo", Some("1"));
    build.define("bar", None);
    build.flag("-test");
    build.target("i686-unknown-linux-musl");
    build.out_dir("/tmp");

    assert_eq!(build.get_args("i686-unknown-linux-musl"), &["-felf32", "-I./", "-Idir/", "-Dfoo=1", "-Dbar", "-test"]);
}
<commit_msg>Fix nasm-rs build issue in paths with spaces<commit_after>#[cfg(feature = "parallel")]
extern crate rayon;
#[cfg(feature = "parallel")]
use rayon::prelude::*;

use std::env;
use std::process::Command;
use std::process::Stdio;
use std::path::{Path, PathBuf};
use std::ffi::OsString;

fn x86_triple(os: &str) -> &'static str {
    match os {
        "darwin" => "-fmacho32",
        "windows" => "-fwin32",
        _ => "-felf32"
    }
}

fn x86_64_triple(os: &str) -> &'static str {
    match os {
        "darwin" => "-fmacho64",
        "windows" => "-fwin64",
        _ => "-felf64"
    }
}

fn parse_triple(trip: &str) -> &'static str {
    let parts = trip.split('-').collect::<Vec<_>>();
    // ARCH-VENDOR-OS-ENVIRONMENT
    // or ARCH-VENDOR-OS
    // we don't care about environ so doesn't matter if triple doesn't have it
    if parts.len() < 3 {
        return ""
    }

    match parts[0] {
        "x86_64" => x86_64_triple(parts[2]),
        "x86" | "i386" | "i586" | "i686" => x86_triple(parts[2]),
        _ => ""
    }
}

/// # Example
///
/// ```no_run
/// nasm_rs::compile_library("libfoo.a", &["foo.s", "bar.s"]);
/// ```
pub fn compile_library(output: &str, files: &[&str]) {
    compile_library_args(output, files, &[]);
}

/// # Example
///
/// ```no_run
/// nasm_rs::compile_library_args("libfoo.a", &["foo.s", "bar.s"], &["-Fdwarf"]);
/// ```
pub fn compile_library_args<P: AsRef<Path>>(output: &str, files: &[P], args: &[&str]) {
    let mut b = Build::new();
    for file in files {
        b.file(file);
    }
    for arg in args {
        b.flag(arg);
    }
    b.compile(output);
}

pub struct Build {
    files: Vec<PathBuf>,
    flags: Vec<String>,
    target: Option<String>,
    out_dir: Option<PathBuf>,
    archiver: Option<PathBuf>,
    nasm: Option<PathBuf>,
    debug: bool,
}

impl Build {
    pub fn new() -> Self {
        Self {
            files: Vec::new(),
            flags: Vec::new(),
            archiver: None,
            out_dir: None,
            nasm: None,
            target: None,
            debug: env::var("DEBUG").ok().map_or(false, |d| d != "false"),
        }
    }

    /// Add a file which will be compiled
    ///
    /// e.g. `"foo.s"`
    pub fn file<P: AsRef<Path>>(&mut self, p: P) -> &mut Self {
        self.files.push(p.as_ref().to_owned());
        self
    }

    /// Set multiple files
    pub fn files<P: AsRef<Path>, I: IntoIterator<Item=P>>(&mut self, files: I) -> &mut Self {
        for file in files {
            self.file(file);
        }
        self
    }

    /// Add a directory to the `-I` include path
    pub fn include<P: AsRef<Path>>(&mut self, dir: P) -> &mut Self {
        let mut flag = format!("-I{}", dir.as_ref().display());
        // nasm requires trailing slash, but `Path` may omit it.
        if !flag.ends_with("/") {
            flag += "/";
        }
        self.flags.push(flag);
        self
    }

    /// Pre-define a macro with an optional value
    pub fn define<'a, V: Into<Option<&'a str>>>(&mut self, var: &str, val: V) -> &mut Self {
        let val = val.into();
        let flag = if let Some(val) = val {
            format!("-D{}={}", var, val)
        } else {
            format!("-D{}", var)
        };
        self.flags.push(flag);
        self
    }

    /// Configures whether the assembler will generate debug information.
    ///
    /// This option is automatically scraped from the `DEBUG` environment
    /// variable by build scripts (only enabled when the profile is "debug"), so
    /// it's not required to call this function.
    pub fn debug(&mut self, enable: bool) -> &mut Self {
        self.debug = enable;
        self
    }

    /// Add an arbitrary flag to the invocation of the assembler
    ///
    /// e.g. `"-Fdwarf"`
    pub fn flag(&mut self, flag: &str) -> &mut Self {
        self.flags.push(flag.to_owned());
        self
    }

    /// Configures the target this configuration will be compiling for.
    ///
    /// This option is automatically scraped from the `TARGET` environment
    /// variable by build scripts, so it's not required to call this function.
    pub fn target(&mut self, target: &str) -> &mut Self {
        self.target = Some(target.to_owned());
        self
    }

    /// Configures the output directory where all object files and static libraries will be located.
    ///
    /// This option is automatically scraped from the OUT_DIR environment variable by build scripts,
    /// so it's not required to call this function.
    pub fn out_dir<P: AsRef<Path>>(&mut self, out_dir: P) -> &mut Self {
        self.out_dir = Some(out_dir.as_ref().to_owned());
        self
    }

    /// Configures the tool used to assemble archives.
    ///
    /// This option is automatically determined from the target platform or a
    /// number of environment variables, so it's not required to call this
    /// function.
    pub fn archiver<P: AsRef<Path>>(&mut self, archiver: P) -> &mut Self {
        self.archiver = Some(archiver.as_ref().to_owned());
        self
    }

    /// Configures path to `nasm` command
    pub fn nasm<P: AsRef<Path>>(&mut self, nasm: P) -> &mut Self {
        self.nasm = Some(nasm.as_ref().to_owned());
        self
    }

    /// Run the compiler, generating the file output
    ///
    /// The name output should be the base name of the library,
    /// without file extension, and without "lib" prefix.
    ///
    /// The output file will have target-specific name,
    /// such as `lib*.a` (non-MSVC) or `*.lib` (MSVC).
    pub fn compile(&mut self, lib_name: &str) {
        // Trim name for backwards comatibility
        let lib_name = if lib_name.starts_with("lib") && lib_name.ends_with(".a") {
            &lib_name[3..lib_name.len() - 2]
        } else {
            lib_name.trim_end_matches(".lib")
        };

        let target = self.get_target();
        let output = if target.ends_with("-msvc") {
            format!("{}.lib", lib_name)
        } else {
            format!("lib{}.a", lib_name)
        };

        let dst = &self.get_out_dir();
        let objects = self.compile_objects();
        self.archive(&dst, &output, &objects[..]);

        println!("cargo:rustc-link-search={}",
                 dst.display());
    }

    /// Run the compiler, generating .o files
    ///
    /// The files can be linked in a separate step, e.g. passed to `cc`
    pub fn compile_objects(&mut self) -> Vec<PathBuf> {
        let target = self.get_target();

        let nasm = self.find_nasm();
        let args = self.get_args(&target);

        let src = &PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR must be set"));
        let dst = &self.get_out_dir();

        self.make_iter().map(|file| {
            self.compile_file(&nasm, file.as_ref(), &args, src, dst)
        }).collect::<Vec<_>>()
    }

    fn get_args(&self, target: &str) -> Vec<&str> {
        let mut args = vec![parse_triple(&target)];

        if self.debug {
            args.push("-g");
        }

        for arg in &self.flags {
            args.push(arg);
        }

        args
    }

    #[cfg(feature = "parallel")]
    fn make_iter(&self) -> rayon::slice::Iter<PathBuf> {
        self.files.par_iter()
    }

    #[cfg(not(feature = "parallel"))]
    fn make_iter(&self) -> std::slice::Iter<PathBuf> {
        self.files.iter()
    }

    fn compile_file(&self, nasm: &Path, file: &Path, new_args: &[&str], src: &Path, dst: &Path) -> PathBuf {
        let obj = dst.join(file).with_extension("o");
        let mut cmd = Command::new(nasm);
        cmd.args(&new_args[..]);
        std::fs::create_dir_all(&obj.parent().unwrap()).unwrap();

        run(cmd.arg(src.join(file)).arg("-o").arg(&obj));
        obj
    }

    fn archive(&self, out_dir: &Path, lib: &str, objs: &[PathBuf]) {
        let ar = if cfg!(target_env = "msvc") {
            self.archiver.clone().unwrap_or_else(|| "lib".into())
        } else {
            self.archiver.clone()
            .or_else(|| env::var_os("AR").map(|a| a.into()))
            .unwrap_or_else(|| "ar".into())
        };
        if cfg!(target_env = "msvc") {
            let mut out_param = OsString::new();
            out_param.push("/OUT:");
            out_param.push(out_dir.join(lib).as_os_str());
            run(Command::new(ar).arg(out_param).args(objs));
        } else {
            run(Command::new(ar).arg("crus").arg(out_dir.join(lib)).args(objs));
        }
    }

    fn get_out_dir(&self) -> PathBuf {
        self.out_dir.clone()
            .unwrap_or_else(|| PathBuf::from(env::var_os("OUT_DIR").expect("OUT_DIR must be set")))
    }

    fn get_target(&self) -> String {
        self.target.clone()
            .unwrap_or_else(|| env::var("TARGET").expect("TARGET must be set"))
    }

    fn find_nasm(&mut self) -> PathBuf {
        match self.nasm.clone() {
            Some(path) => path,
            None => {
                let nasm_path = PathBuf::from("nasm");
                match is_nasm_new_enough(&nasm_path) {
                    Ok(_) => nasm_path,
                    Err(version) => {
                        panic!("This version of NASM is too old: {}", version);
                    },
                }
            },
        }
    }
}

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

/// Returns version string if nasm is too old,
/// or error message string if it's unusable.
fn is_nasm_new_enough(nasm_path: &Path) -> Result<(), String> {
    match get_output(Command::new(nasm_path).arg("-v")) {
        Ok(version) => {
            if version.contains("NASM version 0.") {
                Err(version)?
            } else {
                Ok(())
            }
        },
        Err(err) => Err(err)?,
    }
}

fn run(cmd: &mut Command) {
    println!("running: {:?}", cmd);

    let status = match cmd.stdout(Stdio::inherit()).stderr(Stdio::inherit()).status() {
        Ok(status) => status,

        Err(e) => panic!("failed to spawn process: {}", e),
    };

    if !status.success() {
        panic!("nonzero exit status: {}", status);
    }
}

#[test]
fn test_build() {
    let mut build = Build::new();
    build.file("test");
    build.archiver("ar");
    build.include("./");
    build.include("dir");
    build.define("foo", Some("1"));
    build.define("bar", None);
    build.flag("-test");
    build.target("i686-unknown-linux-musl");
    build.out_dir("/tmp");

    assert_eq!(build.get_args("i686-unknown-linux-musl"), &["-felf32", "-I./", "-Idir/", "-Dfoo=1", "-Dbar", "-test"]);
}
<|endoftext|>


<commit_before>use super::*;

use std::cmp::Ordering;

pub(crate) fn prefix_encode(prefix: &[u8], buf: &[u8]) -> IVec {
    assert!(
        prefix <= buf,
        "prefix {:?} must be lexicographically <= to the encoded buf {:?}",
        prefix,
        buf
    );
    let mut prefix_len = 0_usize;
    for (i, c) in
        prefix.iter().take(u8::max_value() as usize).enumerate()
    {
        if buf[i] == *c {
            prefix_len += 1;
        } else {
            break;
        }
    }

    let encoded_len = 1 + buf.len() - prefix_len;
    let mut ret = Vec::new();
    ret.reserve_exact(encoded_len);
    unsafe {
        ret.set_len(encoded_len);
        std::ptr::copy_nonoverlapping(
            buf[prefix_len..].as_ptr(),
            ret[1..].as_mut_ptr(),
            encoded_len - 1,
        );
    }
    ret[0] = prefix_len as u8;
    ret.into()
}

pub(crate) fn prefix_decode(prefix: &[u8], buf: &[u8]) -> Vec<u8> {
    assert!(!buf.is_empty());
    let prefix_len = buf[0] as usize;
    let mut ret = Vec::with_capacity(prefix_len + buf.len() - 1);
    unsafe {
        ret.set_len(prefix_len + buf.len() - 1);
        std::ptr::copy_nonoverlapping(
            prefix.as_ptr(),
            ret.as_mut_ptr(),
            prefix_len,
        );
        std::ptr::copy_nonoverlapping(
            buf[1..].as_ptr(),
            ret[prefix_len..].as_mut_ptr(),
            buf.len() - 1,
        );
    }
    ret
}

// NB: the correctness of this function depends on
// the invariant that the prefix is ALWAYS lexicographically
// Less than or Equal to the keys that have been encoded
// using it. Otherwise this comparison would make no sense.
pub(crate) fn prefix_cmp(a: &[u8], b: &[u8]) -> Ordering {
    if a.is_empty() && b.is_empty() {
        return Ordering::Equal;
    } else if a.is_empty() && !b.is_empty() {
        return Ordering::Less;
    } else if !a.is_empty() && b.is_empty() {
        return Ordering::Greater;
    }

    if a[0] > b[0] {
        Ordering::Less
    } else if a[0] < b[0] {
        Ordering::Greater
    } else {
        a[1..].cmp(&b[1..])
    }
}

/// Compare `a` and `b`, assuming that `a` is prefix encoded and `b` is not.
pub(crate) fn prefix_cmp_encoded(
    a: &[u8],
    mut b: &[u8],
    mut prefix: &[u8],
) -> Ordering {
    assert!(!a.is_empty() && a[0] as usize <= prefix.len());

    let mut a_prefix_len = a[0];
    let a_suffix = &a[1..];

    while a_prefix_len > 0 {
        if b.is_empty() || prefix[0] > b[0] {
            return Ordering::Greater;
        } else if prefix[0] < b[0] {
            return Ordering::Less;
        }

        a_prefix_len -= 1;
        b = &b[1..];
        prefix = &prefix[1..];
    }

    a_suffix.cmp(b)
}

#[test]
fn test_prefix() {
    let prefix = b"cat";
    assert_eq!(
        prefix_encode(prefix, prefix),
        vec![prefix.len() as u8]
    );
    assert_eq!(prefix_encode(prefix, b"catt"), vec![3, b't']);
    assert_eq!(prefix_encode(prefix, b"cb"), vec![1, b'b']);
    assert_eq!(prefix_encode(prefix, b"caz"), vec![2, b'z']);
    assert_eq!(
        prefix_encode(prefix, b"cvar"),
        vec![1, b'v', b'a', b'r']
    );
    assert_eq!(
        prefix_encode(prefix, b"zig"),
        vec![0, b'z', b'i', b'g']
    );

    let prefix = b"";
    assert_eq!(
        prefix_encode(prefix, prefix),
        vec![prefix.len() as u8]
    );
    assert_eq!(prefix_encode(prefix, b"ca"), vec![0, b'c', b'a']);
    assert_eq!(
        prefix_encode(prefix, b"cat"),
        vec![0, b'c', b'a', b't']
    );
    assert_eq!(
        prefix_encode(prefix, b"cab"),
        vec![0, b'c', b'a', b'b']
    );
    assert_eq!(
        prefix_encode(prefix, b"cvar"),
        vec![0, b'c', b'v', b'a', b'r']
    );
    assert_eq!(
        prefix_encode(prefix, b"zig"),
        vec![0, b'z', b'i', b'g']
    );

    let rtt =
        vec![b"" as &[u8], b"\x00cat", b"\x00", b"oyunwytounw\x00"];
    for item in rtt {
        assert_eq!(
            prefix_decode(prefix, &*prefix_encode(prefix, item)),
            item.to_vec()
        );
    }
}

#[test]
fn test_prefix_cmp() {
    assert_eq!(prefix_cmp(&[], &[]), Ordering::Equal);
    assert_eq!(prefix_cmp(&[0], &[]), Ordering::Greater);
    assert_eq!(prefix_cmp(&[], &[0]), Ordering::Less);

    assert_eq!(prefix_cmp(&[3], &[4]), Ordering::Greater);
    assert_eq!(prefix_cmp(&[4, 3], &[3, 4]), Ordering::Less);

    assert_eq!(prefix_cmp(&[1], &[1]), Ordering::Equal);
    assert_eq!(prefix_cmp(&[1, 1], &[1, 1]), Ordering::Equal);
    assert_eq!(prefix_cmp(&[1, 3], &[1, 1]), Ordering::Greater);
    assert_eq!(prefix_cmp(&[1, 1], &[1, 3]), Ordering::Less);
}

#[test]
fn test_prefix_cmp_encoded() {
    fn assert_pce(
        a: &[u8],
        b: &[u8],
        prefix: &[u8],
        expected: Ordering,
    ) {
        assert_eq!(
            prefix_cmp_encoded(a, &prefix_decode(prefix, b), prefix),
            expected
        );
    }

    let prefix = &vec![3, 3, 3, 3];
    assert_pce(&[0], &[0], prefix, Ordering::Equal);
    assert_pce(&[1], &[0], prefix, Ordering::Greater);
    assert_pce(&[0], &[1], prefix, Ordering::Less);

    assert_pce(&[3], &[2], prefix, Ordering::Greater);
    assert_pce(&[4, 3], &[3, 4], prefix, Ordering::Less);

    assert_pce(&[1], &[1], prefix, Ordering::Equal);
    assert_pce(&[1, 3, 3, 1], &[3, 1], prefix, Ordering::Equal);
    assert_pce(&[1, 3], &[1, 1], prefix, Ordering::Greater);
    assert_pce(&[1, 1], &[3, 3], prefix, Ordering::Less);
}
<commit_msg>Rewrite the prefix_encode function<commit_after>use super::*;

use std::cmp::Ordering;

pub(crate) fn prefix_encode(prefix: &[u8], buf: &[u8]) -> IVec {
    assert!(
        prefix <= buf,
        "prefix {:?} must be lexicographically <= to the encoded buf {:?}",
        prefix,
        buf
    );

    let max = u8::max_value() as usize;
    let zip = prefix.iter().zip(buf);
    let prefix_len = zip.take(max).take_while(|(a, b)| a == b).count();

    let encoded_len = 1 + buf.len() - prefix_len;

    let mut ret = Vec::with_capacity(encoded_len);
    ret.push(prefix_len as u8);
    ret.extend_from_slice(&buf[prefix_len..]);

    IVec::from(ret)
}

pub(crate) fn prefix_decode(prefix: &[u8], buf: &[u8]) -> Vec<u8> {
    assert!(!buf.is_empty());
    let prefix_len = buf[0] as usize;
    let mut ret = Vec::with_capacity(prefix_len + buf.len() - 1);
    unsafe {
        ret.set_len(prefix_len + buf.len() - 1);
        std::ptr::copy_nonoverlapping(
            prefix.as_ptr(),
            ret.as_mut_ptr(),
            prefix_len,
        );
        std::ptr::copy_nonoverlapping(
            buf[1..].as_ptr(),
            ret[prefix_len..].as_mut_ptr(),
            buf.len() - 1,
        );
    }
    ret
}

// NB: the correctness of this function depends on
// the invariant that the prefix is ALWAYS lexicographically
// Less than or Equal to the keys that have been encoded
// using it. Otherwise this comparison would make no sense.
pub(crate) fn prefix_cmp(a: &[u8], b: &[u8]) -> Ordering {
    if a.is_empty() && b.is_empty() {
        return Ordering::Equal;
    } else if a.is_empty() && !b.is_empty() {
        return Ordering::Less;
    } else if !a.is_empty() && b.is_empty() {
        return Ordering::Greater;
    }

    if a[0] > b[0] {
        Ordering::Less
    } else if a[0] < b[0] {
        Ordering::Greater
    } else {
        a[1..].cmp(&b[1..])
    }
}

/// Compare `a` and `b`, assuming that `a` is prefix encoded and `b` is not.
pub(crate) fn prefix_cmp_encoded(
    a: &[u8],
    mut b: &[u8],
    mut prefix: &[u8],
) -> Ordering {
    assert!(!a.is_empty() && a[0] as usize <= prefix.len());

    let mut a_prefix_len = a[0];
    let a_suffix = &a[1..];

    while a_prefix_len > 0 {
        if b.is_empty() || prefix[0] > b[0] {
            return Ordering::Greater;
        } else if prefix[0] < b[0] {
            return Ordering::Less;
        }

        a_prefix_len -= 1;
        b = &b[1..];
        prefix = &prefix[1..];
    }

    a_suffix.cmp(b)
}

#[test]
fn test_prefix() {
    let prefix = b"cat";
    assert_eq!(
        prefix_encode(prefix, prefix),
        vec![prefix.len() as u8]
    );
    assert_eq!(prefix_encode(prefix, b"catt"), vec![3, b't']);
    assert_eq!(prefix_encode(prefix, b"cb"), vec![1, b'b']);
    assert_eq!(prefix_encode(prefix, b"caz"), vec![2, b'z']);
    assert_eq!(
        prefix_encode(prefix, b"cvar"),
        vec![1, b'v', b'a', b'r']
    );
    assert_eq!(
        prefix_encode(prefix, b"zig"),
        vec![0, b'z', b'i', b'g']
    );

    let prefix = b"";
    assert_eq!(
        prefix_encode(prefix, prefix),
        vec![prefix.len() as u8]
    );
    assert_eq!(prefix_encode(prefix, b"ca"), vec![0, b'c', b'a']);
    assert_eq!(
        prefix_encode(prefix, b"cat"),
        vec![0, b'c', b'a', b't']
    );
    assert_eq!(
        prefix_encode(prefix, b"cab"),
        vec![0, b'c', b'a', b'b']
    );
    assert_eq!(
        prefix_encode(prefix, b"cvar"),
        vec![0, b'c', b'v', b'a', b'r']
    );
    assert_eq!(
        prefix_encode(prefix, b"zig"),
        vec![0, b'z', b'i', b'g']
    );

    let rtt =
        vec![b"" as &[u8], b"\x00cat", b"\x00", b"oyunwytounw\x00"];
    for item in rtt {
        assert_eq!(
            prefix_decode(prefix, &*prefix_encode(prefix, item)),
            item.to_vec()
        );
    }
}

#[test]
fn test_prefix_cmp() {
    assert_eq!(prefix_cmp(&[], &[]), Ordering::Equal);
    assert_eq!(prefix_cmp(&[0], &[]), Ordering::Greater);
    assert_eq!(prefix_cmp(&[], &[0]), Ordering::Less);

    assert_eq!(prefix_cmp(&[3], &[4]), Ordering::Greater);
    assert_eq!(prefix_cmp(&[4, 3], &[3, 4]), Ordering::Less);

    assert_eq!(prefix_cmp(&[1], &[1]), Ordering::Equal);
    assert_eq!(prefix_cmp(&[1, 1], &[1, 1]), Ordering::Equal);
    assert_eq!(prefix_cmp(&[1, 3], &[1, 1]), Ordering::Greater);
    assert_eq!(prefix_cmp(&[1, 1], &[1, 3]), Ordering::Less);
}

#[test]
fn test_prefix_cmp_encoded() {
    fn assert_pce(
        a: &[u8],
        b: &[u8],
        prefix: &[u8],
        expected: Ordering,
    ) {
        assert_eq!(
            prefix_cmp_encoded(a, &prefix_decode(prefix, b), prefix),
            expected
        );
    }

    let prefix = &vec![3, 3, 3, 3];
    assert_pce(&[0], &[0], prefix, Ordering::Equal);
    assert_pce(&[1], &[0], prefix, Ordering::Greater);
    assert_pce(&[0], &[1], prefix, Ordering::Less);

    assert_pce(&[3], &[2], prefix, Ordering::Greater);
    assert_pce(&[4, 3], &[3, 4], prefix, Ordering::Less);

    assert_pce(&[1], &[1], prefix, Ordering::Equal);
    assert_pce(&[1, 3, 3, 1], &[3, 1], prefix, Ordering::Equal);
    assert_pce(&[1, 3], &[1, 1], prefix, Ordering::Greater);
    assert_pce(&[1, 1], &[3, 3], prefix, Ordering::Less);
}
<|endoftext|>


<commit_before>//! The parser module is responsible for parsing FITS files.

named!(fits<&[u8], ((Vec<(&[u8], &[u8], &[u8])>, (&[u8], Vec<&[u8]>), Vec<Vec<&[u8]> >), Vec<&[u8]>) >,
       pair!(primary_header, many0!( take!(2880) )));

named!(primary_header<&[u8], (Vec<(&[u8], &[u8], &[u8])>, (&[u8], Vec<&[u8]>), Vec<Vec<&[u8]> >)>,
       tuple!(
           many0!(keyword_record),
           end_record,
           many0!(blank_record)
       ));

named!(keyword_record<&[u8], (&[u8], &[u8], &[u8])>,
       tuple!(
           take!(8),
           tag!("="),
           take!(71)
       ));

named!(end_record<&[u8],(&[u8], Vec<&[u8]>)>, pair!(tag!("END"), count!(tag!(" "), 77)));

named!(blank_record<&[u8],Vec<&[u8]> >, count!(tag!(" "), 80));

#[cfg(test)]
mod tests {
    use nom::{IResult};
    use super::{fits, primary_header, end_record, blank_record, keyword_record};

    #[test]
    fn it_should_parse_a_fits_file(){
        let data = include_bytes!("../../assets/images/k2-trappist1-unofficial-tpf-long-cadence.fits");

        let result = fits(data);

        match result {
            IResult::Done(_, (header, blocks)) => {
                assert_eq!(blocks.len(), 3675);
            },
            IResult::Error(_) => panic!("Did not expect an error"),
            IResult::Incomplete(_) => panic!("Did not expect to be incomplete")
        }
    }

    #[test]
    fn primary_header_should_parse_a_primary_header(){
        let data = include_bytes!("../../assets/images/k2-trappist1-unofficial-tpf-long-cadence.fits");

        let result = primary_header(&data[0..(2*2880)]);

        match result {
            IResult::Done(_, _) => assert!(true),
            IResult::Error(_) => panic!("Did not expect an error"),
            IResult::Incomplete(_) => panic!("Did not expect to be incomplete")
        }
    }

    #[test]
    fn keyword_record_should_parse_a_keyword_record(){
        let data = "OBJECT  = 'EPIC 200164267'     / string version of target id                    "
            .as_bytes();

        let result = keyword_record(data);

        match result {
            IResult::Done(_,_) => assert!(true),
            IResult::Error(_) => panic!("Did not expect an error"),
            IResult::Incomplete(_) => panic!("Did not expect to be incomplete")
        }
    }

    #[test]
    fn end_record_should_parse_an_END_record(){
        let data = "END                                                                             "
            .as_bytes();

        let result = end_record(data);

        match result {
            IResult::Done(_,_) => assert!(true),
            IResult::Error(_) => panic!("Did not expect an error"),
            IResult::Incomplete(_) => panic!("Did not expect to be incomplete")
        }
    }

    #[test]
    fn blank_record_should_parse_a_BLANK_record(){
        let data = "                                                                                "
            .as_bytes();

        let result = blank_record(data);

        match result {
            IResult::Done(_,_) => assert!(true),
            IResult::Error(_) => panic!("Did not expect an error"),
            IResult::Incomplete(_) => panic!("Did not expect to be incomplete")
        }
    }
}
<commit_msg>Map to keywords<commit_after>//! The parser module is responsible for parsing FITS files.

use std::str;
use std::str::FromStr;
use super::types::Keyword;

named!(fits<&[u8], ((Vec<Keyword>, (&[u8], Vec<&[u8]>), Vec<Vec<&[u8]> >), Vec<&[u8]>) >,
       pair!(primary_header, many0!( take!(2880) )));

named!(primary_header<&[u8], (Vec<Keyword>, (&[u8], Vec<&[u8]>), Vec<Vec<&[u8]> >)>,
       tuple!(
           many0!(keyword_record),
           end_record,
           many0!(blank_record)
       ));

named!(keyword_record<&[u8], Keyword>,
       map!( // TODO should use map_res!
           tuple!(
               take!(8),
               tag!("="),
               take!(71)
           ), |(slice, _, _) : (&[u8], &[u8], &[u8])| {
               match str::from_utf8(slice) {
                   Ok(s) => {
                       match Keyword::from_str(s) {
                           Ok(keyword) => keyword,
                           Err(_) => Keyword::SIMPLE // TODO this is not correct
                       }
                   }
                   Err(_) => Keyword::SIMPLE // TODO this is not correct
               }
           }));

named!(end_record<&[u8],(&[u8], Vec<&[u8]>)>, pair!(tag!("END"), count!(tag!(" "), 77)));

named!(blank_record<&[u8],Vec<&[u8]> >, count!(tag!(" "), 80));

#[cfg(test)]
mod tests {
    use nom::{IResult};
    use super::{fits, primary_header, end_record, blank_record, keyword_record};

    #[test]
    fn it_should_parse_a_fits_file(){
        let data = include_bytes!("../../assets/images/k2-trappist1-unofficial-tpf-long-cadence.fits");

        let result = fits(data);

        match result {
            IResult::Done(_, (header, blocks)) => {
                assert_eq!(blocks.len(), 3675);
            },
            IResult::Error(_) => panic!("Did not expect an error"),
            IResult::Incomplete(_) => panic!("Did not expect to be incomplete")
        }
    }

    #[test]
    fn primary_header_should_parse_a_primary_header(){
        let data = include_bytes!("../../assets/images/k2-trappist1-unofficial-tpf-long-cadence.fits");

        let result = primary_header(&data[0..(2*2880)]);

        match result {
            IResult::Done(_, _) => assert!(true),
            IResult::Error(_) => panic!("Did not expect an error"),
            IResult::Incomplete(_) => panic!("Did not expect to be incomplete")
        }
    }

    #[test]
    fn keyword_record_should_parse_a_keyword_record(){
        let data = "OBJECT  = 'EPIC 200164267'     / string version of target id                    "
            .as_bytes();

        let result = keyword_record(data);

        match result {
            IResult::Done(_,_) => assert!(true),
            IResult::Error(_) => panic!("Did not expect an error"),
            IResult::Incomplete(_) => panic!("Did not expect to be incomplete")
        }
    }

    #[test]
    fn end_record_should_parse_an_END_record(){
        let data = "END                                                                             "
            .as_bytes();

        let result = end_record(data);

        match result {
            IResult::Done(_,_) => assert!(true),
            IResult::Error(_) => panic!("Did not expect an error"),
            IResult::Incomplete(_) => panic!("Did not expect to be incomplete")
        }
    }

    #[test]
    fn blank_record_should_parse_a_BLANK_record(){
        let data = "                                                                                "
            .as_bytes();

        let result = blank_record(data);

        match result {
            IResult::Done(_,_) => assert!(true),
            IResult::Error(_) => panic!("Did not expect an error"),
            IResult::Incomplete(_) => panic!("Did not expect to be incomplete")
        }
    }
}
<|endoftext|>


<commit_before>use ast::*;

use std::collections::HashMap;

pub trait Evaluate {
    fn evaluate(&self, bindings: &mut HashMap<String, i32>) -> i32;
}

pub trait Print {
    fn reverse_polish(&self) -> String;
}

impl Evaluate for Expr {
    fn evaluate(&self, bindings: &mut HashMap<String, i32>) -> i32 {
        use ast::Expr::*;
        use ast::BinaryOpCode::*;
        use ast::UnaryOpCode::*;

        match self {
            &Assignment(ref name, box ref exp) => {
                let value = exp.evaluate(bindings);
                bindings.insert(name.clone(), value);
                value
            }
            &Function(ref name, ref args) => {
                if name == "print" && args.len() == 1 {
                    println!("=> {}", &args[0].evaluate(bindings));
                    return 0;
                } else {
                    panic!("Unknown function: {}/{}", name, args.len());
                }
            }
            &BinaryOp(box ref lhs, box ref rhs, ref op) => {
                match op {
                    &Add => lhs.evaluate(bindings) + rhs.evaluate(bindings),
                    &Sub => lhs.evaluate(bindings) - rhs.evaluate(bindings),
                    &Mul => lhs.evaluate(bindings) * rhs.evaluate(bindings),
                    &Div => lhs.evaluate(bindings) / rhs.evaluate(bindings),
                }
            }
            &UnaryOp(box ref exp, ref op) => {
                match op {
                    &Plus => exp.evaluate(bindings),
                    &Minus => -exp.evaluate(bindings),
                }
            }
            &Variable(ref name) => {
                *bindings.get(name).expect(format!("Unbounded variable: {}", name).as_str())
            }
            &Num(value) => value,
        }
    }
}

impl Evaluate for Exprs {
    fn evaluate(&self, bindings: &mut HashMap<String, i32>) -> i32 {
        let mut value = 0;
        for expr in self.exprs.iter() {
            value = expr.evaluate(bindings);
        }
        value
    }
}

impl Print for Expr {
    fn reverse_polish(&self) -> String {
        use ast::Expr::*;
        use ast::BinaryOpCode::*;
        use ast::UnaryOpCode::*;

        match self {
            &Assignment(ref name, box ref exp) => format!("{} {} =", name, exp.reverse_polish()),
            &Function(ref name, ref args) => {
                format!("{}{}",
                        args.iter()
                            .fold(String::new(),
                                  |s, arg| format!("{}{} ", s, arg.reverse_polish())),
                        name)
            }
            &BinaryOp(box ref lhs, box ref rhs, ref op) => {
                match op {
                    &Add => format!("{} {} +", &lhs.reverse_polish(), &rhs.reverse_polish()),
                    &Sub => format!("{} {} -", &lhs.reverse_polish(), &rhs.reverse_polish()),
                    &Mul => format!("{} {} *", &lhs.reverse_polish(), &rhs.reverse_polish()),
                    &Div => format!("{} {} /", &lhs.reverse_polish(), &rhs.reverse_polish()),
                }
            }
            &UnaryOp(box ref exp, ref op) => {
                match op {
                    &Plus => format!("{} ++", &exp.reverse_polish()),
                    &Minus => format!("{} --", &exp.reverse_polish()),
                }
            }
            &Variable(ref name) => name.clone(),
            &Num(value) => value.to_string(),
        }
    }
}

impl Print for Exprs {
    fn reverse_polish(&self) -> String {
        let mut result = String::new();
        for expr in self.exprs.iter() {
            result += expr.reverse_polish().as_str();
            result += "\n";
        }
        result.to_string()
    }
}

pub fn lisp(tree: &Expr) -> String {
    use ast::Expr::*;
    use ast::BinaryOpCode::*;
    use ast::UnaryOpCode::*;

    match tree {
        &Assignment(ref name, box ref exp) => format!("(let {} {})", name, lisp(exp)),
        &Function(ref name, ref args) => {
            format!("({}{})",
                    name,
                    args.iter()
                        .fold(String::new(),
                              |s, arg| format!("{} ({})", s, arg.reverse_polish())))
        }
        &BinaryOp(box ref lhs, box ref rhs, ref op) => {
            match op {
                &Add => format!("(+ {} {})", lisp(&lhs), lisp(&rhs)),
                &Sub => format!("(- {} {})", lisp(&lhs), lisp(&rhs)),
                &Mul => format!("(* {} {})", lisp(&lhs), lisp(&rhs)),
                &Div => format!("(/ {} {})", lisp(&lhs), lisp(&rhs)),
            }
        }
        &UnaryOp(box ref exp, ref op) => {
            match op {
                &Plus => format!("(++ {})", lisp(&exp)),
                &Minus => format!("(-- {})", lisp(&exp)),
            }
        }
        &Variable(ref name) => name.clone(),
        &Num(value) => value.to_string(),
    }
}
<commit_msg>src/processing.rs<commit_after>
use calculator::*;

use ast::*;

use std::collections::HashMap;

pub trait Evaluate {
    fn evaluate(&self, bindings: &mut HashMap<String, i32>) -> i32;
}

pub trait Print {
    fn reverse_polish(&self) -> String;
}

impl Evaluate for Expr {
    fn evaluate(&self, bindings: &mut HashMap<String, i32>) -> i32 {
        use ast::Expr::*;
        use ast::BinaryOpCode::*;
        use ast::UnaryOpCode::*;

        match self {
            &Assignment(ref name, box ref exp) => {
                let value = exp.evaluate(bindings);
                bindings.insert(name.clone(), value);
                value
            }
            &Function(ref name, ref args) => {
                if name == "print" && args.len() == 1 {
                    println!("=> {}", &args[0].evaluate(bindings));
                    return 0;
                } else {
                    panic!("Unknown function: {}/{}", name, args.len());
                }
            }
            &BinaryOp(box ref lhs, box ref rhs, ref op) => {
                match op {
                    &Add => lhs.evaluate(bindings) + rhs.evaluate(bindings),
                    &Sub => lhs.evaluate(bindings) - rhs.evaluate(bindings),
                    &Mul => lhs.evaluate(bindings) * rhs.evaluate(bindings),
                    &Div => lhs.evaluate(bindings) / rhs.evaluate(bindings),
                }
            }
            &UnaryOp(box ref exp, ref op) => {
                match op {
                    &Plus => exp.evaluate(bindings),
                    &Minus => -exp.evaluate(bindings),
                }
            }
            &Variable(ref name) => {
                *bindings.get(name).expect(format!("Unbounded variable: {}", name).as_str())
            }
            &Num(value) => value,
        }
    }
}

impl Evaluate for Exprs {
    fn evaluate(&self, bindings: &mut HashMap<String, i32>) -> i32 {
        let mut value = 0;
        for expr in self.exprs.iter() {
            value = expr.evaluate(bindings);
        }
        value
    }
}

impl Print for Expr {
    fn reverse_polish(&self) -> String {
        use ast::Expr::*;
        use ast::BinaryOpCode::*;
        use ast::UnaryOpCode::*;

        match self {
            &Assignment(ref name, box ref exp) => format!("{} {} =", name, exp.reverse_polish()),
            &Function(ref name, ref args) => {
                format!("{}{}",
                        args.iter()
                            .fold(String::new(),
                                  |s, arg| format!("{}{} ", s, arg.reverse_polish())),
                        name)
            }
            &BinaryOp(box ref lhs, box ref rhs, ref op) => {
                match op {
                    &Add => format!("{} {} +", &lhs.reverse_polish(), &rhs.reverse_polish()),
                    &Sub => format!("{} {} -", &lhs.reverse_polish(), &rhs.reverse_polish()),
                    &Mul => format!("{} {} *", &lhs.reverse_polish(), &rhs.reverse_polish()),
                    &Div => format!("{} {} /", &lhs.reverse_polish(), &rhs.reverse_polish()),
                }
            }
            &UnaryOp(box ref exp, ref op) => {
                match op {
                    &Plus => format!("{} ++", &exp.reverse_polish()),
                    &Minus => format!("{} --", &exp.reverse_polish()),
                }
            }
            &Variable(ref name) => name.clone(),
            &Num(value) => value.to_string(),
        }
    }
}

impl Print for Exprs {
    fn reverse_polish(&self) -> String {
        let mut result = String::new();
        for expr in self.exprs.iter() {
            result += expr.reverse_polish().as_str();
            result += "\n";
        }
        result.to_string()
    }
}

pub fn lisp(tree: &Expr) -> String {
    use ast::Expr::*;
    use ast::BinaryOpCode::*;
    use ast::UnaryOpCode::*;

    match tree {
        &Assignment(ref name, box ref exp) => format!("(let {} {})", name, lisp(exp)),
        &Function(ref name, ref args) => {
            format!("({}{})",
                    name,
                    args.iter()
                        .fold(String::new(),
                              |s, arg| format!("{} ({})", s, arg.reverse_polish())))
        }
        &BinaryOp(box ref lhs, box ref rhs, ref op) => {
            match op {
                &Add => format!("(+ {} {})", lisp(&lhs), lisp(&rhs)),
                &Sub => format!("(- {} {})", lisp(&lhs), lisp(&rhs)),
                &Mul => format!("(* {} {})", lisp(&lhs), lisp(&rhs)),
                &Div => format!("(/ {} {})", lisp(&lhs), lisp(&rhs)),
            }
        }
        &UnaryOp(box ref exp, ref op) => {
            match op {
                &Plus => format!("(++ {})", lisp(&exp)),
                &Minus => format!("(-- {})", lisp(&exp)),
            }
        }
        &Variable(ref name) => name.clone(),
        &Num(value) => value.to_string(),
    }
}

#[cfg(test)]
mod test {
    use calculator;
    use super::Print;
#[test]
    fn it_works() {
        let exp_str = "2+2+2";
        let expected = "2 2 + 2 +\n";
        let exp = calculator::parse_Expressions(exp_str).unwrap();
        let result = &exp.reverse_polish();
        assert_eq!(expected,result);
    }
}
<|endoftext|>


<commit_before>// Copyright 2018 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.

//! A small fast RNG

use {RngCore, SeedableRng, Error};

#[cfg(all(all(rustc_1_26, not(target_os = "emscripten")), target_pointer_width = "64"))]
type Rng = ::rand_pcg::Pcg64Mcg;
#[cfg(not(all(all(rustc_1_26, not(target_os = "emscripten")), target_pointer_width = "64")))]
type Rng = ::rand_pcg::Pcg32;

/// An RNG recommended when small state, cheap initialization and good
/// performance are required. The PRNG algorithm in `SmallRng` is chosen to be
/// efficient on the current platform, **without consideration for cryptography
/// or security**. The size of its state is much smaller than for [`StdRng`].
///
/// Reproducibility of output from this generator is however not required, thus
/// future library versions may use a different internal generator with
/// different output. Further, this generator may not be portable and can
/// produce different output depending on the architecture. If you require
/// reproducible output, use a named RNG. Refer to the documentation on the
/// [`prng`][crate::prng] module.
///
/// The current algorithm is [`Pcg64Mcg`][rand_pcg::Pcg64Mcg] on 64-bit platforms with Rust version
/// 1.26 and later, or [`Pcg32`][rand_pcg::Pcg32] otherwise.
///
/// # Examples
///
/// Initializing `SmallRng` with a random seed can be done using [`FromEntropy`]:
///
/// ```
/// # use rand::Rng;
/// use rand::FromEntropy;
/// use rand::rngs::SmallRng;
///
/// // Create small, cheap to initialize and fast RNG with a random seed.
/// // The randomness is supplied by the operating system.
/// let mut small_rng = SmallRng::from_entropy();
/// # let v: u32 = small_rng.gen();
/// ```
///
/// When initializing a lot of `SmallRng`'s, using [`thread_rng`] can be more
/// efficient:
///
/// ```
/// use std::iter;
/// use rand::{SeedableRng, thread_rng};
/// use rand::rngs::SmallRng;
///
/// // Create a big, expensive to initialize and slower, but unpredictable RNG.
/// // This is cached and done only once per thread.
/// let mut thread_rng = thread_rng();
/// // Create small, cheap to initialize and fast RNGs with random seeds.
/// // One can generally assume this won't fail.
/// let rngs: Vec<SmallRng> = iter::repeat(())
///     .map(|()| SmallRng::from_rng(&mut thread_rng).unwrap())
///     .take(10)
///     .collect();
/// ```
///
/// [`FromEntropy`]: crate::FromEntropy
/// [`StdRng`]: crate::rngs::StdRng
/// [`thread_rng`]: crate::thread_rng
#[derive(Clone, Debug)]
pub struct SmallRng(Rng);

impl RngCore for SmallRng {
    #[inline(always)]
    fn next_u32(&mut self) -> u32 {
        self.0.next_u32()
    }

    #[inline(always)]
    fn next_u64(&mut self) -> u64 {
        self.0.next_u64()
    }

    fn fill_bytes(&mut self, dest: &mut [u8]) {
        self.0.fill_bytes(dest);
    }

    fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {
        self.0.try_fill_bytes(dest)
    }
}

impl SeedableRng for SmallRng {
    type Seed = <Rng as SeedableRng>::Seed;

    fn from_seed(seed: Self::Seed) -> Self {
        SmallRng(Rng::from_seed(seed))
    }

    fn from_rng<R: RngCore>(rng: R) -> Result<Self, Error> {
        Rng::from_rng(rng).map(SmallRng)
    }
}
<commit_msg>Fix external links in SmallRng documentation<commit_after>// Copyright 2018 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.

//! A small fast RNG

use {RngCore, SeedableRng, Error};

#[cfg(all(all(rustc_1_26, not(target_os = "emscripten")), target_pointer_width = "64"))]
type Rng = ::rand_pcg::Pcg64Mcg;
#[cfg(not(all(all(rustc_1_26, not(target_os = "emscripten")), target_pointer_width = "64")))]
type Rng = ::rand_pcg::Pcg32;

/// An RNG recommended when small state, cheap initialization and good
/// performance are required. The PRNG algorithm in `SmallRng` is chosen to be
/// efficient on the current platform, **without consideration for cryptography
/// or security**. The size of its state is much smaller than for [`StdRng`].
///
/// Reproducibility of output from this generator is however not required, thus
/// future library versions may use a different internal generator with
/// different output. Further, this generator may not be portable and can
/// produce different output depending on the architecture. If you require
/// reproducible output, use a named RNG.
/// Refer to [The Book](https://rust-random.github.io/book/guide-rngs.html).
///
///
/// The current algorithm is [`Pcg64Mcg`][rand_pcg::Pcg64Mcg] on 64-bit platforms with Rust version
/// 1.26 and later, or [`Pcg32`][rand_pcg::Pcg32] otherwise. Both are found in
/// the [rand_pcg] crate.
///
/// # Examples
///
/// Initializing `SmallRng` with a random seed can be done using [`FromEntropy`]:
///
/// ```
/// # use rand::Rng;
/// use rand::FromEntropy;
/// use rand::rngs::SmallRng;
///
/// // Create small, cheap to initialize and fast RNG with a random seed.
/// // The randomness is supplied by the operating system.
/// let mut small_rng = SmallRng::from_entropy();
/// # let v: u32 = small_rng.gen();
/// ```
///
/// When initializing a lot of `SmallRng`'s, using [`thread_rng`] can be more
/// efficient:
///
/// ```
/// use std::iter;
/// use rand::{SeedableRng, thread_rng};
/// use rand::rngs::SmallRng;
///
/// // Create a big, expensive to initialize and slower, but unpredictable RNG.
/// // This is cached and done only once per thread.
/// let mut thread_rng = thread_rng();
/// // Create small, cheap to initialize and fast RNGs with random seeds.
/// // One can generally assume this won't fail.
/// let rngs: Vec<SmallRng> = iter::repeat(())
///     .map(|()| SmallRng::from_rng(&mut thread_rng).unwrap())
///     .take(10)
///     .collect();
/// ```
///
/// [`FromEntropy`]: crate::FromEntropy
/// [`StdRng`]: crate::rngs::StdRng
/// [`thread_rng`]: crate::thread_rng
/// [rand_pcg]: https://crates.io/crates/rand_pcg
#[derive(Clone, Debug)]
pub struct SmallRng(Rng);

impl RngCore for SmallRng {
    #[inline(always)]
    fn next_u32(&mut self) -> u32 {
        self.0.next_u32()
    }

    #[inline(always)]
    fn next_u64(&mut self) -> u64 {
        self.0.next_u64()
    }

    fn fill_bytes(&mut self, dest: &mut [u8]) {
        self.0.fill_bytes(dest);
    }

    fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {
        self.0.try_fill_bytes(dest)
    }
}

impl SeedableRng for SmallRng {
    type Seed = <Rng as SeedableRng>::Seed;

    fn from_seed(seed: Self::Seed) -> Self {
        SmallRng(Rng::from_seed(seed))
    }

    fn from_rng<R: RngCore>(rng: R) -> Result<Self, Error> {
        Rng::from_rng(rng).map(SmallRng)
    }
}
<|endoftext|>