<commit_before>use {Error, Errno, Result};
use std::os::unix::io::RawFd;
use libc::{c_void, off_t, size_t};
use libc;
use std::io::Write;
use std::io::stderr;
use std::marker::PhantomData;
use std::mem;
use std::ptr::{null, null_mut};
use sys::signal::*;
use sys::time::TimeSpec;

/// Mode for `aio_fsync`.  Controls whether only data or both data and metadata
/// are synced.
#[repr(i32)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum AioFsyncMode {
    /// do it like `fsync`
    O_SYNC = libc::O_SYNC,
    /// on supported operating systems only, do it like `fdatasync`
    #[cfg(any(target_os = "openbsd", target_os = "bitrig",
              target_os = "netbsd", target_os = "macos", target_os = "ios",
              target_os = "linux"))]
    O_DSYNC = libc::O_DSYNC
}

/// When used with `lio_listio`, determines whether a given `aiocb` should be
/// used for a read operation, a write operation, or ignored.  Has no effect for
/// any other aio functions.
#[repr(i32)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum LioOpcode {
    LIO_NOP = libc::LIO_NOP,
    LIO_WRITE = libc::LIO_WRITE,
    LIO_READ = libc::LIO_READ
}

/// Mode for `lio_listio`.
#[repr(i32)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum LioMode {
    /// Requests that `lio_listio` block until all requested operations have
    /// been completed
    LIO_WAIT = libc::LIO_WAIT,
    /// Requests that `lio_listio` return immediately
    LIO_NOWAIT = libc::LIO_NOWAIT,
}

/// Return values for `aio_cancel`
#[repr(i32)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum AioCancelStat {
    /// All outstanding requests were canceled
    AioCanceled = libc::AIO_CANCELED,
    /// Some requests were not canceled.  Their status should be checked with
    /// `aio_error`
    AioNotCanceled = libc::AIO_NOTCANCELED,
    /// All of the requests have already finished
    AioAllDone = libc::AIO_ALLDONE,
}

/// The basic structure used by all aio functions.  Each `aiocb` represents one
/// I/O request.
pub struct AioCb<'a> {
    aiocb: libc::aiocb,
    /// Tracks whether the buffer pointed to by aiocb.aio_buf is mutable
    mutable: bool,
    /// Could this `AioCb` potentially have any in-kernel state?
    in_progress: bool,
    phantom: PhantomData<&'a mut [u8]>
}

impl<'a> AioCb<'a> {
    /// Constructs a new `AioCb` with no associated buffer.
    ///
    /// The resulting `AioCb` structure is suitable for use with `aio_fsync`.
    /// * `fd`  File descriptor.  Required for all aio functions.
    /// * `prio` If POSIX Prioritized IO is supported, then the operation will
    /// be prioritized at the process's priority level minus `prio`
    /// * `sigev_notify` Determines how you will be notified of event
    /// completion.
    pub fn from_fd(fd: RawFd, prio: ::c_int,
                    sigev_notify: SigevNotify) -> AioCb<'a> {
        let mut a = AioCb::common_init(fd, prio, sigev_notify);
        a.aio_offset = 0;
        a.aio_nbytes = 0;
        a.aio_buf = null_mut();

        let aiocb = AioCb { aiocb: a, mutable: false, in_progress: false,
                            phantom: PhantomData};
        aiocb
    }

    /// Constructs a new `AioCb`.
    ///
    /// * `fd`  File descriptor.  Required for all aio functions.
    /// * `offs` File offset
    /// * `buf` A memory buffer
    /// * `prio` If POSIX Prioritized IO is supported, then the operation will
    /// be prioritized at the process's priority level minus `prio`
    /// * `sigev_notify` Determines how you will be notified of event
    /// completion.
    /// * `opcode` This field is only used for `lio_listio`.  It determines
    /// which operation to use for this individual aiocb
    pub fn from_mut_slice(fd: RawFd, offs: off_t, buf: &'a mut [u8],
                          prio: ::c_int, sigev_notify: SigevNotify,
                          opcode: LioOpcode) -> AioCb {
        let mut a = AioCb::common_init(fd, prio, sigev_notify);
        a.aio_offset = offs;
        a.aio_nbytes = buf.len() as size_t;
        // casting an immutable buffer to a mutable pointer looks unsafe, but
        // technically its only unsafe to dereference it, not to create it.
        a.aio_buf = buf.as_ptr() as *mut c_void;
        a.aio_lio_opcode = opcode as ::c_int;

        let aiocb = AioCb { aiocb: a, mutable: true, in_progress: false,
                            phantom: PhantomData};
        aiocb
    }

    /// Like `from_mut_slice`, but works on constant slices rather than
    /// mutable slices.
    ///
    /// An `AioCb` created this way cannot be used with `read`, and its
    /// `LioOpcode` cannot be set to `LIO_READ`.  This method is useful when writing a
    /// const buffer with `aio_write`, since from_mut_slice can't work with const
    /// buffers.
    // Note: another solution to the problem of writing const buffers would be
    // to genericize AioCb for both &mut [u8] and &[u8] buffers.  aio_read could
    // take the former and aio_write could take the latter.  However, then
    // lio_listio wouldn't work, because that function needs a slice of AioCb,
    // and they must all be the same type.  We're basically stuck with using an
    // unsafe function, since aio (as designed in C) is an unsafe API.
    pub fn from_slice(fd: RawFd, offs: off_t, buf: &'a [u8],
                      prio: ::c_int, sigev_notify: SigevNotify,
                      opcode: LioOpcode) -> AioCb {
        let mut a = AioCb::common_init(fd, prio, sigev_notify);
        a.aio_offset = offs;
        a.aio_nbytes = buf.len() as size_t;
        a.aio_buf = buf.as_ptr() as *mut c_void;
        assert!(opcode != LioOpcode::LIO_READ, "Can't read into an immutable buffer");
        a.aio_lio_opcode = opcode as ::c_int;

        let aiocb = AioCb { aiocb: a, mutable: false, in_progress: false,
                            phantom: PhantomData};
        aiocb
    }

    fn common_init(fd: RawFd, prio: ::c_int,
                   sigev_notify: SigevNotify) -> libc::aiocb {
        // Use mem::zeroed instead of explicitly zeroing each field, because the
        // number and name of reserved fields is OS-dependent.  On some OSes,
        // some reserved fields are used the kernel for state, and must be
        // explicitly zeroed when allocated.
        let mut a = unsafe { mem::zeroed::<libc::aiocb>()};
        a.aio_fildes = fd;
        a.aio_reqprio = prio;
        a.aio_sigevent = SigEvent::new(sigev_notify).sigevent();
        a
    }

    /// Update the notification settings for an existing `aiocb`
    pub fn set_sigev_notify(&mut self, sigev_notify: SigevNotify) {
        self.aiocb.aio_sigevent = SigEvent::new(sigev_notify).sigevent();
    }

    /// Cancels an outstanding AIO request.
    pub fn cancel(&mut self) -> Result<AioCancelStat> {
        match unsafe { libc::aio_cancel(self.aiocb.aio_fildes, &mut self.aiocb) } {
            libc::AIO_CANCELED => Ok(AioCancelStat::AioCanceled),
            libc::AIO_NOTCANCELED => Ok(AioCancelStat::AioNotCanceled),
            libc::AIO_ALLDONE => Ok(AioCancelStat::AioAllDone),
            -1 => Err(Error::last()),
            _ => panic!("unknown aio_cancel return value")
        }
    }

    /// Retrieve error status of an asynchronous operation.  If the request has not
    /// yet completed, returns `EINPROGRESS`.  Otherwise, returns `Ok` or any other
    /// error.
    pub fn error(&mut self) -> Result<()> {
        match unsafe { libc::aio_error(&mut self.aiocb as *mut libc::aiocb) } {
            0 => Ok(()),
            num if num > 0 => Err(Error::from_errno(Errno::from_i32(num))),
            -1 => Err(Error::last()),
            num => panic!("unknown aio_error return value {:?}", num)
        }
    }

    /// An asynchronous version of `fsync`.
    pub fn fsync(&mut self, mode: AioFsyncMode) -> Result<()> {
        let p: *mut libc::aiocb = &mut self.aiocb;
        self.in_progress = true;
        Errno::result(unsafe { libc::aio_fsync(mode as ::c_int, p) }).map(drop)
    }

    /// Asynchronously reads from a file descriptor into a buffer
    pub fn read(&mut self) -> Result<()> {
        assert!(self.mutable, "Can't read into an immutable buffer");
        let p: *mut libc::aiocb = &mut self.aiocb;
        self.in_progress = true;
        Errno::result(unsafe { libc::aio_read(p) }).map(drop)
    }

    /// Retrieve return status of an asynchronous operation.  Should only be called
    /// once for each `AioCb`, after `aio_error` indicates that it has completed.
    /// The result the same as for `read`, `write`, of `fsync`.
    // Note: this should be just `return`, but that's a reserved word
    pub fn aio_return(&mut self) -> Result<isize> {
        let p: *mut libc::aiocb = &mut self.aiocb;
        self.in_progress = false;
        Errno::result(unsafe { libc::aio_return(p) })
    }

    /// Asynchronously writes from a buffer to a file descriptor
    pub fn write(&mut self) -> Result<()> {
        let p: *mut libc::aiocb = &mut self.aiocb;
        self.in_progress = true;
        Errno::result(unsafe { libc::aio_write(p) }).map(drop)
    }

}

/// Cancels outstanding AIO requests.  All requests for `fd` will be cancelled.
pub fn aio_cancel_all(fd: RawFd) -> Result<AioCancelStat> {
    match unsafe { libc::aio_cancel(fd, null_mut()) } {
        libc::AIO_CANCELED => Ok(AioCancelStat::AioCanceled),
        libc::AIO_NOTCANCELED => Ok(AioCancelStat::AioNotCanceled),
        libc::AIO_ALLDONE => Ok(AioCancelStat::AioAllDone),
        -1 => Err(Error::last()),
        _ => panic!("unknown aio_cancel return value")
    }
}

/// Suspends the calling process until at least one of the specified `AioCb`s
/// has completed, a signal is delivered, or the timeout has passed.  If
/// `timeout` is `None`, `aio_suspend` will block indefinitely.
pub fn aio_suspend(list: &[&AioCb], timeout: Option<TimeSpec>) -> Result<()> {
    // We must use transmute because Rust doesn't understand that a pointer to a
    // Struct is the same as a pointer to its first element.
    let plist = unsafe {
        mem::transmute::<&[&AioCb], *const [*const libc::aiocb]>(list)
    };
    let p = plist as *const *const libc::aiocb;
    let timep = match timeout {
        None    => null::<libc::timespec>(),
        Some(x) => x.as_ref() as *const libc::timespec
    };
    Errno::result(unsafe {
        libc::aio_suspend(p, list.len() as i32, timep)
    }).map(drop)
}

/// Submits multiple asynchronous I/O requests with a single system call.  The
/// order in which the requests are carried out is not specified.
#[cfg(not(any(target_os = "ios", target_os = "macos")))]
pub fn lio_listio(mode: LioMode, list: &[&mut AioCb],
                  sigev_notify: SigevNotify) -> Result<()> {
    let sigev = SigEvent::new(sigev_notify);
    let sigevp = &mut sigev.sigevent() as *mut libc::sigevent;
    // We must use transmute because Rust doesn't understand that a pointer to a
    // Struct is the same as a pointer to its first element.
    let plist = unsafe {
        mem::transmute::<&[&mut AioCb], *const [*mut libc::aiocb]>(list)
    };
    let p = plist as *const *mut libc::aiocb;
    Errno::result(unsafe {
        libc::lio_listio(mode as i32, p, list.len() as i32, sigevp)
    }).map(drop)
}

impl<'a> Drop for AioCb<'a> {
    /// If the `AioCb` has no remaining state in the kernel, just drop it.
    /// Otherwise, collect its error and return values, so as not to leak
    /// resources.
    fn drop(&mut self) {
        if self.in_progress {
            // Well-written programs should never get here.  They should always
            // wait for an AioCb to complete before dropping it
            let _ = write!(stderr(), "WARNING: dropped an in-progress AioCb");
            loop {
                let ret = aio_suspend(&[&self], None);
                match ret {
                    Ok(()) => break,
                    Err(Error::Sys(Errno::EINVAL)) => panic!(
                        "Inconsistent AioCb.in_progress value"),
                    Err(Error::Sys(Errno::EINTR)) => (),    // Retry interrupted syscall
                    _ => panic!("Unexpected aio_suspend return value {:?}", ret)
                };
            }
            let _ = self.aio_return();
        }
    }
}
<commit_msg>Fix some identifiers in comments<commit_after>use {Error, Errno, Result};
use std::os::unix::io::RawFd;
use libc::{c_void, off_t, size_t};
use libc;
use std::io::Write;
use std::io::stderr;
use std::marker::PhantomData;
use std::mem;
use std::ptr::{null, null_mut};
use sys::signal::*;
use sys::time::TimeSpec;

/// Mode for `AioCb::fsync`.  Controls whether only data or both data and
/// metadata are synced.
#[repr(i32)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum AioFsyncMode {
    /// do it like `fsync`
    O_SYNC = libc::O_SYNC,
    /// on supported operating systems only, do it like `fdatasync`
    #[cfg(any(target_os = "openbsd", target_os = "bitrig",
              target_os = "netbsd", target_os = "macos", target_os = "ios",
              target_os = "linux"))]
    O_DSYNC = libc::O_DSYNC
}

/// When used with `lio_listio`, determines whether a given `aiocb` should be
/// used for a read operation, a write operation, or ignored.  Has no effect for
/// any other aio functions.
#[repr(i32)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum LioOpcode {
    LIO_NOP = libc::LIO_NOP,
    LIO_WRITE = libc::LIO_WRITE,
    LIO_READ = libc::LIO_READ
}

/// Mode for `lio_listio`.
#[repr(i32)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum LioMode {
    /// Requests that `lio_listio` block until all requested operations have
    /// been completed
    LIO_WAIT = libc::LIO_WAIT,
    /// Requests that `lio_listio` return immediately
    LIO_NOWAIT = libc::LIO_NOWAIT,
}

/// Return values for `AioCb::cancel and aio_cancel_all`
#[repr(i32)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum AioCancelStat {
    /// All outstanding requests were canceled
    AioCanceled = libc::AIO_CANCELED,
    /// Some requests were not canceled.  Their status should be checked with
    /// `AioCb::error`
    AioNotCanceled = libc::AIO_NOTCANCELED,
    /// All of the requests have already finished
    AioAllDone = libc::AIO_ALLDONE,
}

/// The basic structure used by all aio functions.  Each `aiocb` represents one
/// I/O request.
pub struct AioCb<'a> {
    aiocb: libc::aiocb,
    /// Tracks whether the buffer pointed to by aiocb.aio_buf is mutable
    mutable: bool,
    /// Could this `AioCb` potentially have any in-kernel state?
    in_progress: bool,
    phantom: PhantomData<&'a mut [u8]>
}

impl<'a> AioCb<'a> {
    /// Constructs a new `AioCb` with no associated buffer.
    ///
    /// The resulting `AioCb` structure is suitable for use with `AioCb::fsync`.
    /// * `fd`  File descriptor.  Required for all aio functions.
    /// * `prio` If POSIX Prioritized IO is supported, then the operation will
    /// be prioritized at the process's priority level minus `prio`
    /// * `sigev_notify` Determines how you will be notified of event
    /// completion.
    pub fn from_fd(fd: RawFd, prio: ::c_int,
                    sigev_notify: SigevNotify) -> AioCb<'a> {
        let mut a = AioCb::common_init(fd, prio, sigev_notify);
        a.aio_offset = 0;
        a.aio_nbytes = 0;
        a.aio_buf = null_mut();

        let aiocb = AioCb { aiocb: a, mutable: false, in_progress: false,
                            phantom: PhantomData};
        aiocb
    }

    /// Constructs a new `AioCb`.
    ///
    /// * `fd`  File descriptor.  Required for all aio functions.
    /// * `offs` File offset
    /// * `buf` A memory buffer
    /// * `prio` If POSIX Prioritized IO is supported, then the operation will
    /// be prioritized at the process's priority level minus `prio`
    /// * `sigev_notify` Determines how you will be notified of event
    /// completion.
    /// * `opcode` This field is only used for `lio_listio`.  It determines
    /// which operation to use for this individual aiocb
    pub fn from_mut_slice(fd: RawFd, offs: off_t, buf: &'a mut [u8],
                          prio: ::c_int, sigev_notify: SigevNotify,
                          opcode: LioOpcode) -> AioCb {
        let mut a = AioCb::common_init(fd, prio, sigev_notify);
        a.aio_offset = offs;
        a.aio_nbytes = buf.len() as size_t;
        // casting an immutable buffer to a mutable pointer looks unsafe, but
        // technically its only unsafe to dereference it, not to create it.
        a.aio_buf = buf.as_ptr() as *mut c_void;
        a.aio_lio_opcode = opcode as ::c_int;

        let aiocb = AioCb { aiocb: a, mutable: true, in_progress: false,
                            phantom: PhantomData};
        aiocb
    }

    /// Like `from_mut_slice`, but works on constant slices rather than
    /// mutable slices.
    ///
    /// An `AioCb` created this way cannot be used with `read`, and its
    /// `LioOpcode` cannot be set to `LIO_READ`.  This method is useful when
    /// writing a const buffer with `AioCb::write`, since from_mut_slice can't
    /// work with const buffers.
    // Note: another solution to the problem of writing const buffers would be
    // to genericize AioCb for both &mut [u8] and &[u8] buffers.  AioCb::read
    // could take the former and AioCb::write could take the latter.  However,
    // then lio_listio wouldn't work, because that function needs a slice of
    // AioCb, and they must all be the same type.  We're basically stuck with
    // using an unsafe function, since aio (as designed in C) is an unsafe API.
    pub fn from_slice(fd: RawFd, offs: off_t, buf: &'a [u8],
                      prio: ::c_int, sigev_notify: SigevNotify,
                      opcode: LioOpcode) -> AioCb {
        let mut a = AioCb::common_init(fd, prio, sigev_notify);
        a.aio_offset = offs;
        a.aio_nbytes = buf.len() as size_t;
        a.aio_buf = buf.as_ptr() as *mut c_void;
        assert!(opcode != LioOpcode::LIO_READ, "Can't read into an immutable buffer");
        a.aio_lio_opcode = opcode as ::c_int;

        let aiocb = AioCb { aiocb: a, mutable: false, in_progress: false,
                            phantom: PhantomData};
        aiocb
    }

    fn common_init(fd: RawFd, prio: ::c_int,
                   sigev_notify: SigevNotify) -> libc::aiocb {
        // Use mem::zeroed instead of explicitly zeroing each field, because the
        // number and name of reserved fields is OS-dependent.  On some OSes,
        // some reserved fields are used the kernel for state, and must be
        // explicitly zeroed when allocated.
        let mut a = unsafe { mem::zeroed::<libc::aiocb>()};
        a.aio_fildes = fd;
        a.aio_reqprio = prio;
        a.aio_sigevent = SigEvent::new(sigev_notify).sigevent();
        a
    }

    /// Update the notification settings for an existing `aiocb`
    pub fn set_sigev_notify(&mut self, sigev_notify: SigevNotify) {
        self.aiocb.aio_sigevent = SigEvent::new(sigev_notify).sigevent();
    }

    /// Cancels an outstanding AIO request.
    pub fn cancel(&mut self) -> Result<AioCancelStat> {
        match unsafe { libc::aio_cancel(self.aiocb.aio_fildes, &mut self.aiocb) } {
            libc::AIO_CANCELED => Ok(AioCancelStat::AioCanceled),
            libc::AIO_NOTCANCELED => Ok(AioCancelStat::AioNotCanceled),
            libc::AIO_ALLDONE => Ok(AioCancelStat::AioAllDone),
            -1 => Err(Error::last()),
            _ => panic!("unknown aio_cancel return value")
        }
    }

    /// Retrieve error status of an asynchronous operation.  If the request has
    /// not yet completed, returns `EINPROGRESS`.  Otherwise, returns `Ok` or
    /// any other error.
    pub fn error(&mut self) -> Result<()> {
        match unsafe { libc::aio_error(&mut self.aiocb as *mut libc::aiocb) } {
            0 => Ok(()),
            num if num > 0 => Err(Error::from_errno(Errno::from_i32(num))),
            -1 => Err(Error::last()),
            num => panic!("unknown aio_error return value {:?}", num)
        }
    }

    /// An asynchronous version of `fsync`.
    pub fn fsync(&mut self, mode: AioFsyncMode) -> Result<()> {
        let p: *mut libc::aiocb = &mut self.aiocb;
        self.in_progress = true;
        Errno::result(unsafe { libc::aio_fsync(mode as ::c_int, p) }).map(drop)
    }

    /// Asynchronously reads from a file descriptor into a buffer
    pub fn read(&mut self) -> Result<()> {
        assert!(self.mutable, "Can't read into an immutable buffer");
        let p: *mut libc::aiocb = &mut self.aiocb;
        self.in_progress = true;
        Errno::result(unsafe { libc::aio_read(p) }).map(drop)
    }

    /// Retrieve return status of an asynchronous operation.  Should only be
    /// called once for each `AioCb`, after `AioCb::error` indicates that it has
    /// completed.  The result is the same as for `read`, `write`, of `fsync`.
    // Note: this should be just `return`, but that's a reserved word
    pub fn aio_return(&mut self) -> Result<isize> {
        let p: *mut libc::aiocb = &mut self.aiocb;
        self.in_progress = false;
        Errno::result(unsafe { libc::aio_return(p) })
    }

    /// Asynchronously writes from a buffer to a file descriptor
    pub fn write(&mut self) -> Result<()> {
        let p: *mut libc::aiocb = &mut self.aiocb;
        self.in_progress = true;
        Errno::result(unsafe { libc::aio_write(p) }).map(drop)
    }

}

/// Cancels outstanding AIO requests.  All requests for `fd` will be cancelled.
pub fn aio_cancel_all(fd: RawFd) -> Result<AioCancelStat> {
    match unsafe { libc::aio_cancel(fd, null_mut()) } {
        libc::AIO_CANCELED => Ok(AioCancelStat::AioCanceled),
        libc::AIO_NOTCANCELED => Ok(AioCancelStat::AioNotCanceled),
        libc::AIO_ALLDONE => Ok(AioCancelStat::AioAllDone),
        -1 => Err(Error::last()),
        _ => panic!("unknown aio_cancel return value")
    }
}

/// Suspends the calling process until at least one of the specified `AioCb`s
/// has completed, a signal is delivered, or the timeout has passed.  If
/// `timeout` is `None`, `aio_suspend` will block indefinitely.
pub fn aio_suspend(list: &[&AioCb], timeout: Option<TimeSpec>) -> Result<()> {
    // We must use transmute because Rust doesn't understand that a pointer to a
    // Struct is the same as a pointer to its first element.
    let plist = unsafe {
        mem::transmute::<&[&AioCb], *const [*const libc::aiocb]>(list)
    };
    let p = plist as *const *const libc::aiocb;
    let timep = match timeout {
        None    => null::<libc::timespec>(),
        Some(x) => x.as_ref() as *const libc::timespec
    };
    Errno::result(unsafe {
        libc::aio_suspend(p, list.len() as i32, timep)
    }).map(drop)
}

/// Submits multiple asynchronous I/O requests with a single system call.  The
/// order in which the requests are carried out is not specified.
#[cfg(not(any(target_os = "ios", target_os = "macos")))]
pub fn lio_listio(mode: LioMode, list: &[&mut AioCb],
                  sigev_notify: SigevNotify) -> Result<()> {
    let sigev = SigEvent::new(sigev_notify);
    let sigevp = &mut sigev.sigevent() as *mut libc::sigevent;
    // We must use transmute because Rust doesn't understand that a pointer to a
    // Struct is the same as a pointer to its first element.
    let plist = unsafe {
        mem::transmute::<&[&mut AioCb], *const [*mut libc::aiocb]>(list)
    };
    let p = plist as *const *mut libc::aiocb;
    Errno::result(unsafe {
        libc::lio_listio(mode as i32, p, list.len() as i32, sigevp)
    }).map(drop)
}

impl<'a> Drop for AioCb<'a> {
    /// If the `AioCb` has no remaining state in the kernel, just drop it.
    /// Otherwise, collect its error and return values, so as not to leak
    /// resources.
    fn drop(&mut self) {
        if self.in_progress {
            // Well-written programs should never get here.  They should always
            // wait for an AioCb to complete before dropping it
            let _ = write!(stderr(), "WARNING: dropped an in-progress AioCb");
            loop {
                let ret = aio_suspend(&[&self], None);
                match ret {
                    Ok(()) => break,
                    Err(Error::Sys(Errno::EINVAL)) => panic!(
                        "Inconsistent AioCb.in_progress value"),
                    Err(Error::Sys(Errno::EINTR)) => (),    // Retry interrupted syscall
                    _ => panic!("Unexpected aio_suspend return value {:?}", ret)
                };
            }
            let _ = self.aio_return();
        }
    }
}
<|endoftext|>


<commit_before>use gfx;
use image;
use image::{GenericImage, ImageBuf, Pixel, Rgba};

fn load_rgba8(path: &Path) -> Result<ImageBuf<Rgba<u8>>, String> {
    match image::open(path) {
        Ok(image::ImageRgba8(img)) => Ok(img),
        Ok(image::ImageRgb8(img)) => {
            let (w, h) = img.dimensions();
            Ok(ImageBuf::from_fn(w, h, |x, y| img.get_pixel(x, y).to_rgba()))
        }
        Ok(img) => return Err(format!("Unsupported color type {} in '{}'",
                                      img.color(), path.display())),
        Err(e)  => return Err(format!("Could not load '{}': {}", path.display(), e))
    }
}

pub struct Texture {
    pub tex: gfx::TextureHandle,
    pub width: u32,
    pub height: u32
}

impl Texture {
    /// Loads image by relative file name to the asset root.
    pub fn from_path<D: gfx::Device>(path: &Path, d: &mut D) -> Result<Texture, String> {
        Ok(Texture::from_rgba8(try!(load_rgba8(path)), d))
    }

    pub fn from_rgba8<D: gfx::Device>(img: ImageBuf<Rgba<u8>>, d: &mut D) -> Texture {
        let (width, height) = img.dimensions();

        let mut ti = gfx::tex::TextureInfo::new();
        ti.width = width as u16;
        ti.height = height as u16;
        ti.kind = gfx::tex::Texture2D;
        ti.format = gfx::tex::RGBA8;

        let tex = d.create_texture(ti).unwrap();
        d.update_texture(&tex, &ti.to_image_info(), &img.into_vec()).unwrap();
        d.generate_mipmap(&tex);

        Texture {
            tex: tex,
            width: width,
            height: height
        }
    }
}

pub struct ColorMap {
    image: ImageBuf<Rgba<u8>>
}

impl ColorMap {
    pub fn from_path(path: &Path) -> Result<ColorMap, String> {
        let img = try!(load_rgba8(path));

        match img.dimensions() {
            (256, 256) => Ok(ColorMap {image: img}),
            (w, h) => Err(format!("ColorMap expected 256x256, found {}x{} in '{}'",
                                  w, h, path.display()))
        }
    }

    pub fn get(&self, x: f32, y: f32) -> [u8, ..3] {
        // Clamp to [0.0, 1.0].
        let x = x.max(0.0).min(1.0);
        let y = y.max(0.0).min(1.0);

        // Scale y from [0.0, 1.0] to [0.0, x], forming a triangle.
        let y = x * y;

        // Origin is in the bottom-right corner.
        let x = ((1.0 - x) * 255.0) as u8;
        let y = ((1.0 - y) * 255.0) as u8;

        let (r, g, b, _) = self.image.get_pixel(x as u32, y as u32).channels();
        [r, g, b]
    }
}
<commit_msg>texture: add a progressive AtlasBuilder for fixed-size textures.<commit_after>use gfx;
use image;
use image::{GenericImage, ImageBuf, MutableRefImage, Pixel, Rgba, SubImage};
use std::collections::HashMap;
use std::mem;

fn load_rgba8(path: &Path) -> Result<ImageBuf<Rgba<u8>>, String> {
    match image::open(path) {
        Ok(image::ImageRgba8(img)) => Ok(img),
        Ok(image::ImageRgb8(img)) => {
            let (w, h) = img.dimensions();
            Ok(ImageBuf::from_fn(w, h, |x, y| img.get_pixel(x, y).to_rgba()))
        }
        Ok(img) => return Err(format!("Unsupported color type {} in '{}'",
                                      img.color(), path.display())),
        Err(e)  => return Err(format!("Could not load '{}': {}", path.display(), e))
    }
}

pub struct Texture {
    pub tex: gfx::TextureHandle,
    pub width: u32,
    pub height: u32
}

impl Texture {
    /// Loads image by relative file name to the asset root.
    pub fn from_path<D: gfx::Device>(path: &Path, d: &mut D) -> Result<Texture, String> {
        Ok(Texture::from_rgba8(try!(load_rgba8(path)), d))
    }

    pub fn from_rgba8<D: gfx::Device>(img: ImageBuf<Rgba<u8>>, d: &mut D) -> Texture {
        let (width, height) = img.dimensions();

        let mut ti = gfx::tex::TextureInfo::new();
        ti.width = width as u16;
        ti.height = height as u16;
        ti.kind = gfx::tex::Texture2D;
        ti.format = gfx::tex::RGBA8;

        let tex = d.create_texture(ti).unwrap();
        d.update_texture(&tex, &ti.to_image_info(), &img.into_vec()).unwrap();
        d.generate_mipmap(&tex);

        Texture {
            tex: tex,
            width: width,
            height: height
        }
    }
}

pub struct ColorMap {
    image: ImageBuf<Rgba<u8>>
}

impl ColorMap {
    pub fn from_path(path: &Path) -> Result<ColorMap, String> {
        let img = try!(load_rgba8(path));

        match img.dimensions() {
            (256, 256) => Ok(ColorMap {image: img}),
            (w, h) => Err(format!("ColorMap expected 256x256, found {}x{} in '{}'",
                                  w, h, path.display()))
        }
    }

    pub fn get(&self, x: f32, y: f32) -> [u8, ..3] {
        // Clamp to [0.0, 1.0].
        let x = x.max(0.0).min(1.0);
        let y = y.max(0.0).min(1.0);

        // Scale y from [0.0, 1.0] to [0.0, x], forming a triangle.
        let y = x * y;

        // Origin is in the bottom-right corner.
        let x = ((1.0 - x) * 255.0) as u8;
        let y = ((1.0 - y) * 255.0) as u8;

        let (r, g, b, _) = self.image.get_pixel(x as u32, y as u32).channels();
        [r, g, b]
    }
}

pub struct AtlasBuilder {
    image: ImageBuf<Rgba<u8>>,
    // Base path for loading tiles.
    path: Path,
    // Size of an individual tile.
    unit_width: u32,
    unit_height: u32,
    // Size of the entirely occupied square, in tiles.
    completed_tiles_size: u32,
    // Position in the current strip.
    position: u32,
    // Position cache for loaded tiles (in pixels).
    tile_positions: HashMap<String, (u32, u32)>,
    // Opacity cache for rectangles in the atlas.
    opacity_cache: HashMap<(u32, u32, u32, u32), bool>
}

impl AtlasBuilder {
    pub fn new(path: Path, unit_width: u32, unit_height: u32) -> AtlasBuilder {
        AtlasBuilder {
            image: ImageBuf::new(unit_width * 4, unit_height * 4),
            path: path,
            unit_width: unit_width,
            unit_height: unit_height,
            completed_tiles_size: 0,
            position: 0,
            tile_positions: HashMap::new(),
            opacity_cache: HashMap::new()
        }
    }

    pub fn load(&mut self, name: &str) -> (u32, u32) {
        match self.tile_positions.find_equiv(&name) {
            Some(pos) => return *pos,
            None => {}
        }

        let mut path = self.path.join(name);
        path.set_extension("png");
        let img = load_rgba8(&path).unwrap();

        let (iw, ih) = img.dimensions();
        assert!(iw == self.unit_width);
        assert!((ih % self.unit_height) == 0);
        if ih > self.unit_height {
            println!("ignoring {} extra frames in '{}'", (ih / self.unit_height) - 1, name);
        }

        let (uw, uh) = (self.unit_width, self.unit_height);
        let (w, h) = self.image.dimensions();
        let size = self.completed_tiles_size;

        // Expand the image buffer if necessary.
        if self.position == 0 && (uw * size >= w || uh * size >= h) {
            let old = mem::replace(&mut self.image, ImageBuf::new(w * 2, h * 2));
            let mut dest = SubImage::new(&mut self.image, 0, 0, w, h);
            for ((_, _, a), (_, _, b)) in dest.mut_pixels().zip(old.pixels()) {
                *a = b;
            }
        }

        let (x, y) = if self.position < size {
            (self.position, size)
        } else {
            (size, self.position - size)
        };

        self.position += 1;
        if self.position >= size * 2 + 1 {
            self.position = 0;
            self.completed_tiles_size += 1;
        }

        let mut dest = SubImage::new(&mut self.image, x * uw, y * uh, uw, uh);
        for ((_, _, a), (_, _, b)) in dest.mut_pixels().zip(img.pixels()) {
            *a = b;
        }

        *self.tile_positions.find_or_insert(name.to_string(), (x * uw, y * uh))
    }

    pub fn is_opaque(&mut self, x: u32, y: u32, w: u32, h: u32) -> bool {
        match self.opacity_cache.find(&(x, y, w, h)) {
            Some(opaque) => return *opaque,
            None => {}
        }

        let tile = SubImage::new(&mut self.image, x, y, w, h);
        let opaque = !tile.pixels().any(|(_, _, p)| p.alpha() < 255);
        self.opacity_cache.insert((x, y, w, h), opaque);
        opaque
    }

    pub fn complete<D: gfx::Device>(self, d: &mut D) -> Texture {
        Texture::from_rgba8(self.image, d)
    }
}
<|endoftext|>


<commit_before>use utils::bigint::{M256, U256, U512};
use utils::gas::Gas;
use utils::address::Address;
use vm::{Machine, Memory, Stack, PC, Error, Result};
use vm::opcode::Opcode;
use vm::machine::MachineState;
use transaction::Transaction;
use blockchain::Block;
use std::cmp::{min, max};

const G_ZERO: usize = 0;
const G_BASE: usize = 2;
const G_VERYLOW: usize = 3;
const G_LOW: usize = 5;
const G_MID: usize = 8;
const G_HIGH: usize = 10;
const G_EXTCODE_DEFAULT: usize = 20;
const G_EXTCODE_EIP150: usize = 700;
const G_BALANCE_DEFAULT: usize = 20;
const G_BALANCE_EIP150: usize = 400;
const G_SLOAD_DEFAULT: usize = 50;
const G_SLOAD_EIP150: usize = 200;
const G_JUMPDEST: usize = 1;
const G_SSET: usize = 20000;
const G_SRESET: usize = 5000;
const R_SCLEAR: usize = 15000;
const R_SUICIDE: usize = 24000;
const G_SUICIDE_DEFAULT: usize = 0;
const G_SUICIDE_EIP150: usize = 5000;
const G_CREATE: usize = 32000;
const G_CODEDEPOSITE: usize = 200;
const G_CALL_DEFAULT: usize = 20;
const G_CALL_EIP150: usize = 700;
const G_CALLVALUE: usize = 9000;
const G_CALLSTIPEND: usize = 2300;
const G_NEWACCOUNT: usize = 25000;
const G_EXP: usize = 10;
const G_EXPBYTE_DEFAULT: usize = 10;
const G_EXPBYTE_EIP160: usize = 50;
const G_MEMORY: usize = 3;
const G_TXCREATE: usize = 32000;
const G_TXDATAZERO: usize = 4;
const G_TXDATANONZERO: usize = 68;
const G_TRANSACTION: usize = 21000;
const G_LOG: usize = 375;
const G_LOGDATA: usize = 8;
const G_LOGTOPIC: usize = 375;
const G_SHA3: usize = 30;
const G_SHA3WORD: usize = 6;
const G_COPY: usize = 3;
const G_BLOCKHASH: usize = 20;

fn memory_cost(a: Gas) -> Gas {
    (Gas::from(G_MEMORY) * a + a * a / Gas::from(512u64)).into()
}

fn sstore_cost<M: MachineState>(machine: &M) -> Result<Gas> {
    let index = machine.stack().peek(0)?;
    let value = machine.stack().peek(1)?;
    let address = machine.transaction().callee();

    if value != M256::zero() && machine.block().account_storage(address, index) == M256::zero() {
        Ok(G_SSET.into())
    } else {
        Ok(G_SRESET.into())
    }
}

fn call_cost<M: MachineState>(machine: &M, available_gas: Gas) -> Result<Gas> {
    Ok(gascap_cost(machine, available_gas)? + extra_cost(machine)?)
}

fn callgas_cost<M: MachineState>(machine: &M, available_gas: Gas) -> Result<Gas> {
    let val = machine.stack().peek(2)?;
    if val != M256::zero() {
        Ok(gascap_cost(machine, available_gas)? + G_CALLSTIPEND.into())
    } else {
        Ok(gascap_cost(machine, available_gas)?)
    }
}

fn gascap_cost<M: MachineState>(machine: &M, available_gas: Gas) -> Result<Gas> {
    let base2 = machine.stack().peek(0)?.into();

    if available_gas >= extra_cost(machine)? {
        let base1 = available_gas - extra_cost(machine)?;
        Ok(min(base1 - base1 / Gas::from(64u64), base2))
    } else {
        Ok(base2)
    }
}

fn extra_cost<M: MachineState>(machine: &M) -> Result<Gas> {
    Ok(Gas::from(if machine.eip150() { G_CALL_EIP150 } else { G_CALL_DEFAULT }) + xfer_cost(machine)? + new_cost(machine)?)
}

fn xfer_cost<M: MachineState>(machine: &M) -> Result<Gas> {
    let val = machine.stack().peek(2)?;
    if val != M256::zero() {
        Ok(G_CALLVALUE.into())
    } else {
        Ok(Gas::zero())
    }
}

fn new_cost<M: MachineState>(machine: &M) -> Result<Gas> {
    let address: Address = machine.stack().peek(1)?.into();
    if address == Address::default() {
        Ok(G_NEWACCOUNT.into())
    } else {
        Ok(Gas::zero())
    }
}

fn suicide_cost<M: MachineState>(machine: &M) -> Result<Gas> {
    let address: Address = machine.stack().peek(1)?.into();
    Ok(Gas::from(if machine.eip150() { G_SUICIDE_EIP150 } else { G_SUICIDE_DEFAULT }) + if address == Address::default() {
        Gas::from(G_NEWACCOUNT)
    } else {
        Gas::zero()
    })
}

#[derive(Clone, Copy, Debug)]
pub struct CostAggregrator(Gas);

impl CostAggregrator {
    pub fn active_memory_len(&self) -> M256 {
        self.0.into()
    }
}

impl Default for CostAggregrator {
    fn default() -> CostAggregrator {
        CostAggregrator(Gas::zero())
    }
}

fn memory_expand(current: Gas, from: Gas, len: Gas) -> Gas {
    if len == Gas::zero() {
        return current;
    }

    let rem = (from + len) % Gas::from(32u64);
    let new = if rem == Gas::zero() {
        (from + len) / Gas::from(32u64)
    } else {
        (from + len) / Gas::from(32u64) + Gas::from(1u64)
    };
    max(current, new)
}

fn memory_gas_cost<M: MachineState>(opcode: Opcode, machine: &M, aggregrator: CostAggregrator)
                               -> Result<(Gas, CostAggregrator)> {
    let ref stack = machine.stack();
    let ref memory = machine.memory();

    let current = aggregrator.0;
    let next = match opcode {
        Opcode::SHA3 | Opcode::CODECOPY | Opcode::RETURN => {
            let from: U256 = stack.peek(0)?.into();
            let len: U256 = stack.peek(1)?.into();
            memory_expand(current, Gas::from(from), Gas::from(len))
        },
        Opcode::MLOAD | Opcode::MSTORE => {
            let from: U256 = stack.peek(0)?.into();
            memory_expand(current, Gas::from(from), Gas::from(32u64))
        },
        Opcode::MSTORE8 => {
            let from: U256 = stack.peek(0)?.into();
            memory_expand(current, Gas::from(from), Gas::from(1u64))
        },
        Opcode::CREATE => {
            let from: U256 = stack.peek(1)?.into();
            let len: U256 = stack.peek(2)?.into();
            memory_expand(current, Gas::from(from), Gas::from(len))
        },
        _ => {
            current
        }
    };
    Ok((memory_cost(next) - memory_cost(current), CostAggregrator(next)))
}

pub fn gas_cost<M: MachineState>(opcode: Opcode, machine: &M, available_gas: Gas, aggregrator: CostAggregrator)
                            -> Result<(Gas, CostAggregrator)> {
    let ref stack = machine.stack();
    let ref memory = machine.memory();

    let self_cost: Gas = match opcode {
        Opcode::CALL | Opcode::CALLCODE |
        Opcode::DELEGATECALL => call_cost(machine, available_gas)?,
        Opcode::SUICIDE => suicide_cost(machine)?,
        Opcode::SSTORE => sstore_cost(machine)?,

        Opcode::SHA3 => {
            let len = stack.peek(1)?;
            let wordd = Gas::from(len) / Gas::from(32u64);
            let wordr = Gas::from(len) % Gas::from(32u64);
            (Gas::from(G_SHA3) + Gas::from(G_SHA3WORD) * if wordr == Gas::zero() { wordd } else { wordd + Gas::from(1u64) }).into()
        },

        Opcode::LOG(v) => {
            let len = stack.peek(1)?;
            (Gas::from(G_LOG) + Gas::from(G_LOGDATA) * Gas::from(len) + Gas::from(G_LOGTOPIC) * Gas::from(v)).into()
        },

        Opcode::EXTCODECOPY => {
            let len = stack.peek(2)?;
            (Gas::from(if machine.eip150() { G_EXTCODE_EIP150 } else { G_EXTCODE_DEFAULT }) + Gas::from(G_COPY) * (Gas::from(len) / Gas::from(32u64))).into()
        },

        Opcode::CALLDATACOPY | Opcode::CODECOPY => {
            let len = stack.peek(2)?;
            (Gas::from(G_VERYLOW) + Gas::from(G_COPY) * (Gas::from(len) / Gas::from(32u64))).into()
        },

        Opcode::EXP => {
            if stack.peek(1)? == M256::zero() {
                Gas::from(G_EXP)
            } else {
                Gas::from(G_EXP) + Gas::from(if machine.eip160() { G_EXPBYTE_EIP160 } else { G_EXPBYTE_DEFAULT }) * (Gas::from(1u64) + Gas::from(stack.peek(1)?.log2floor()) / Gas::from(8u64))
            }
        }

        Opcode::CREATE => G_CREATE.into(),
        Opcode::JUMPDEST => G_JUMPDEST.into(),
        Opcode::SLOAD => (if machine.eip150() { G_SLOAD_EIP150 } else { G_SLOAD_DEFAULT }).into(),

        // W_zero
        Opcode::STOP | Opcode::RETURN
            => G_ZERO.into(),

        // W_base
        Opcode::ADDRESS | Opcode::ORIGIN | Opcode::CALLER |
        Opcode::CALLVALUE | Opcode::CALLDATASIZE |
        Opcode::CODESIZE | Opcode::GASPRICE | Opcode::COINBASE |
        Opcode::TIMESTAMP | Opcode::NUMBER | Opcode::DIFFICULTY |
        Opcode::GASLIMIT | Opcode::POP | Opcode::PC |
        Opcode::MSIZE | Opcode::GAS
            => G_BASE.into(),

        // W_verylow
        Opcode::ADD | Opcode::SUB | Opcode::NOT | Opcode::LT |
        Opcode::GT | Opcode::SLT | Opcode::SGT | Opcode::EQ |
        Opcode::ISZERO | Opcode::AND | Opcode::OR | Opcode::XOR |
        Opcode::BYTE | Opcode::CALLDATALOAD | Opcode::MLOAD |
        Opcode::MSTORE | Opcode::MSTORE8 | Opcode::PUSH(_) |
        Opcode::DUP(_) | Opcode::SWAP(_)
            => G_VERYLOW.into(),

        // W_low
        Opcode::MUL | Opcode::DIV | Opcode::SDIV | Opcode::MOD |
        Opcode::SMOD | Opcode::SIGNEXTEND
            => G_LOW.into(),

        // W_mid
        Opcode::ADDMOD | Opcode::MULMOD | Opcode::JUMP
            => G_MID.into(),

        // W_high
        Opcode::JUMPI => G_HIGH.into(),

        // W_extcode
        Opcode::EXTCODESIZE => (if machine.eip150() { G_EXTCODE_EIP150 } else { G_EXTCODE_DEFAULT }).into(),

        Opcode::BALANCE => (if machine.eip150() { G_BALANCE_EIP150 } else { G_BALANCE_DEFAULT }).into(),
        Opcode::BLOCKHASH => G_BLOCKHASH.into(),
        Opcode::INVALID => Gas::zero(),
    };
    let (memory_gas, agg) = memory_gas_cost(opcode, machine, aggregrator)?;
    Ok((self_cost + memory_gas, agg))
}

pub fn gas_refund<M: MachineState>(opcode: Opcode, machine: &M) -> Result<Gas> {
    match opcode {
        Opcode::SSTORE => {
            let index = machine.stack().peek(0)?;
            let value = machine.stack().peek(1)?;
            let address = machine.transaction().callee();

            if value == M256::zero() && machine.block().account_storage(address, index) != M256::zero() {
                Ok(Gas::from(R_SCLEAR))
            } else {
                Ok(Gas::zero())
            }
        },
        Opcode::SUICIDE => {
            // TODO: check whether I_a belongs to A_s
            Ok(Gas::zero())
        },
        _ => Ok(Gas::zero())
    }
}
<commit_msg>Problem: *CODE/DATACOPY cost is ceil rather than floor Solution: fix it<commit_after>use utils::bigint::{M256, U256, U512};
use utils::gas::Gas;
use utils::address::Address;
use vm::{Machine, Memory, Stack, PC, Error, Result};
use vm::opcode::Opcode;
use vm::machine::MachineState;
use transaction::Transaction;
use blockchain::Block;
use std::cmp::{min, max};

const G_ZERO: usize = 0;
const G_BASE: usize = 2;
const G_VERYLOW: usize = 3;
const G_LOW: usize = 5;
const G_MID: usize = 8;
const G_HIGH: usize = 10;
const G_EXTCODE_DEFAULT: usize = 20;
const G_EXTCODE_EIP150: usize = 700;
const G_BALANCE_DEFAULT: usize = 20;
const G_BALANCE_EIP150: usize = 400;
const G_SLOAD_DEFAULT: usize = 50;
const G_SLOAD_EIP150: usize = 200;
const G_JUMPDEST: usize = 1;
const G_SSET: usize = 20000;
const G_SRESET: usize = 5000;
const R_SCLEAR: usize = 15000;
const R_SUICIDE: usize = 24000;
const G_SUICIDE_DEFAULT: usize = 0;
const G_SUICIDE_EIP150: usize = 5000;
const G_CREATE: usize = 32000;
const G_CODEDEPOSITE: usize = 200;
const G_CALL_DEFAULT: usize = 20;
const G_CALL_EIP150: usize = 700;
const G_CALLVALUE: usize = 9000;
const G_CALLSTIPEND: usize = 2300;
const G_NEWACCOUNT: usize = 25000;
const G_EXP: usize = 10;
const G_EXPBYTE_DEFAULT: usize = 10;
const G_EXPBYTE_EIP160: usize = 50;
const G_MEMORY: usize = 3;
const G_TXCREATE: usize = 32000;
const G_TXDATAZERO: usize = 4;
const G_TXDATANONZERO: usize = 68;
const G_TRANSACTION: usize = 21000;
const G_LOG: usize = 375;
const G_LOGDATA: usize = 8;
const G_LOGTOPIC: usize = 375;
const G_SHA3: usize = 30;
const G_SHA3WORD: usize = 6;
const G_COPY: usize = 3;
const G_BLOCKHASH: usize = 20;

fn memory_cost(a: Gas) -> Gas {
    (Gas::from(G_MEMORY) * a + a * a / Gas::from(512u64)).into()
}

fn sstore_cost<M: MachineState>(machine: &M) -> Result<Gas> {
    let index = machine.stack().peek(0)?;
    let value = machine.stack().peek(1)?;
    let address = machine.transaction().callee();

    if value != M256::zero() && machine.block().account_storage(address, index) == M256::zero() {
        Ok(G_SSET.into())
    } else {
        Ok(G_SRESET.into())
    }
}

fn call_cost<M: MachineState>(machine: &M, available_gas: Gas) -> Result<Gas> {
    Ok(gascap_cost(machine, available_gas)? + extra_cost(machine)?)
}

fn callgas_cost<M: MachineState>(machine: &M, available_gas: Gas) -> Result<Gas> {
    let val = machine.stack().peek(2)?;
    if val != M256::zero() {
        Ok(gascap_cost(machine, available_gas)? + G_CALLSTIPEND.into())
    } else {
        Ok(gascap_cost(machine, available_gas)?)
    }
}

fn gascap_cost<M: MachineState>(machine: &M, available_gas: Gas) -> Result<Gas> {
    let base2 = machine.stack().peek(0)?.into();

    if available_gas >= extra_cost(machine)? {
        let base1 = available_gas - extra_cost(machine)?;
        Ok(min(base1 - base1 / Gas::from(64u64), base2))
    } else {
        Ok(base2)
    }
}

fn extra_cost<M: MachineState>(machine: &M) -> Result<Gas> {
    Ok(Gas::from(if machine.eip150() { G_CALL_EIP150 } else { G_CALL_DEFAULT }) + xfer_cost(machine)? + new_cost(machine)?)
}

fn xfer_cost<M: MachineState>(machine: &M) -> Result<Gas> {
    let val = machine.stack().peek(2)?;
    if val != M256::zero() {
        Ok(G_CALLVALUE.into())
    } else {
        Ok(Gas::zero())
    }
}

fn new_cost<M: MachineState>(machine: &M) -> Result<Gas> {
    let address: Address = machine.stack().peek(1)?.into();
    if address == Address::default() {
        Ok(G_NEWACCOUNT.into())
    } else {
        Ok(Gas::zero())
    }
}

fn suicide_cost<M: MachineState>(machine: &M) -> Result<Gas> {
    let address: Address = machine.stack().peek(1)?.into();
    Ok(Gas::from(if machine.eip150() { G_SUICIDE_EIP150 } else { G_SUICIDE_DEFAULT }) + if address == Address::default() {
        Gas::from(G_NEWACCOUNT)
    } else {
        Gas::zero()
    })
}

#[derive(Clone, Copy, Debug)]
pub struct CostAggregrator(Gas);

impl CostAggregrator {
    pub fn active_memory_len(&self) -> M256 {
        self.0.into()
    }
}

impl Default for CostAggregrator {
    fn default() -> CostAggregrator {
        CostAggregrator(Gas::zero())
    }
}

fn memory_expand(current: Gas, from: Gas, len: Gas) -> Gas {
    if len == Gas::zero() {
        return current;
    }

    let rem = (from + len) % Gas::from(32u64);
    let new = if rem == Gas::zero() {
        (from + len) / Gas::from(32u64)
    } else {
        (from + len) / Gas::from(32u64) + Gas::from(1u64)
    };
    max(current, new)
}

fn memory_gas_cost<M: MachineState>(opcode: Opcode, machine: &M, aggregrator: CostAggregrator)
                               -> Result<(Gas, CostAggregrator)> {
    let ref stack = machine.stack();
    let ref memory = machine.memory();

    let current = aggregrator.0;
    let next = match opcode {
        Opcode::SHA3 | Opcode::CODECOPY | Opcode::RETURN => {
            let from: U256 = stack.peek(0)?.into();
            let len: U256 = stack.peek(1)?.into();
            memory_expand(current, Gas::from(from), Gas::from(len))
        },
        Opcode::MLOAD | Opcode::MSTORE => {
            let from: U256 = stack.peek(0)?.into();
            memory_expand(current, Gas::from(from), Gas::from(32u64))
        },
        Opcode::MSTORE8 => {
            let from: U256 = stack.peek(0)?.into();
            memory_expand(current, Gas::from(from), Gas::from(1u64))
        },
        Opcode::CREATE => {
            let from: U256 = stack.peek(1)?.into();
            let len: U256 = stack.peek(2)?.into();
            memory_expand(current, Gas::from(from), Gas::from(len))
        },
        _ => {
            current
        }
    };
    Ok((memory_cost(next) - memory_cost(current), CostAggregrator(next)))
}

pub fn gas_cost<M: MachineState>(opcode: Opcode, machine: &M, available_gas: Gas, aggregrator: CostAggregrator)
                            -> Result<(Gas, CostAggregrator)> {
    let ref stack = machine.stack();
    let ref memory = machine.memory();

    let self_cost: Gas = match opcode {
        Opcode::CALL | Opcode::CALLCODE |
        Opcode::DELEGATECALL => call_cost(machine, available_gas)?,
        Opcode::SUICIDE => suicide_cost(machine)?,
        Opcode::SSTORE => sstore_cost(machine)?,

        Opcode::SHA3 => {
            let len = stack.peek(1)?;
            let wordd = Gas::from(len) / Gas::from(32u64);
            let wordr = Gas::from(len) % Gas::from(32u64);
            (Gas::from(G_SHA3) + Gas::from(G_SHA3WORD) * if wordr == Gas::zero() { wordd } else { wordd + Gas::from(1u64) }).into()
        },

        Opcode::LOG(v) => {
            let len = stack.peek(1)?;
            (Gas::from(G_LOG) + Gas::from(G_LOGDATA) * Gas::from(len) + Gas::from(G_LOGTOPIC) * Gas::from(v)).into()
        },

        Opcode::EXTCODECOPY => {
            let len = stack.peek(2)?;
            let wordd = Gas::from(len) / Gas::from(32u64);
            let wordr = Gas::from(len) % Gas::from(32u64);
            (Gas::from(if machine.eip150() { G_EXTCODE_EIP150 } else { G_EXTCODE_DEFAULT }) + Gas::from(G_COPY) * if wordr == Gas::zero() { wordd } else { wordd + Gas::from(1u64) }).into()
        },

        Opcode::CALLDATACOPY | Opcode::CODECOPY => {
            let len = stack.peek(2)?;
            let wordd = Gas::from(len) / Gas::from(32u64);
            let wordr = Gas::from(len) % Gas::from(32u64);
            (Gas::from(G_VERYLOW) + Gas::from(G_COPY) * if wordr == Gas::zero() { wordd } else { wordd + Gas::from(1u64) }).into()
        },

        Opcode::EXP => {
            if stack.peek(1)? == M256::zero() {
                Gas::from(G_EXP)
            } else {
                Gas::from(G_EXP) + Gas::from(if machine.eip160() { G_EXPBYTE_EIP160 } else { G_EXPBYTE_DEFAULT }) * (Gas::from(1u64) + Gas::from(stack.peek(1)?.log2floor()) / Gas::from(8u64))
            }
        }

        Opcode::CREATE => G_CREATE.into(),
        Opcode::JUMPDEST => G_JUMPDEST.into(),
        Opcode::SLOAD => (if machine.eip150() { G_SLOAD_EIP150 } else { G_SLOAD_DEFAULT }).into(),

        // W_zero
        Opcode::STOP | Opcode::RETURN
            => G_ZERO.into(),

        // W_base
        Opcode::ADDRESS | Opcode::ORIGIN | Opcode::CALLER |
        Opcode::CALLVALUE | Opcode::CALLDATASIZE |
        Opcode::CODESIZE | Opcode::GASPRICE | Opcode::COINBASE |
        Opcode::TIMESTAMP | Opcode::NUMBER | Opcode::DIFFICULTY |
        Opcode::GASLIMIT | Opcode::POP | Opcode::PC |
        Opcode::MSIZE | Opcode::GAS
            => G_BASE.into(),

        // W_verylow
        Opcode::ADD | Opcode::SUB | Opcode::NOT | Opcode::LT |
        Opcode::GT | Opcode::SLT | Opcode::SGT | Opcode::EQ |
        Opcode::ISZERO | Opcode::AND | Opcode::OR | Opcode::XOR |
        Opcode::BYTE | Opcode::CALLDATALOAD | Opcode::MLOAD |
        Opcode::MSTORE | Opcode::MSTORE8 | Opcode::PUSH(_) |
        Opcode::DUP(_) | Opcode::SWAP(_)
            => G_VERYLOW.into(),

        // W_low
        Opcode::MUL | Opcode::DIV | Opcode::SDIV | Opcode::MOD |
        Opcode::SMOD | Opcode::SIGNEXTEND
            => G_LOW.into(),

        // W_mid
        Opcode::ADDMOD | Opcode::MULMOD | Opcode::JUMP
            => G_MID.into(),

        // W_high
        Opcode::JUMPI => G_HIGH.into(),

        // W_extcode
        Opcode::EXTCODESIZE => (if machine.eip150() { G_EXTCODE_EIP150 } else { G_EXTCODE_DEFAULT }).into(),

        Opcode::BALANCE => (if machine.eip150() { G_BALANCE_EIP150 } else { G_BALANCE_DEFAULT }).into(),
        Opcode::BLOCKHASH => G_BLOCKHASH.into(),
        Opcode::INVALID => Gas::zero(),
    };
    let (memory_gas, agg) = memory_gas_cost(opcode, machine, aggregrator)?;
    Ok((self_cost + memory_gas, agg))
}

pub fn gas_refund<M: MachineState>(opcode: Opcode, machine: &M) -> Result<Gas> {
    match opcode {
        Opcode::SSTORE => {
            let index = machine.stack().peek(0)?;
            let value = machine.stack().peek(1)?;
            let address = machine.transaction().callee();

            if value == M256::zero() && machine.block().account_storage(address, index) != M256::zero() {
                Ok(Gas::from(R_SCLEAR))
            } else {
                Ok(Gas::zero())
            }
        },
        Opcode::SUICIDE => {
            // TODO: check whether I_a belongs to A_s
            Ok(Gas::zero())
        },
        _ => Ok(Gas::zero())
    }
}
<|endoftext|>


<commit_before>//! common functions for all parsers

/// Interface of all Rusticata parsers.
///
/// A object implementing the RParser trait is an instance of a parser,
/// including the state (and all associated variables).
pub trait RParser {
    // XXX static functions seem to cause problems with hashmaps
    // fn probe(&[u8]) -> bool;

    /// Parsing function
    ///
    /// This function is called for every packet of a connection.
    ///
    /// Arguments:
    ///
    /// - `self`: the state (parser instance)
    /// - a slice on the packet data
    /// - the direction of this packet (0: to server, 1: to client)
    ///
    /// Return value:
    ///
    /// `R_STATUS_OK` or `R_STATUS_FAIL`, possibly or'ed with
    /// `R_STATUS_EVENTS` if parsing events were raised.
    fn parse(&mut self, &[u8], u8) -> u32;
}

// status: return code, events

pub static R_STATUS_EVENTS : u32  = 0x0100;

pub static R_STATUS_OK : u32      = 0x0000;
pub static R_STATUS_FAIL : u32    = 0x0001;

pub static R_STATUS_EV_MASK : u32 = 0x0f00;
pub static R_STATUS_MASK : u32    = 0x00ff;

#[macro_export]
macro_rules! r_status_is_ok {
    ($status:expr) => { ($status & $crate::R_STATUS_MASK) == $crate::R_STATUS_MASK }
}

#[macro_export]
macro_rules! r_status_has_events {
    ($status:expr) => { ($status & $crate::R_STATUS_EV_MASK) == $crate::R_STATUS_EVENTS }
}

// Constants
pub const STREAM_TOSERVER : u8 = 0;
pub const STREAM_TOCLIENT : u8 = 1;

// Helper macros
// We can't define them (can we ?) in the trait, partly because of the 'no_mangle' and 'extern'
// stuff.
// This forces to use macros in addition to the trait, but at least provides a proper way of
// encapsulating the translation of C variables to rust.

#[macro_export]
macro_rules! r_declare_state_new {
    ($f:ident, $ty:ident, $args:expr) => {
        #[no_mangle]
        pub extern "C" fn $f() -> *mut libc::c_char {
            let b = Box::new($ty::new($args));
            unsafe { mem::transmute(b) }
        }
    }
}

#[macro_export]
macro_rules! r_declare_state_free {
    ($f:ident, $ty:ident, $expr:expr) => {
        impl<'a> Drop for $ty<'a> {
            fn drop(&mut self) {
                $expr
            }
        }

        #[no_mangle]
        pub unsafe extern fn $f(ptr: *mut libc::c_char)
        {
            let b: Box<$ty> = mem::transmute(ptr);
            // reference will be dropped automatically, and allocated memory
            // will be freed
            // but let's do it explicitly
            drop(b);
        }
    }
}

#[macro_export]
macro_rules! r_implement_probe {
    ($f:ident, $g:ident) => {
        #[no_mangle]
        pub extern "C" fn $f(input: *const c_char, input_len: u32, _offset: *const c_char) -> u32 {
            let data_len = input_len as usize;
            let data : &[u8] = unsafe { std::slice::from_raw_parts(input as *mut u8, data_len) };
            let res = ::std::panic::catch_unwind(|| {
                match $g(data) {
                    true  => 1,
                    false => 0,
                }
            });
            match res {
                Ok(v)  => v,
                Err(_) => 0xffffffff,
            }
        }
    }
}

#[macro_export]
macro_rules! r_implement_parse {
    ($f:ident, $g:ident) => {
        #[no_mangle]
        pub extern "C" fn $f(direction: u8, input: *const c_char, input_len: u32, ptr: *mut $g) -> u32 {
            let data_len = input_len as usize;
            let data : &[u8] = unsafe { std::slice::from_raw_parts(input as *mut u8, data_len) };
            if ptr.is_null() { return 0xffffffff; };
            let res = ::std::panic::catch_unwind(|| {
                let parser = unsafe { &mut *ptr };
                parser.parse(data, direction)
            });
            match res {
                Ok(v)  => v,
                Err(_) => 0xffffffff,
            }
        }
    }
}

<commit_msg>Require RParser trait to be Send + Sync<commit_after>//! common functions for all parsers

/// Interface of all Rusticata parsers.
///
/// A object implementing the RParser trait is an instance of a parser,
/// including the state (and all associated variables).
pub trait RParser : Send + Sync {
    // XXX static functions seem to cause problems with hashmaps
    // fn probe(&[u8]) -> bool;

    /// Parsing function
    ///
    /// This function is called for every packet of a connection.
    ///
    /// Arguments:
    ///
    /// - `self`: the state (parser instance)
    /// - a slice on the packet data
    /// - the direction of this packet (0: to server, 1: to client)
    ///
    /// Return value:
    ///
    /// `R_STATUS_OK` or `R_STATUS_FAIL`, possibly or'ed with
    /// `R_STATUS_EVENTS` if parsing events were raised.
    fn parse(&mut self, &[u8], u8) -> u32;
}

// status: return code, events

pub static R_STATUS_EVENTS : u32  = 0x0100;

pub static R_STATUS_OK : u32      = 0x0000;
pub static R_STATUS_FAIL : u32    = 0x0001;

pub static R_STATUS_EV_MASK : u32 = 0x0f00;
pub static R_STATUS_MASK : u32    = 0x00ff;

#[macro_export]
macro_rules! r_status_is_ok {
    ($status:expr) => { ($status & $crate::R_STATUS_MASK) == $crate::R_STATUS_MASK }
}

#[macro_export]
macro_rules! r_status_has_events {
    ($status:expr) => { ($status & $crate::R_STATUS_EV_MASK) == $crate::R_STATUS_EVENTS }
}

// Constants
pub const STREAM_TOSERVER : u8 = 0;
pub const STREAM_TOCLIENT : u8 = 1;

// Helper macros
// We can't define them (can we ?) in the trait, partly because of the 'no_mangle' and 'extern'
// stuff.
// This forces to use macros in addition to the trait, but at least provides a proper way of
// encapsulating the translation of C variables to rust.

#[macro_export]
macro_rules! r_declare_state_new {
    ($f:ident, $ty:ident, $args:expr) => {
        #[no_mangle]
        pub extern "C" fn $f() -> *mut libc::c_char {
            let b = Box::new($ty::new($args));
            unsafe { mem::transmute(b) }
        }
    }
}

#[macro_export]
macro_rules! r_declare_state_free {
    ($f:ident, $ty:ident, $expr:expr) => {
        impl<'a> Drop for $ty<'a> {
            fn drop(&mut self) {
                $expr
            }
        }

        #[no_mangle]
        pub unsafe extern fn $f(ptr: *mut libc::c_char)
        {
            let b: Box<$ty> = mem::transmute(ptr);
            // reference will be dropped automatically, and allocated memory
            // will be freed
            // but let's do it explicitly
            drop(b);
        }
    }
}

#[macro_export]
macro_rules! r_implement_probe {
    ($f:ident, $g:ident) => {
        #[no_mangle]
        pub extern "C" fn $f(input: *const c_char, input_len: u32, _offset: *const c_char) -> u32 {
            let data_len = input_len as usize;
            let data : &[u8] = unsafe { std::slice::from_raw_parts(input as *mut u8, data_len) };
            let res = ::std::panic::catch_unwind(|| {
                match $g(data) {
                    true  => 1,
                    false => 0,
                }
            });
            match res {
                Ok(v)  => v,
                Err(_) => 0xffffffff,
            }
        }
    }
}

#[macro_export]
macro_rules! r_implement_parse {
    ($f:ident, $g:ident) => {
        #[no_mangle]
        pub extern "C" fn $f(direction: u8, input: *const c_char, input_len: u32, ptr: *mut $g) -> u32 {
            let data_len = input_len as usize;
            let data : &[u8] = unsafe { std::slice::from_raw_parts(input as *mut u8, data_len) };
            if ptr.is_null() { return 0xffffffff; };
            let res = ::std::panic::catch_unwind(|| {
                let parser = unsafe { &mut *ptr };
                parser.parse(data, direction)
            });
            match res {
                Ok(v)  => v,
                Err(_) => 0xffffffff,
            }
        }
    }
}

<|endoftext|>


<commit_before>#![feature(io)]

mod dict;
mod acc;
mod graph;
use dict::Dict;
use graph::Graph;

fn main() {
    let _dict = dict::Dict::load("tdict-std.txt").unwrap();
    let g = Graph::build("กามกา", &_dict);
    println!("@@@@ {:?}", g.to_str_vec());
}<commit_msg>read/write using stdin/stdout<commit_after>#![feature(io)]
#![feature(old_io)]

mod dict;
mod acc;
mod graph;
use dict::Dict;
use graph::Graph;
use std::old_io as io;

fn main() {
    let _dict = dict::Dict::load("tdict-std.txt").unwrap();
    let mut reader = io::stdin();
    let mut o = io::stdout();
    loop {
        let input = match reader.read_line() {
            Ok(line) => line,
            Err(_) => break
        };
        let g = Graph::build((&input[..]).trim_right_matches('\n'), &_dict);
        let segmented: &str = &(g.to_str_vec().connect("|"))[..];
        o.write_str(segmented);
        o.write_str("\n");
    }
} <|endoftext|>


<commit_before>/// SYSCALL invokes an OS system-call handler at privilege level 0.
/// It does so by loading RIP from the IA32_LSTAR
/// MSR (after saving the address of the instruction following SYSCALL into RCX).
/// "A.2 AMD64 Linux Kernel Conventions" of System V Application Binary Interface AMD64 Architecture Processor Supplement:
/// A) The kernel interface uses %rdi, %rsi, %rdx, %r10, %r8 and %r9.
/// B) A system-call is done via the syscall instruction. The kernel destroys registers %rcx and %r11.
/// C) The number of the syscall has to be passed in register %rax.
/// D) System-calls are limited to six arguments, no argument is passed directly on the stack.
/// E) Returning from the syscall, register %rax contains the result of the system-call. A value in the range between -4095 and -1 indicates an error, it is -errno.
/// F) Only values of class INTEGER or class MEMORY are passed to the kernel.
/// This code is inspired by the syscall.rs (https://github.com/kmcallister/syscall.rs/) project.

#[macro_export]
macro_rules! syscall {
    ($arg0:expr)
        => ( $crate::syscall::syscall0($arg0 as u64) );

    ($arg0:expr, $arg1:expr)
        => ( $crate::syscall::syscall1($arg0 as u64, $arg1 as u64) );

    ($arg0:expr, $arg1:expr, $arg2:expr)
        => ( $crate::syscall::syscall2($arg0 as u64, $arg1 as u64, $arg2 as u64) );

    ($arg0:expr, $arg1:expr, $arg2:expr, $arg3:expr)
        => ( $crate::syscall::syscall3($arg0 as u64, $arg1 as u64, $arg2 as u64, $arg3 as u64) );

    ($arg0:expr, $arg1:expr, $arg2:expr, $arg3:expr, $arg4:expr)
        => ( $crate::syscall::syscall4($arg0 as u64, $arg1 as u64, $arg2 as u64, $arg3 as u64, $arg4 as u64) );

    ($arg0:expr, $arg1:expr, $arg2:expr, $arg3:expr, $arg4:expr, $arg5:expr)
        => ( $crate::syscall::syscall5($arg0 as u64, $arg1 as u64, $arg2 as u64, $arg3 as u64, $arg4 as u64, $arg5 as u64) );

    ($arg0:expr, $arg1:expr, $arg2:expr, $arg3:expr, $arg4:expr, $arg5:expr, $arg6:expr)
        => ( $crate::syscall::syscall6($arg0 as u64, $arg1 as u64, $arg2 as u64, $arg3 as u64, $arg4 as u64, $arg5 as u64, $arg6 as u64) );

    ($arg0:expr, $arg1:expr, $arg2:expr, $arg3:expr, $arg4:expr, $arg5:expr, $arg6:expr, $arg7:expr)
        => ( $crate::syscall::syscall7($arg0 as u64, $arg1 as u64, $arg2 as u64, $arg3 as u64, $arg4 as u64, $arg5 as u64, $arg6 as u64, $arg7 as u64) );
}

#[inline(always)]
pub unsafe fn syscall0(arg0: u64) -> u64 {
    let mut ret: u64;
    asm!("syscall" : "={rax}" (ret) : "{rax}" (arg0) : "rcx", "r11", "memory" : "volatile");
    ret
}

#[inline(always)]
pub unsafe fn syscall1(arg0: u64, arg1: u64) -> u64 {
    let mut ret: u64;
    asm!("syscall" : "={rax}" (ret) : "{rax}" (arg0), "{rdi}" (arg1)
                   : "rcx", "r11", "memory" : "volatile");
    ret
}

#[inline(always)]
pub unsafe fn syscall2(arg0: u64, arg1: u64, arg2: u64) -> u64 {
    let mut ret: u64;
    asm!("syscall" : "={rax}" (ret) : "{rax}" (arg0), "{rdi}" (arg1), "{rsi}" (arg2)
                   : "rcx", "r11", "memory" : "volatile");
    ret
}

#[inline(always)]
pub unsafe fn syscall3(arg0: u64, arg1: u64, arg2: u64, arg3: u64) -> u64 {
    let mut ret: u64;
    asm!("syscall" : "={rax}" (ret) : "{rax}" (arg0), "{rdi}" (arg1), "{rsi}" (arg2), "{rdx}" (arg3)
                   : "rcx", "r11", "memory" : "volatile");
    ret
}

#[inline(always)]
pub unsafe fn syscall4(arg0: u64, arg1: u64, arg2: u64, arg3: u64, arg4: u64) -> u64 {
    let mut ret: u64;
    asm!("syscall" : "={rax}" (ret)
                   : "{rax}"  (arg0), "{rdi}"  (arg1), "{rsi}"  (arg2), "{rdx}"  (arg3), "{r10}"  (arg4)
                   : "rcx", "r11", "memory" : "volatile");
    ret
}

#[inline(always)]
pub unsafe fn syscall5(arg0: u64, arg1: u64, arg2: u64, arg3: u64, arg4: u64, arg5: u64) -> u64 {
    let mut ret: u64;
    asm!("syscall" : "={rax}" (ret)
                   : "{rax}" (arg0), "{rdi}" (arg1), "{rsi}" (arg2), "{rdx}" (arg3), "{r10}" (arg4), "{r8}" (arg5)
                   : "rcx", "r11", "memory"
                   : "volatile");
    ret
}

#[inline(always)]
pub unsafe fn syscall6(arg0: u64, arg1: u64, arg2: u64, arg3: u64, arg4: u64, arg5: u64, arg6: u64) -> u64 {
    let mut ret: u64;
    asm!("syscall" : "={rax}" (ret)
                   : "{rax}" (arg0), "{rdi}" (arg1), "{rsi}" (arg2), "{rdx}" (arg3),
                     "{r10}" (arg4), "{r8}" (arg5), "{r9}" (arg6)
                   : "rcx", "r11", "memory"
                   : "volatile");
    ret
}<commit_msg>Fix comment.<commit_after>/// SYSCALL invokes an OS system-call handler at privilege level 0.
///
/// It does so by loading RIP from the IA32_LSTAR MSR (after saving the address of the instruction following SYSCALL into RCX).
///
/// "A.2 AMD64 Linux Kernel Conventions" of System V Application Binary Interface AMD64 Architecture Processor Supplement:
///
/// * The kernel interface uses %rdi, %rsi, %rdx, %r10, %r8 and %r9.
/// * A system-call is done via the syscall instruction. The kernel destroys registers %rcx and %r11.
/// * The number of the syscall has to be passed in register %rax.
/// * System-calls are limited to six arguments, no argument is passed directly on the stack.
/// * Returning from the syscall, register %rax contains the result of the system-call. A value in the range between -4095 and -1 indicates an error, it is -errno.
/// * Only values of class INTEGER or class MEMORY are passed to the kernel.
///
/// This code is inspired by the syscall.rs (https://github.com/kmcallister/syscall.rs/) project.

#[macro_export]
macro_rules! syscall {
    ($arg0:expr)
        => ( $crate::syscall::syscall0($arg0 as u64) );

    ($arg0:expr, $arg1:expr)
        => ( $crate::syscall::syscall1($arg0 as u64, $arg1 as u64) );

    ($arg0:expr, $arg1:expr, $arg2:expr)
        => ( $crate::syscall::syscall2($arg0 as u64, $arg1 as u64, $arg2 as u64) );

    ($arg0:expr, $arg1:expr, $arg2:expr, $arg3:expr)
        => ( $crate::syscall::syscall3($arg0 as u64, $arg1 as u64, $arg2 as u64, $arg3 as u64) );

    ($arg0:expr, $arg1:expr, $arg2:expr, $arg3:expr, $arg4:expr)
        => ( $crate::syscall::syscall4($arg0 as u64, $arg1 as u64, $arg2 as u64, $arg3 as u64, $arg4 as u64) );

    ($arg0:expr, $arg1:expr, $arg2:expr, $arg3:expr, $arg4:expr, $arg5:expr)
        => ( $crate::syscall::syscall5($arg0 as u64, $arg1 as u64, $arg2 as u64, $arg3 as u64, $arg4 as u64, $arg5 as u64) );

    ($arg0:expr, $arg1:expr, $arg2:expr, $arg3:expr, $arg4:expr, $arg5:expr, $arg6:expr)
        => ( $crate::syscall::syscall6($arg0 as u64, $arg1 as u64, $arg2 as u64, $arg3 as u64, $arg4 as u64, $arg5 as u64, $arg6 as u64) );

    ($arg0:expr, $arg1:expr, $arg2:expr, $arg3:expr, $arg4:expr, $arg5:expr, $arg6:expr, $arg7:expr)
        => ( $crate::syscall::syscall7($arg0 as u64, $arg1 as u64, $arg2 as u64, $arg3 as u64, $arg4 as u64, $arg5 as u64, $arg6 as u64, $arg7 as u64) );
}

#[inline(always)]
pub unsafe fn syscall0(arg0: u64) -> u64 {
    let mut ret: u64;
    asm!("syscall" : "={rax}" (ret) : "{rax}" (arg0) : "rcx", "r11", "memory" : "volatile");
    ret
}

#[inline(always)]
pub unsafe fn syscall1(arg0: u64, arg1: u64) -> u64 {
    let mut ret: u64;
    asm!("syscall" : "={rax}" (ret) : "{rax}" (arg0), "{rdi}" (arg1)
                   : "rcx", "r11", "memory" : "volatile");
    ret
}

#[inline(always)]
pub unsafe fn syscall2(arg0: u64, arg1: u64, arg2: u64) -> u64 {
    let mut ret: u64;
    asm!("syscall" : "={rax}" (ret) : "{rax}" (arg0), "{rdi}" (arg1), "{rsi}" (arg2)
                   : "rcx", "r11", "memory" : "volatile");
    ret
}

#[inline(always)]
pub unsafe fn syscall3(arg0: u64, arg1: u64, arg2: u64, arg3: u64) -> u64 {
    let mut ret: u64;
    asm!("syscall" : "={rax}" (ret) : "{rax}" (arg0), "{rdi}" (arg1), "{rsi}" (arg2), "{rdx}" (arg3)
                   : "rcx", "r11", "memory" : "volatile");
    ret
}

#[inline(always)]
pub unsafe fn syscall4(arg0: u64, arg1: u64, arg2: u64, arg3: u64, arg4: u64) -> u64 {
    let mut ret: u64;
    asm!("syscall" : "={rax}" (ret)
                   : "{rax}"  (arg0), "{rdi}"  (arg1), "{rsi}"  (arg2), "{rdx}"  (arg3), "{r10}"  (arg4)
                   : "rcx", "r11", "memory" : "volatile");
    ret
}

#[inline(always)]
pub unsafe fn syscall5(arg0: u64, arg1: u64, arg2: u64, arg3: u64, arg4: u64, arg5: u64) -> u64 {
    let mut ret: u64;
    asm!("syscall" : "={rax}" (ret)
                   : "{rax}" (arg0), "{rdi}" (arg1), "{rsi}" (arg2), "{rdx}" (arg3), "{r10}" (arg4), "{r8}" (arg5)
                   : "rcx", "r11", "memory"
                   : "volatile");
    ret
}

#[inline(always)]
pub unsafe fn syscall6(arg0: u64, arg1: u64, arg2: u64, arg3: u64, arg4: u64, arg5: u64, arg6: u64) -> u64 {
    let mut ret: u64;
    asm!("syscall" : "={rax}" (ret)
                   : "{rax}" (arg0), "{rdi}" (arg1), "{rsi}" (arg2), "{rdx}" (arg3),
                     "{r10}" (arg4), "{r8}" (arg5), "{r9}" (arg6)
                   : "rcx", "r11", "memory"
                   : "volatile");
    ret
}<|endoftext|>


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

extern crate tokio_io;
extern crate futures;

use futures::future::ok;
use std::rc::Rc;
use std::cell::RefCell;

use super::{Peer, io_other_error, BoxedNewPeerFuture, box_up_err};
use super::ws_peer::{WsReadWrapper, WsWriteWrapper, PeerForWs};

use tokio_io::{AsyncRead,AsyncWrite};
use std::io::{Read, Write, Error as IoError};

use std::ops::DerefMut;
use futures::Future;

type PeerSlot = Rc<RefCell<Option<Peer>>>;

#[derive(Default,Clone)]
pub struct GlobalState(PeerSlot);

#[derive(Clone)]
struct PeerHandle(PeerSlot);

impl Read for PeerHandle {
    fn read (&mut self, b:&mut [u8]) -> Result<usize, IoError> {
        if let Some(ref mut x) = self.0.borrow_mut().deref_mut() {
            x.0.read(b)
        } else {
            unreachable!()
        }
    }
}
impl AsyncRead for PeerHandle{}

impl Write for PeerHandle {
    fn write (&mut self, b: &[u8]) -> Result<usize, IoError> {
        if let Some(ref mut x) = self.0.borrow_mut().deref_mut() {
            x.1.write(b)
        } else {
            unreachable!()
        }
    }
    fn flush (&mut self) -> Result<(), IoError> {
        if let Some(ref mut x) = self.0.borrow_mut().deref_mut() {
            x.1.flush()
        } else {
            unreachable!()
        }
    }
}
impl AsyncWrite for PeerHandle {
    fn shutdown(&mut self) -> futures::Poll<(),IoError> {
        if let Some(ref mut x) = self.0.borrow_mut().deref_mut() {
            x.1.shutdown()
        } else {
            unreachable!()
        }
    }
}

pub fn connection_reuser(s: &mut GlobalState, inner_peer : BoxedNewPeerFuture) -> BoxedNewPeerFuture
{
    let need_init = s.0.borrow().is_none();

    let rc = s.0.clone();

    if need_init {
        Box::new(inner_peer.and_then(move |inner| {
            {
                let mut b = rc.borrow_mut();
                let x : &mut Option<Peer> = b.deref_mut();
                *x = Some(inner);
            }

            let ps : PeerSlot = rc.clone();

            let ph1 = PeerHandle(ps);
            let ph2 = ph1.clone();
            let peer = Peer::new(ph1, ph2);
            ok(peer)
        })) as BoxedNewPeerFuture
    } else {
        let ps : PeerSlot = rc.clone();

        let ph1 = PeerHandle(ps);
        let ph2 = ph1.clone();
        let peer = Peer::new(ph1, ph2);
        Box::new(ok(peer)) as BoxedNewPeerFuture
    }
}
<commit_msg>Tidy a bit<commit_after>extern crate tokio_io;
extern crate futures;

use futures::future::ok;
use std::rc::Rc;
use std::cell::RefCell;

use super::{Peer, BoxedNewPeerFuture};

use tokio_io::{AsyncRead,AsyncWrite};
use std::io::{Read, Write, Error as IoError};

use std::ops::DerefMut;
use futures::Future;

type PeerSlot = Rc<RefCell<Option<Peer>>>;

#[derive(Default,Clone)]
pub struct GlobalState(PeerSlot);

#[derive(Clone)]
struct PeerHandle(PeerSlot);

impl Read for PeerHandle {
    fn read (&mut self, b:&mut [u8]) -> Result<usize, IoError> {
        if let Some(ref mut x) = self.0.borrow_mut().deref_mut() {
            x.0.read(b)
        } else {
            unreachable!()
        }
    }
}
impl AsyncRead for PeerHandle{}

impl Write for PeerHandle {
    fn write (&mut self, b: &[u8]) -> Result<usize, IoError> {
        if let Some(ref mut x) = self.0.borrow_mut().deref_mut() {
            x.1.write(b)
        } else {
            unreachable!()
        }
    }
    fn flush (&mut self) -> Result<(), IoError> {
        if let Some(ref mut x) = self.0.borrow_mut().deref_mut() {
            x.1.flush()
        } else {
            unreachable!()
        }
    }
}
impl AsyncWrite for PeerHandle {
    fn shutdown(&mut self) -> futures::Poll<(),IoError> {
        if let Some(ref mut x) = self.0.borrow_mut().deref_mut() {
            x.1.shutdown()
        } else {
            unreachable!()
        }
    }
}

pub fn connection_reuser(s: &mut GlobalState, inner_peer : BoxedNewPeerFuture) -> BoxedNewPeerFuture
{
    let need_init = s.0.borrow().is_none();

    let rc = s.0.clone();

    if need_init {
        Box::new(inner_peer.and_then(move |inner| {
            {
                let mut b = rc.borrow_mut();
                let x : &mut Option<Peer> = b.deref_mut();
                *x = Some(inner);
            }

            let ps : PeerSlot = rc.clone();

            let ph1 = PeerHandle(ps);
            let ph2 = ph1.clone();
            let peer = Peer::new(ph1, ph2);
            ok(peer)
        })) as BoxedNewPeerFuture
    } else {
        let ps : PeerSlot = rc.clone();

        let ph1 = PeerHandle(ps);
        let ph2 = ph1.clone();
        let peer = Peer::new(ph1, ph2);
        Box::new(ok(peer)) as BoxedNewPeerFuture
    }
}
<|endoftext|>


<commit_before>use dailyschedule::{Handler, Schedule};
use std::cell::{Cell, RefCell};
use std::collections::BTreeSet;
use std::path;
use std::rc::Rc;
use super::config;
use super::serial;
use time::{Duration, Timespec, at_utc, at, now_utc};
use zoneinfo::ZoneInfo;

#[derive(Eq, PartialEq, Debug)]
enum Context {
    Off,
    On
}

struct Event {
    serial: serial::SerialClient,
    alias: String,
    last_on: Cell<Timespec>,
    valid_events: RefCell<BTreeSet<Timespec>>,
}

impl Event {
    fn new(alias: String, serial: serial::SerialClient) -> Event {
        Event {
            alias: alias,
            serial: serial,
            last_on: Cell::new(Timespec::new(0, 0)),
            valid_events: RefCell::new(BTreeSet::new()),
        }
    }
}

impl Handler<Context> for Event {
    /// Hint the event-handler for future events; this function will
    /// add only valid events (where on event lies before the off event) to
    /// the valid events list.
    fn hint(&self, ts: &Timespec, context: &Context) {
        match context {
            &Context::On => self.last_on.set(*ts),
            &Context::Off => {
                if *ts > self.last_on.get() {
                    let mut events = self.valid_events.borrow_mut();
                    events.insert(self.last_on.get());
                    events.insert(*ts);
                }
            }
        }
    }

    /// Perform action only when the timestamp is considered valid;
    /// Remove the current or prior timestamps from the expected timestamps.
    fn kick(&self, ts: &Timespec, context: &Context) {
        if self.valid_events.borrow().contains(ts) {
            println!("XXX: {} {:?} {}", at(*ts).asctime(), context, self.alias); // XXX
            match context {
                &Context::Off => self.serial.switch_off(&self.alias[..]),
                &Context::On => self.serial.switch_on(&self.alias[..]),
            }

            let mut events = self.valid_events.borrow_mut();

            // don't like the clone here, but keeps events mutable inside the loop
            for e in events.clone().iter().take_while(|&e| *e <= *ts) {
                events.remove(e);
            }
        }
    }
}

struct TrackerInner {
    serial: serial::SerialClient,
    config: config::Config,
    schedule: Schedule<Context, Event>,
    schedule_ref: Timespec,
    initial: bool,
}

impl TrackerInner {
    fn load_schedule(&mut self) {
        for circle in self.config.circles.iter() {
            match circle.default {
                config::CircleSetting::On => self.serial.switch_on(&circle.alias),
                config::CircleSetting::Off => self.serial.switch_off(&circle.alias),
                config::CircleSetting::Schedule => {
                    for toggle in circle.toggles.iter() {
                        let switch = Rc::new(Event::new(circle.alias.clone(), self.serial.clone()));
                        let start = toggle.start.into_dailyevent(&self.config.device);
                        let end = toggle.end.into_dailyevent(&self.config.device);

                        self.schedule.add_event(start, switch.clone(), Context::On);
                        self.schedule.add_event(end, switch.clone(), Context::Off);
                    }
                }
            }
        }
    }

    fn new(configfile: &path::PathBuf, zoneinfo: &ZoneInfo) -> TrackerInner {
        let config = config::Config::new(configfile).unwrap(); // XXX
        let schedule = Schedule::new(zoneinfo.clone());
        let serial = serial::Serial::spawn();

        let mut tracker = TrackerInner {
            config: config,
            schedule: schedule,
            serial: serial,
            schedule_ref: Timespec::new(0,0),
            initial: true,
        };

        tracker.load_schedule();

        tracker
    }

    fn update_schedule(&mut self) {
        self.schedule.update_schedule(self.schedule_ref);
        self.schedule_ref = self.schedule_ref + Duration::days(1);
    }

    fn process_tick(&mut self, timestamp: Timespec) {
        if self.initial {
            self.initial = false;
            let mut tm = at_utc(timestamp);
            tm.tm_hour = 0;
            tm.tm_min = 0;
            tm.tm_sec = 0;
            tm.tm_nsec = 0;
            self.schedule_ref = tm.to_timespec();

            // fill schedule for 48 hours:
            self.update_schedule();
            self.update_schedule();
        }

        if (self.schedule_ref - timestamp) <= Duration::days(1) {
            // make sure that at least 1 day of future updates is known
            self.update_schedule();
        }

        self.schedule.kick_event(timestamp);
    }

    // XXX remove in time
    fn fast_forward(&mut self) {
        let mut now = now_utc().to_timespec();

        loop {
            match self.schedule.kick_event(now) {
                Some(next) => {
                    now = next;
                }
                None => break
            }
        }
    }

}

pub struct Tracker {
    zoneinfo: ZoneInfo,
    path: path::PathBuf,
    inner: TrackerInner,
}

impl Tracker {
    pub fn new(configfile: path::PathBuf) -> Tracker {
        let zoneinfo = ZoneInfo::get_local_zoneinfo().ok().expect("BUG: not able to load local zoneinfo");

        let tracker = Tracker {
            inner: TrackerInner::new(&configfile, &zoneinfo),
            path: configfile,
            zoneinfo: zoneinfo,
        };

        tracker
    }

    pub fn process_tick(&mut self, timestamp: Timespec) {
        self.inner.process_tick(timestamp);
    }

    // XXX remove in time
    pub fn update_schedule(&mut self) {
        self.inner.update_schedule();
    }

    // XXX remove in time
    pub fn fast_forward(&mut self) {
        self.inner.fast_forward();
    }
}

<commit_msg>Couple of new ideas:<commit_after>use dailyschedule::{Handler, Schedule};
use std::cell::{Cell, RefCell};
use std::collections::BTreeMap;
use std::path;
use std::rc::Rc;
use super::config;
use super::serial;
use time::{Duration, Timespec, at_utc, at, now_utc};
use zoneinfo::ZoneInfo;

#[derive(Eq, PartialEq, Debug, Copy, Clone)]
enum Context {
    Off,
    On
}

struct Switch {
    serial: serial::SerialClient,
    alias: String,
    last_on: Cell<Timespec>,
    state: Cell<Context>,
    valid_events: RefCell<BTreeMap<Timespec, Context>>,
}

impl Switch {
    fn new(alias: String, serial: serial::SerialClient) -> Switch {
        Switch {
            alias: alias,
            serial: serial,
            last_on: Cell::new(Timespec::new(0, 0)),
            state: Cell::new(Context::Off),
            valid_events: RefCell::new(BTreeMap::new()),
        }
    }
}

impl Handler<Context> for Switch {
    /// Hint the event-handler for future events; this function will
    /// add only valid events (where on event lies before the off event) to
    /// the valid events list.
    fn hint(&self, ts: &Timespec, context: &Context) {
        match context {
            &Context::On => self.last_on.set(*ts),
            &Context::Off => {
                if *ts > self.last_on.get() {
                    let mut events = self.valid_events.borrow_mut();
                    events.insert(self.last_on.get(), Context::On);
                    events.insert(*ts, Context::Off);
                }
            }
        }
    }

    /// Perform action only when the timestamp is considered valid;
    /// Remove the current or prior timestamps from the expected timestamps.
    fn kick(&self, ts: &Timespec, context: &Context) {
        if self.valid_events.borrow().contains_key(ts) {
            println!("XXX: {} {:?} {}", at(*ts).asctime(), context, self.alias); // XXX
            self.state.set(*context);
            match context {
                &Context::Off => self.serial.switch_off(&self.alias[..]),
                &Context::On => self.serial.switch_on(&self.alias[..]),
            }

            let mut events = self.valid_events.borrow_mut();

            // don't like the clone here, but keeps events mutable inside the loop
            for (e, _) in events.clone().iter().take_while(|&(&e, _)| e <= *ts) {
                events.remove(e);
            }
        }
    }
}

struct TrackerInner {
    serial: serial::SerialClient,
    config: config::Config,
    schedule: Schedule<Context, Switch>,
    schedule_ref: Timespec,
    initial: bool,
    switches: BTreeMap<String, Rc<Switch>>,
}

impl TrackerInner {
    fn load_schedule(&mut self) {
        self.switches.clear();
        for circle in self.config.circles.iter() {
            let switch = Rc::new(Switch::new(circle.alias.clone(), self.serial.clone()));
            match circle.default {
                config::CircleSetting::On => self.serial.switch_on(&circle.alias),
                config::CircleSetting::Off => self.serial.switch_off(&circle.alias),
                config::CircleSetting::Schedule => {
                    for toggle in circle.toggles.iter() {
                        let start = toggle.start.into_dailyevent(&self.config.device);
                        let end = toggle.end.into_dailyevent(&self.config.device);

                        self.schedule.add_event(start, switch.clone(), Context::On);
                        self.schedule.add_event(end, switch.clone(), Context::Off);
                    }
                }
            }
            self.switches.insert(circle.alias.clone(), switch);
        }
    }

    fn new(configfile: &path::PathBuf, zoneinfo: &ZoneInfo) -> TrackerInner {
        let config = config::Config::new(configfile).unwrap(); // XXX
        let schedule = Schedule::new(zoneinfo.clone());
        let serial = serial::Serial::spawn();

        // XXX: register circles / connect etc...

        let mut tracker = TrackerInner {
            config: config,
            schedule: schedule,
            serial: serial,
            schedule_ref: Timespec::new(0,0),
            initial: true,
            switches: BTreeMap::new(),
        };

        tracker.load_schedule();

        tracker
    }

    fn update_schedule(&mut self) {
        self.schedule.update_schedule(self.schedule_ref);
        self.schedule_ref = self.schedule_ref + Duration::days(1);
    }

    fn process_tick(&mut self, timestamp: Timespec) {
        if self.initial {
            self.initial = false;
            let mut tm = at_utc(timestamp);
            tm.tm_hour = 0;
            tm.tm_min = 0;
            tm.tm_sec = 0;
            tm.tm_nsec = 0;
            self.schedule_ref = tm.to_timespec();

            // fill schedule for 48 hours:
            self.update_schedule();
            self.update_schedule();
        }

        if (self.schedule_ref - timestamp) <= Duration::days(1) {
            // make sure that at least 1 day of future updates is known
            self.update_schedule();
        }

        self.schedule.kick_event(timestamp);
    }

    // XXX remove in time
    fn fast_forward(&mut self) {
        let mut now = now_utc().to_timespec();

        loop {
            match self.schedule.kick_event(now) {
                Some(next) => {
                    now = next;
                }
                None => break
            }
        }
    }

}

pub struct Tracker {
    zoneinfo: ZoneInfo,
    path: path::PathBuf,
    inner: TrackerInner,
}

impl Tracker {
    pub fn new(configfile: path::PathBuf) -> Tracker {
        let zoneinfo = ZoneInfo::get_local_zoneinfo().ok().expect("BUG: not able to load local zoneinfo");

        let tracker = Tracker {
            inner: TrackerInner::new(&configfile, &zoneinfo),
            path: configfile,
            zoneinfo: zoneinfo,
        };

        tracker
    }

    pub fn process_tick(&mut self, timestamp: Timespec) {
        self.inner.process_tick(timestamp);
    }

    // XXX remove in time
    pub fn update_schedule(&mut self) {
        self.inner.update_schedule();
    }

    // XXX remove in time
    pub fn fast_forward(&mut self) {
        self.inner.fast_forward();
    }
}

<|endoftext|>