<commit_before>// DOCS

extern crate tempdir;

use std::error::Error;
use std::fmt;
use std::io;
use std::fs;
use std::borrow::Borrow;
use std::path;
use std::convert::AsRef;

use tempdir::TempDir;

pub use OverwriteBehavior::{AllowOverwrite, DisallowOverwrite};

#[derive(Clone,Copy)]
pub enum OverwriteBehavior {
    /// Overwrite files silently.
    AllowOverwrite,

    /// Don't overwrite files. `AtomicFile.write` will raise errors for such conditions only after
    /// you've already written your data.
    DisallowOverwrite
}

/// Represents an error raised by `AtomicFile.write`.
#[derive(Debug)]
pub enum AtomicWriteError<E> {
    /// The error originated in the library itself, while it was either creating a temporary file
    /// or moving the file into place.
    Internal(io::Error),
    /// The error originated in the user-supplied callback.
    User(E)
}

impl<E: fmt::Display> fmt::Display for AtomicWriteError<E> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            AtomicWriteError::Internal(ref e) => e.fmt(f),
            AtomicWriteError::User(ref e) => e.fmt(f)
        }
    }
}

impl<E: Error> Error for AtomicWriteError<E> {
    fn description(&self) -> &str {
        match *self {
            AtomicWriteError::Internal(ref e) => e.description(),
            AtomicWriteError::User(ref e) => e.description()
        }
    }

    fn cause(&self) -> Option<&Error> {
        match *self {
            AtomicWriteError::Internal(ref e) => Some(e),
            AtomicWriteError::User(ref e) => Some(e)
        }
    }
}

pub struct AtomicFile {
    path: path::PathBuf,
    overwrite: OverwriteBehavior,
    tmpdir: path::PathBuf
}

impl AtomicFile {
    /// Helper for writing to `path` in write-only mode.
    ///
    /// If `DisallowOverwrite` is given, errors will be returned from `self.write(...)` if the file
    /// exists.
    pub fn new<P: AsRef<path::Path>>(path: P, overwrite: OverwriteBehavior) -> Self {
        let p = path.as_ref();
        AtomicFile::new_with_tmpdir(p, overwrite, p.parent().unwrap_or(p))
    }

    pub fn new_with_tmpdir<P: AsRef<path::Path>>(path: P, overwrite: OverwriteBehavior, tmpdir: P) -> Self {
        AtomicFile {
            path: path.as_ref().to_path_buf(),
            overwrite: overwrite,
            tmpdir: tmpdir.as_ref().to_path_buf()
        }
    }

    fn commit(&self, tmppath: &path::Path) -> io::Result<()> {
        match self.overwrite {
            AllowOverwrite => replace_atomic(tmppath, self.path()),
            DisallowOverwrite => move_atomic(tmppath, self.path())
        }
    }

    /// Get the target filepath.
    pub fn path(&self) -> &path::Path { &self.path.borrow() }

    /// Open a temporary file, call `f` on it (which is supposed to write to it), then move the
    /// file atomically to `self.path`.
    pub fn write<T, E, F>(&self, f: F) -> Result<T, AtomicWriteError<E>> where
        F: FnOnce(&mut fs::File) -> Result<T, E>
    {
        macro_rules! try_internal {
            ($expr:expr) => {
                match $expr {
                    Ok(r) => r,
                    Err(e) => return Err(AtomicWriteError::Internal(e))
                }
            };
        }

        let tmpdir = try_internal!(TempDir::new_in(
            &self.tmpdir,
            ".atomicwrite"
        ));

        let tmppath = tmpdir.path().join("tmpfile.tmp");
        let rv = {
            let mut tmpfile = try_internal!(fs::File::create(&tmppath));
            try!(f(&mut tmpfile).map_err(AtomicWriteError::User))
        };
        try_internal!(self.commit(&tmppath));
        Ok(rv)
    }
}

#[cfg(unix)]
mod imp {
    extern crate nix;

    use std::{io,fs,path};
    use std::os::unix::io::AsRawFd;

    fn fsync_dir(x: &path::Path) -> io::Result<()> {
        let f = try!(fs::File::open(x));
        try!(nix::unistd::fsync(f.as_raw_fd()));
        Ok(())
    }

    pub fn replace_atomic(src: &path::Path, dst: &path::Path) -> io::Result<()> {
        try!(fs::rename(src, dst));

        let dst_directory = dst.parent().unwrap();
        try!(fsync_dir(dst_directory));
        Ok(())
    }

    pub fn move_atomic(src: &path::Path, dst: &path::Path) -> io::Result<()> {
        try!(fs::hard_link(src, dst));
        try!(fs::remove_file(src));

        let src_directory = src.parent().unwrap();
        let dst_directory = dst.parent().unwrap();
        try!(fsync_dir(dst_directory));
        if src_directory != dst_directory { try!(fsync_dir(src_directory)); }
        Ok(())
    }
}

#[cfg(windows)]
mod imp {
    extern crate winapi;
    extern crate kernel32 as win32kernel;

    use std::{io,path};
    use std::ffi::OsStr;
    use std::os::windows::ffi::OsStrExt;

    macro_rules! call {
        ($e: expr) => (
            if $e != 0 {
                Ok(())
            } else {
                Err(io::Error::last_os_error())
            }
        )
    }

    fn path_to_windows_str<T: AsRef<OsStr>>(x: T) -> Vec<winapi::WCHAR> {
        x.as_ref().encode_wide().chain(Some(0)).collect()
    }

    pub fn replace_atomic(src: &path::Path, dst: &path::Path) -> io::Result<()> {
        call!(unsafe {win32kernel::MoveFileExW(
            path_to_windows_str(src).as_ptr(), path_to_windows_str(dst).as_ptr(),
            winapi::MOVEFILE_WRITE_THROUGH | winapi::MOVEFILE_REPLACE_EXISTING
        )})
    }

    pub fn move_atomic(src: &path::Path, dst: &path::Path) -> io::Result<()> {
        call!(unsafe {win32kernel::MoveFileExW(
            path_to_windows_str(src).as_ptr(), path_to_windows_str(dst).as_ptr(),
            winapi::MOVEFILE_WRITE_THROUGH
        )})
    }
}

/// Move `src` to `dst`. If `dst` exists, it will be silently overwritten.
///
/// Both paths must reside on the same filesystem for the operation to be atomic.
pub fn replace_atomic(src: &path::Path, dst: &path::Path) -> io::Result<()> {
    imp::replace_atomic(src, dst)
}

/// Move `src` to `dst`. An error will be returned if `dst` exists.
///
/// Both paths must reside on the same filesystem for the operation to be atomic.
pub fn move_atomic(src: &path::Path, dst: &path::Path) -> io::Result<()> {
    imp::move_atomic(src, dst)
}
<commit_msg>Rename AtomicWriteError to Error<commit_after>// DOCS

extern crate tempdir;

use std::error::Error as ErrorTrait;
use std::fmt;
use std::io;
use std::fs;
use std::borrow::Borrow;
use std::path;
use std::convert::AsRef;

use tempdir::TempDir;

pub use OverwriteBehavior::{AllowOverwrite, DisallowOverwrite};

#[derive(Clone,Copy)]
pub enum OverwriteBehavior {
    /// Overwrite files silently.
    AllowOverwrite,

    /// Don't overwrite files. `AtomicFile.write` will raise errors for such conditions only after
    /// you've already written your data.
    DisallowOverwrite
}

/// Represents an error raised by `AtomicFile.write`.
#[derive(Debug)]
pub enum Error<E> {
    /// The error originated in the library itself, while it was either creating a temporary file
    /// or moving the file into place.
    Internal(io::Error),
    /// The error originated in the user-supplied callback.
    User(E)
}

impl<E: fmt::Display> fmt::Display for Error<E> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Error::Internal(ref e) => e.fmt(f),
            Error::User(ref e) => e.fmt(f)
        }
    }
}

impl<E: ErrorTrait> ErrorTrait for Error<E> {
    fn description(&self) -> &str {
        match *self {
            Error::Internal(ref e) => e.description(),
            Error::User(ref e) => e.description()
        }
    }

    fn cause(&self) -> Option<&ErrorTrait> {
        match *self {
            Error::Internal(ref e) => Some(e),
            Error::User(ref e) => Some(e)
        }
    }
}

pub struct AtomicFile {
    path: path::PathBuf,
    overwrite: OverwriteBehavior,
    tmpdir: path::PathBuf
}

impl AtomicFile {
    /// Helper for writing to `path` in write-only mode.
    ///
    /// If `DisallowOverwrite` is given, errors will be returned from `self.write(...)` if the file
    /// exists.
    pub fn new<P: AsRef<path::Path>>(path: P, overwrite: OverwriteBehavior) -> Self {
        let p = path.as_ref();
        AtomicFile::new_with_tmpdir(p, overwrite, p.parent().unwrap_or(p))
    }

    pub fn new_with_tmpdir<P: AsRef<path::Path>>(path: P, overwrite: OverwriteBehavior, tmpdir: P) -> Self {
        AtomicFile {
            path: path.as_ref().to_path_buf(),
            overwrite: overwrite,
            tmpdir: tmpdir.as_ref().to_path_buf()
        }
    }

    fn commit(&self, tmppath: &path::Path) -> io::Result<()> {
        match self.overwrite {
            AllowOverwrite => replace_atomic(tmppath, self.path()),
            DisallowOverwrite => move_atomic(tmppath, self.path())
        }
    }

    /// Get the target filepath.
    pub fn path(&self) -> &path::Path { &self.path.borrow() }

    /// Open a temporary file, call `f` on it (which is supposed to write to it), then move the
    /// file atomically to `self.path`.
    pub fn write<T, E, F>(&self, f: F) -> Result<T, Error<E>> where
        F: FnOnce(&mut fs::File) -> Result<T, E>
    {
        macro_rules! try_internal {
            ($expr:expr) => {
                match $expr {
                    Ok(r) => r,
                    Err(e) => return Err(Error::Internal(e))
                }
            };
        }

        let tmpdir = try_internal!(TempDir::new_in(
            &self.tmpdir,
            ".atomicwrite"
        ));

        let tmppath = tmpdir.path().join("tmpfile.tmp");
        let rv = {
            let mut tmpfile = try_internal!(fs::File::create(&tmppath));
            try!(f(&mut tmpfile).map_err(Error::User))
        };
        try_internal!(self.commit(&tmppath));
        Ok(rv)
    }
}

#[cfg(unix)]
mod imp {
    extern crate nix;

    use std::{io,fs,path};
    use std::os::unix::io::AsRawFd;

    fn fsync_dir(x: &path::Path) -> io::Result<()> {
        let f = try!(fs::File::open(x));
        try!(nix::unistd::fsync(f.as_raw_fd()));
        Ok(())
    }

    pub fn replace_atomic(src: &path::Path, dst: &path::Path) -> io::Result<()> {
        try!(fs::rename(src, dst));

        let dst_directory = dst.parent().unwrap();
        try!(fsync_dir(dst_directory));
        Ok(())
    }

    pub fn move_atomic(src: &path::Path, dst: &path::Path) -> io::Result<()> {
        try!(fs::hard_link(src, dst));
        try!(fs::remove_file(src));

        let src_directory = src.parent().unwrap();
        let dst_directory = dst.parent().unwrap();
        try!(fsync_dir(dst_directory));
        if src_directory != dst_directory { try!(fsync_dir(src_directory)); }
        Ok(())
    }
}

#[cfg(windows)]
mod imp {
    extern crate winapi;
    extern crate kernel32 as win32kernel;

    use std::{io,path};
    use std::ffi::OsStr;
    use std::os::windows::ffi::OsStrExt;

    macro_rules! call {
        ($e: expr) => (
            if $e != 0 {
                Ok(())
            } else {
                Err(io::Error::last_os_error())
            }
        )
    }

    fn path_to_windows_str<T: AsRef<OsStr>>(x: T) -> Vec<winapi::WCHAR> {
        x.as_ref().encode_wide().chain(Some(0)).collect()
    }

    pub fn replace_atomic(src: &path::Path, dst: &path::Path) -> io::Result<()> {
        call!(unsafe {win32kernel::MoveFileExW(
            path_to_windows_str(src).as_ptr(), path_to_windows_str(dst).as_ptr(),
            winapi::MOVEFILE_WRITE_THROUGH | winapi::MOVEFILE_REPLACE_EXISTING
        )})
    }

    pub fn move_atomic(src: &path::Path, dst: &path::Path) -> io::Result<()> {
        call!(unsafe {win32kernel::MoveFileExW(
            path_to_windows_str(src).as_ptr(), path_to_windows_str(dst).as_ptr(),
            winapi::MOVEFILE_WRITE_THROUGH
        )})
    }
}

/// Move `src` to `dst`. If `dst` exists, it will be silently overwritten.
///
/// Both paths must reside on the same filesystem for the operation to be atomic.
pub fn replace_atomic(src: &path::Path, dst: &path::Path) -> io::Result<()> {
    imp::replace_atomic(src, dst)
}

/// Move `src` to `dst`. An error will be returned if `dst` exists.
///
/// Both paths must reside on the same filesystem for the operation to be atomic.
pub fn move_atomic(src: &path::Path, dst: &path::Path) -> io::Result<()> {
    imp::move_atomic(src, dst)
}
<|endoftext|>


<commit_before>//! Implementation of the Micro Transport Protocol.[^spec]
//!
//! [^spec]: http://www.bittorrent.org/beps/bep_0029.html

//! # Examples
//!
//! ```no_run
//! extern crate utp;
//!
//! use utp::UtpStream;
//! use std::io::{Read, Write};
//!
//! fn main() {
//!     // Connect to an hypothetical local server running on port 8080
//!     let addr = "127.0.0.1:8080";
//!     let mut stream = match UtpStream::connect(addr) {
//!         Ok(stream) => stream,
//!         Err(e) => panic!("{}", e),
//!     };
//!
//!     // Send a string
//!     match stream.write("Hi there!".as_bytes()) {
//!         Ok(_) => (),
//!         Err(e) => println!("Write failed with {}", e)
//!     }
//!
//!     // Close the stream
//!     match stream.close() {
//!         Ok(()) => println!("Connection closed"),
//!         Err(e) => println!("{}", e)
//!     }
//! }
//! ```

//   __________  ____  ____
//  /_  __/ __ \/ __ \/ __ \
//   / / / / / / / / / / / /
//  / / / /_/ / /_/ / /_/ /
// /_/  \____/_____/\____/
//
// - Lossy UDP socket for testing purposes: send and receive ops are wrappers
// that stochastically drop or reorder packets.
// - Sending FIN on drop
// - Handle packet loss
// - Path MTU discovery (RFC4821)

#![deny(missing_docs)]

extern crate rand;
extern crate time;
extern crate num;
#[macro_use] extern crate log;
#[cfg(test)] extern crate quickcheck;

// Public API
pub use socket::UtpSocket;
pub use stream::UtpStream;

mod util;
mod bit_iterator;
mod packet;
mod socket;
mod stream;
<commit_msg>Improve module documentation a tiny bit.<commit_after>//! Implementation of the [Micro Transport Protocol][spec].
//!
//! This library provides both a socket interface (`UtpSocket`) and a stream interface (`UtpStream`).
//! I recommend that you use `UtpStream`, as it implements the `Read` and `Write`
//! traits we all know (and love) from `std::io`, which makes it generally easier to work with than `UtpSocket`.
//!
//! [spec]: http://www.bittorrent.org/beps/bep_0029.html
//!
//! # Installation
//!
//! Ensure your `Cargo.toml` contains:
//!
//! ```toml
//! [dependencies]
//! utp = "*"
//! ```
//!
//! # Examples
//!
//! ```no_run
//! extern crate utp;
//!
//! use utp::UtpStream;
//! use std::io::{Read, Write};
//!
//! fn main() {
//!     // Connect to an hypothetical local server running on port 8080
//!     let addr = "127.0.0.1:8080";
//!     let mut stream = match UtpStream::connect(addr) {
//!         Ok(stream) => stream,
//!         Err(e) => panic!("{}", e),
//!     };
//!
//!     // Send a string
//!     match stream.write("Hi there!".as_bytes()) {
//!         Ok(_) => (),
//!         Err(e) => println!("Write failed with {}", e)
//!     }
//!
//!     // Close the stream
//!     match stream.close() {
//!         Ok(()) => println!("Connection closed"),
//!         Err(e) => println!("{}", e)
//!     }
//! }
//! ```

//   __________  ____  ____
//  /_  __/ __ \/ __ \/ __ \
//   / / / / / / / / / / / /
//  / / / /_/ / /_/ / /_/ /
// /_/  \____/_____/\____/
//
// - Lossy UDP socket for testing purposes: send and receive ops are wrappers
// that stochastically drop or reorder packets.
// - Sending FIN on drop
// - Handle packet loss
// - Path MTU discovery (RFC4821)

#![deny(missing_docs)]

extern crate rand;
extern crate time;
extern crate num;
#[macro_use] extern crate log;
#[cfg(test)] extern crate quickcheck;

// Public API
pub use socket::UtpSocket;
pub use stream::UtpStream;

mod util;
mod bit_iterator;
mod packet;
mod socket;
mod stream;
<|endoftext|>


<commit_before>#![allow(non_camel_case_types, non_upper_case_globals, non_snake_case)]

extern crate libc;

use libc::{c_char, c_int, c_uchar, c_void};
use libc::size_t;

macro_rules! opaque {
    ($name:ident) => {
        #[repr(C)]
        pub struct $name {
            _private: [u8; 0]
        }
    };
}

// These are opaque types that LevelDB uses.
opaque!(leveldb_t);
opaque!(leveldb_cache_t);
opaque!(leveldb_comparator_t);
opaque!(leveldb_env_t);
opaque!(leveldb_filelock_t);
opaque!(leveldb_filterpolicy_t);
opaque!(leveldb_iterator_t);
opaque!(leveldb_logger_t);
opaque!(leveldb_options_t);
opaque!(leveldb_randomfile_t);
opaque!(leveldb_readoptions_t);
opaque!(leveldb_seqfile_t);
opaque!(leveldb_snapshot_t);
opaque!(leveldb_writablefile_t);
opaque!(leveldb_writebatch_t);
opaque!(leveldb_writeoptions_t);

#[repr(C)]
#[derive(Copy,Clone)]
pub enum Compression {
  No = 0,
  Snappy = 1
}

extern "C" {
    // DB operations
    /// Open the database at path `name` with the configurations set in `options`.
    ///
    /// If this operation fails,
    /// - `leveldb_t` is a nullpointer
    /// - `errptr` contains more information about the error reason
    pub fn leveldb_open(options: *const leveldb_options_t, name: *const c_char, errptr: *mut *mut c_char) -> *mut leveldb_t;
    /// Close the database at path `name`.
    ///
    /// Note that this operation cannot fail.
    pub fn leveldb_close(db: *mut leveldb_t);
    pub fn leveldb_put(db: *mut leveldb_t, options: *const leveldb_writeoptions_t, key: *const c_char, keylen: size_t, val: *const c_char, vallen: size_t, errptr: *mut *mut c_char);
    pub fn leveldb_delete(db: *mut leveldb_t, options: *const leveldb_writeoptions_t, key: *const c_char, keylen: size_t, errptr: *mut *mut c_char);
    pub fn leveldb_write(db: *mut leveldb_t, options: *const leveldb_writeoptions_t, batch: *mut leveldb_writebatch_t, errptr: *mut *mut c_char);
    pub fn leveldb_get(db: *mut leveldb_t, options: *const leveldb_readoptions_t, key: *const c_char, keylen: size_t, vallen: *mut size_t, errptr: *mut *mut c_char) -> *mut c_char;
    pub fn leveldb_create_iterator(db: *mut leveldb_t, options: *const leveldb_readoptions_t) -> *mut leveldb_iterator_t;
    pub fn leveldb_create_snapshot(db: *mut leveldb_t) -> *mut leveldb_snapshot_t;
    pub fn leveldb_release_snapshot(db: *mut leveldb_t, snapshot: *const leveldb_snapshot_t);
    pub fn leveldb_property_value(db: *mut leveldb_t, propname: *const c_char) -> *mut c_char;

    // TODO: const'ness of pointers here is in question
    pub fn leveldb_approximate_sizes(db: *mut leveldb_t, num_ranges: c_int, range_start_key: *const *const c_char, range_start_key_len: *const size_t, range_limit_key: *const *const c_char, range_limit_key_len: *const size_t, sizes: *mut u64);
    pub fn leveldb_compact_range(db: *mut leveldb_t, start_key: *const c_char, start_key_len: size_t, limit_key: *const c_char, limit_key_len: size_t);

    // Management operations
    pub fn leveldb_destroy_db(options: *const leveldb_options_t, name: *const c_char, errptr: *mut *mut c_char);
    pub fn leveldb_repair_db(options: *const leveldb_options_t, name: *const c_char, errptr: *mut *mut c_char);

    // Iterator
    pub fn leveldb_iter_destroy(it: *mut leveldb_iterator_t);
    pub fn leveldb_iter_valid(it: *const leveldb_iterator_t) -> c_uchar;
    pub fn leveldb_iter_seek_to_first(it: *mut leveldb_iterator_t);
    pub fn leveldb_iter_seek_to_last(it: *mut leveldb_iterator_t);
    pub fn leveldb_iter_seek(it: *mut leveldb_iterator_t, k: *const c_char, klen: size_t);
    pub fn leveldb_iter_next(it: *mut leveldb_iterator_t);
    pub fn leveldb_iter_prev(it: *mut leveldb_iterator_t);
    pub fn leveldb_iter_key(it: *const leveldb_iterator_t, klen: *const size_t) -> *const c_char;
    pub fn leveldb_iter_value(it: *const leveldb_iterator_t, vlen: *const size_t) -> *const c_char;
    pub fn leveldb_iter_get_error(it: *const leveldb_iterator_t, errptr: *const *const c_char);

    // Write batch
    pub fn leveldb_writebatch_create() -> *mut leveldb_writebatch_t;
    pub fn leveldb_writebatch_destroy(b: *mut leveldb_writebatch_t);
    pub fn leveldb_writebatch_clear(b: *mut leveldb_writebatch_t);
    pub fn leveldb_writebatch_put(b: *mut leveldb_writebatch_t, key: *const c_char, keylen: size_t, val: *const c_char, vallen: size_t);
    pub fn leveldb_writebatch_delete(b: *mut leveldb_writebatch_t, key: *const c_char, keylen: size_t);
    pub fn leveldb_writebatch_iterate(
        b: *mut leveldb_writebatch_t,
        state: *mut c_void,
        put: extern fn(*mut c_void, *const c_char, size_t, *const c_char, size_t),
        deleted: extern fn(*mut c_void, *const c_char, size_t)
    );

    // Options
    /// Create a new `leveldb_options_t` (not the database, but the database *configuration*!)
    pub fn leveldb_options_create() -> *mut leveldb_options_t;
    pub fn leveldb_options_destroy(o: *mut leveldb_options_t);
    pub fn leveldb_options_set_comparator(o: *mut leveldb_options_t, c: *mut leveldb_comparator_t);
    pub fn leveldb_options_set_filter_policy(o: *mut leveldb_options_t, c: *mut leveldb_filterpolicy_t);
    /// Modify `o` to specify whether a new database should be created if none exists yet
    ///
    /// - If `val` is != 0, new database creation is enabled
    /// - If `val` is 0,    no new database will be created if none exists yet
    pub fn leveldb_options_set_create_if_missing(o: *mut leveldb_options_t, val: c_uchar);
    pub fn leveldb_options_set_error_if_exists(o: *mut leveldb_options_t, val: c_uchar);
    pub fn leveldb_options_set_paranoid_checks(o: *mut leveldb_options_t, val: c_uchar);
    pub fn leveldb_options_set_env(o: *mut leveldb_options_t, env: *mut leveldb_env_t);
    pub fn leveldb_options_set_info_log(o: *mut leveldb_options_t, logger: *mut leveldb_logger_t);
    pub fn leveldb_options_set_write_buffer_size(o: *mut leveldb_options_t, size: size_t);
    pub fn leveldb_options_set_max_open_files(o: *mut leveldb_options_t, num: c_int);
    pub fn leveldb_options_set_cache(o: *mut leveldb_options_t, cache: *mut leveldb_cache_t);
    pub fn leveldb_options_set_block_size(o: *mut leveldb_options_t, size: size_t);
    pub fn leveldb_options_set_block_restart_interval(o: *mut leveldb_options_t, interval: c_int);
    pub fn leveldb_options_set_compression(o: *mut leveldb_options_t, val: Compression);

    // Comparator
    pub fn leveldb_comparator_create(
        state: *mut c_void,
        destructor: extern fn(*mut c_void),
        compare: extern fn(*mut c_void, *const c_char, size_t, *const c_char, size_t) -> c_int,
        name: extern fn(*mut c_void) -> *const c_char
    ) -> *mut leveldb_comparator_t;
    pub fn leveldb_comparator_destroy(c: *mut leveldb_comparator_t);

    // Filter policy
    //pub leveldb_filterpolicy_create(state: *mut c_void, /* TODO */) -> *mut leveldb_filterpolicy_t;
    /*
        extern leveldb_filterpolicy_t* leveldb_filterpolicy_create(
            void* state,
            void (*destructor)(void*),
            char* (*create_filter)(
                void*,
                const char* const* key_array, const size_t* key_length_array,
                int num_keys,
                size_t* filter_length),
            unsigned char (*key_may_match)(
                void*,
                const char* key, size_t length,
                const char* filter, size_t filter_length),
            const char* (*name)(void*));
    */
    pub fn leveldb_filterpolicy_destroy(p: *mut leveldb_filterpolicy_t);
    pub fn leveldb_filterpolicy_create_bloom(bits_per_key: c_int) -> *mut leveldb_filterpolicy_t;

    // Read options
    pub fn leveldb_readoptions_create() -> *mut leveldb_readoptions_t;
    pub fn leveldb_readoptions_destroy(o: *mut leveldb_readoptions_t);
    pub fn leveldb_readoptions_set_verify_checksums(o: *mut leveldb_readoptions_t, val: c_uchar);
    pub fn leveldb_readoptions_set_fill_cache(o: *mut leveldb_readoptions_t, val: c_uchar);
    pub fn leveldb_readoptions_set_snapshot(o: *mut leveldb_readoptions_t, snapshot: *const leveldb_snapshot_t);

    // Write options
    pub fn leveldb_writeoptions_create() -> *mut leveldb_writeoptions_t;
    pub fn leveldb_writeoptions_destroy(o: *mut leveldb_writeoptions_t);
    pub fn leveldb_writeoptions_set_sync(o: *mut leveldb_writeoptions_t, val: c_uchar);

    // Cache
    pub fn leveldb_cache_create_lru(capacity: size_t) -> *mut leveldb_cache_t;
    pub fn leveldb_cache_destroy(c: *mut leveldb_cache_t);

    // Env
    pub fn leveldb_create_default_env() -> *mut leveldb_env_t;
    pub fn leveldb_env_destroy(e: *mut leveldb_env_t);

    // Utility
    pub fn leveldb_free(ptr: *mut c_void);

    // Versioning
    pub fn leveldb_major_version() -> c_int;
    pub fn leveldb_minor_version() -> c_int;
}
<commit_msg>document leveldb_options_destroy()<commit_after>#![allow(non_camel_case_types, non_upper_case_globals, non_snake_case)]

extern crate libc;

use libc::{c_char, c_int, c_uchar, c_void};
use libc::size_t;

macro_rules! opaque {
    ($name:ident) => {
        #[repr(C)]
        pub struct $name {
            _private: [u8; 0]
        }
    };
}

// These are opaque types that LevelDB uses.
opaque!(leveldb_t);
opaque!(leveldb_cache_t);
opaque!(leveldb_comparator_t);
opaque!(leveldb_env_t);
opaque!(leveldb_filelock_t);
opaque!(leveldb_filterpolicy_t);
opaque!(leveldb_iterator_t);
opaque!(leveldb_logger_t);
opaque!(leveldb_options_t);
opaque!(leveldb_randomfile_t);
opaque!(leveldb_readoptions_t);
opaque!(leveldb_seqfile_t);
opaque!(leveldb_snapshot_t);
opaque!(leveldb_writablefile_t);
opaque!(leveldb_writebatch_t);
opaque!(leveldb_writeoptions_t);

#[repr(C)]
#[derive(Copy,Clone)]
pub enum Compression {
  No = 0,
  Snappy = 1
}

extern "C" {
    // DB operations
    /// Open the database at path `name` with the configurations set in `options`.
    ///
    /// If this operation fails,
    /// - `leveldb_t` is a nullpointer
    /// - `errptr` contains more information about the error reason
    pub fn leveldb_open(options: *const leveldb_options_t, name: *const c_char, errptr: *mut *mut c_char) -> *mut leveldb_t;
    /// Close the database at path `name`.
    ///
    /// Note that this operation cannot fail.
    pub fn leveldb_close(db: *mut leveldb_t);
    pub fn leveldb_put(db: *mut leveldb_t, options: *const leveldb_writeoptions_t, key: *const c_char, keylen: size_t, val: *const c_char, vallen: size_t, errptr: *mut *mut c_char);
    pub fn leveldb_delete(db: *mut leveldb_t, options: *const leveldb_writeoptions_t, key: *const c_char, keylen: size_t, errptr: *mut *mut c_char);
    pub fn leveldb_write(db: *mut leveldb_t, options: *const leveldb_writeoptions_t, batch: *mut leveldb_writebatch_t, errptr: *mut *mut c_char);
    pub fn leveldb_get(db: *mut leveldb_t, options: *const leveldb_readoptions_t, key: *const c_char, keylen: size_t, vallen: *mut size_t, errptr: *mut *mut c_char) -> *mut c_char;
    pub fn leveldb_create_iterator(db: *mut leveldb_t, options: *const leveldb_readoptions_t) -> *mut leveldb_iterator_t;
    pub fn leveldb_create_snapshot(db: *mut leveldb_t) -> *mut leveldb_snapshot_t;
    pub fn leveldb_release_snapshot(db: *mut leveldb_t, snapshot: *const leveldb_snapshot_t);
    pub fn leveldb_property_value(db: *mut leveldb_t, propname: *const c_char) -> *mut c_char;

    // TODO: const'ness of pointers here is in question
    pub fn leveldb_approximate_sizes(db: *mut leveldb_t, num_ranges: c_int, range_start_key: *const *const c_char, range_start_key_len: *const size_t, range_limit_key: *const *const c_char, range_limit_key_len: *const size_t, sizes: *mut u64);
    pub fn leveldb_compact_range(db: *mut leveldb_t, start_key: *const c_char, start_key_len: size_t, limit_key: *const c_char, limit_key_len: size_t);

    // Management operations
    pub fn leveldb_destroy_db(options: *const leveldb_options_t, name: *const c_char, errptr: *mut *mut c_char);
    pub fn leveldb_repair_db(options: *const leveldb_options_t, name: *const c_char, errptr: *mut *mut c_char);

    // Iterator
    pub fn leveldb_iter_destroy(it: *mut leveldb_iterator_t);
    pub fn leveldb_iter_valid(it: *const leveldb_iterator_t) -> c_uchar;
    pub fn leveldb_iter_seek_to_first(it: *mut leveldb_iterator_t);
    pub fn leveldb_iter_seek_to_last(it: *mut leveldb_iterator_t);
    pub fn leveldb_iter_seek(it: *mut leveldb_iterator_t, k: *const c_char, klen: size_t);
    pub fn leveldb_iter_next(it: *mut leveldb_iterator_t);
    pub fn leveldb_iter_prev(it: *mut leveldb_iterator_t);
    pub fn leveldb_iter_key(it: *const leveldb_iterator_t, klen: *const size_t) -> *const c_char;
    pub fn leveldb_iter_value(it: *const leveldb_iterator_t, vlen: *const size_t) -> *const c_char;
    pub fn leveldb_iter_get_error(it: *const leveldb_iterator_t, errptr: *const *const c_char);

    // Write batch
    pub fn leveldb_writebatch_create() -> *mut leveldb_writebatch_t;
    pub fn leveldb_writebatch_destroy(b: *mut leveldb_writebatch_t);
    pub fn leveldb_writebatch_clear(b: *mut leveldb_writebatch_t);
    pub fn leveldb_writebatch_put(b: *mut leveldb_writebatch_t, key: *const c_char, keylen: size_t, val: *const c_char, vallen: size_t);
    pub fn leveldb_writebatch_delete(b: *mut leveldb_writebatch_t, key: *const c_char, keylen: size_t);
    pub fn leveldb_writebatch_iterate(
        b: *mut leveldb_writebatch_t,
        state: *mut c_void,
        put: extern fn(*mut c_void, *const c_char, size_t, *const c_char, size_t),
        deleted: extern fn(*mut c_void, *const c_char, size_t)
    );

    // Options
    /// Create a new `leveldb_options_t` (not the database, but the database *configuration*!)
    pub fn leveldb_options_create() -> *mut leveldb_options_t;
    /// Deallocate `o` (not the database!)
    pub fn leveldb_options_destroy(o: *mut leveldb_options_t);
    pub fn leveldb_options_set_comparator(o: *mut leveldb_options_t, c: *mut leveldb_comparator_t);
    pub fn leveldb_options_set_filter_policy(o: *mut leveldb_options_t, c: *mut leveldb_filterpolicy_t);
    /// Modify `o` to specify whether a new database should be created if none exists yet
    ///
    /// - If `val` is != 0, new database creation is enabled
    /// - If `val` is 0,    no new database will be created if none exists yet
    pub fn leveldb_options_set_create_if_missing(o: *mut leveldb_options_t, val: c_uchar);
    pub fn leveldb_options_set_error_if_exists(o: *mut leveldb_options_t, val: c_uchar);
    pub fn leveldb_options_set_paranoid_checks(o: *mut leveldb_options_t, val: c_uchar);
    pub fn leveldb_options_set_env(o: *mut leveldb_options_t, env: *mut leveldb_env_t);
    pub fn leveldb_options_set_info_log(o: *mut leveldb_options_t, logger: *mut leveldb_logger_t);
    pub fn leveldb_options_set_write_buffer_size(o: *mut leveldb_options_t, size: size_t);
    pub fn leveldb_options_set_max_open_files(o: *mut leveldb_options_t, num: c_int);
    pub fn leveldb_options_set_cache(o: *mut leveldb_options_t, cache: *mut leveldb_cache_t);
    pub fn leveldb_options_set_block_size(o: *mut leveldb_options_t, size: size_t);
    pub fn leveldb_options_set_block_restart_interval(o: *mut leveldb_options_t, interval: c_int);
    pub fn leveldb_options_set_compression(o: *mut leveldb_options_t, val: Compression);

    // Comparator
    pub fn leveldb_comparator_create(
        state: *mut c_void,
        destructor: extern fn(*mut c_void),
        compare: extern fn(*mut c_void, *const c_char, size_t, *const c_char, size_t) -> c_int,
        name: extern fn(*mut c_void) -> *const c_char
    ) -> *mut leveldb_comparator_t;
    pub fn leveldb_comparator_destroy(c: *mut leveldb_comparator_t);

    // Filter policy
    //pub leveldb_filterpolicy_create(state: *mut c_void, /* TODO */) -> *mut leveldb_filterpolicy_t;
    /*
        extern leveldb_filterpolicy_t* leveldb_filterpolicy_create(
            void* state,
            void (*destructor)(void*),
            char* (*create_filter)(
                void*,
                const char* const* key_array, const size_t* key_length_array,
                int num_keys,
                size_t* filter_length),
            unsigned char (*key_may_match)(
                void*,
                const char* key, size_t length,
                const char* filter, size_t filter_length),
            const char* (*name)(void*));
    */
    pub fn leveldb_filterpolicy_destroy(p: *mut leveldb_filterpolicy_t);
    pub fn leveldb_filterpolicy_create_bloom(bits_per_key: c_int) -> *mut leveldb_filterpolicy_t;

    // Read options
    pub fn leveldb_readoptions_create() -> *mut leveldb_readoptions_t;
    pub fn leveldb_readoptions_destroy(o: *mut leveldb_readoptions_t);
    pub fn leveldb_readoptions_set_verify_checksums(o: *mut leveldb_readoptions_t, val: c_uchar);
    pub fn leveldb_readoptions_set_fill_cache(o: *mut leveldb_readoptions_t, val: c_uchar);
    pub fn leveldb_readoptions_set_snapshot(o: *mut leveldb_readoptions_t, snapshot: *const leveldb_snapshot_t);

    // Write options
    pub fn leveldb_writeoptions_create() -> *mut leveldb_writeoptions_t;
    pub fn leveldb_writeoptions_destroy(o: *mut leveldb_writeoptions_t);
    pub fn leveldb_writeoptions_set_sync(o: *mut leveldb_writeoptions_t, val: c_uchar);

    // Cache
    pub fn leveldb_cache_create_lru(capacity: size_t) -> *mut leveldb_cache_t;
    pub fn leveldb_cache_destroy(c: *mut leveldb_cache_t);

    // Env
    pub fn leveldb_create_default_env() -> *mut leveldb_env_t;
    pub fn leveldb_env_destroy(e: *mut leveldb_env_t);

    // Utility
    pub fn leveldb_free(ptr: *mut c_void);

    // Versioning
    pub fn leveldb_major_version() -> c_int;
    pub fn leveldb_minor_version() -> c_int;
}
<|endoftext|>


<commit_before>//! Library to extract the raw data and some metadata from digital camera
//! images. Given an image in a supported format and camera you will be able to get
//! everything needed to process the image
//!
//! # Example
//! ```rust,no_run
//! use std::env;
//! use std::fs::File;
//! use std::io::prelude::*;
//! use std::io::BufWriter;
//!
//! extern crate rawloader;
//!
//! fn main() {
//!   let args: Vec<_> = env::args().collect();
//!   if args.len() != 2 {
//!     println!("Usage: {} <file>", args[0]);
//!     std::process::exit(2);
//!   }
//!   let file = &args[1];
//!   let image = rawloader::decode(file).unwrap();
//!
//!   // Write out the image as a grayscale PPM
//!   let mut f = BufWriter::new(File::create(format!("{}.ppm",file)).unwrap());
//!   let preamble = format!("P6 {} {} {}\n", image.width, image.height, 65535).into_bytes();
//!   f.write_all(&preamble).unwrap();
//!   for pix in image.data {
//!     // Do an extremely crude "demosaic" by setting R=G=B
//!     let pixhigh = (pix>>8) as u8;
//!     let pixlow  = (pix&0x0f) as u8;
//!     f.write_all(&[pixhigh, pixlow, pixhigh, pixlow, pixhigh, pixlow]).unwrap()
//!   }
//! }
//! ```

#![deny(
  missing_docs,
  missing_debug_implementations,
  missing_copy_implementations,
  unsafe_code,
  unstable_features,
  unused_import_braces,
  unused_qualifications
)]

#[macro_use] extern crate enum_primitive;
extern crate num;
#[macro_use] extern crate lazy_static;
#[macro_use] extern crate serde_derive;
extern crate itertools;

mod decoders;
pub use decoders::RawImage;
pub use decoders::Orientation;
pub use decoders::cfa::CFA;
#[doc(hidden)] pub use decoders::Buffer;
#[doc(hidden)] pub use decoders::RawLoader;

lazy_static! {
  static ref LOADER: RawLoader = decoders::RawLoader::new();
}

use std::path::Path;

/// Take a path to a raw file and return a decoded image or an error
///
/// # Example
/// ```rust,ignore
/// let image = match rawloader::decode("path/to/your/file.RAW") {
///   Ok(val) => val,
///   Err(e) => ... some appropriate action when the file is unreadable ...
/// };
/// ```
pub fn decode<P: AsRef<Path>>(path: P) -> Result<RawImage,String> {
  LOADER.decode(path.as_ref())
}
<commit_msg>Add a rawloader error type for future proofing<commit_after>//! Library to extract the raw data and some metadata from digital camera
//! images. Given an image in a supported format and camera you will be able to get
//! everything needed to process the image
//!
//! # Example
//! ```rust,no_run
//! use std::env;
//! use std::fs::File;
//! use std::io::prelude::*;
//! use std::io::BufWriter;
//!
//! extern crate rawloader;
//!
//! fn main() {
//!   let args: Vec<_> = env::args().collect();
//!   if args.len() != 2 {
//!     println!("Usage: {} <file>", args[0]);
//!     std::process::exit(2);
//!   }
//!   let file = &args[1];
//!   let image = rawloader::decode(file).unwrap();
//!
//!   // Write out the image as a grayscale PPM
//!   let mut f = BufWriter::new(File::create(format!("{}.ppm",file)).unwrap());
//!   let preamble = format!("P6 {} {} {}\n", image.width, image.height, 65535).into_bytes();
//!   f.write_all(&preamble).unwrap();
//!   for pix in image.data {
//!     // Do an extremely crude "demosaic" by setting R=G=B
//!     let pixhigh = (pix>>8) as u8;
//!     let pixlow  = (pix&0x0f) as u8;
//!     f.write_all(&[pixhigh, pixlow, pixhigh, pixlow, pixhigh, pixlow]).unwrap()
//!   }
//! }
//! ```

#![deny(
  missing_docs,
  missing_debug_implementations,
  missing_copy_implementations,
  unsafe_code,
  unstable_features,
  unused_import_braces,
  unused_qualifications
)]

#[macro_use] extern crate enum_primitive;
extern crate num;
#[macro_use] extern crate lazy_static;
#[macro_use] extern crate serde_derive;
extern crate itertools;

mod decoders;
pub use decoders::RawImage;
pub use decoders::Orientation;
pub use decoders::cfa::CFA;
#[doc(hidden)] pub use decoders::Buffer;
#[doc(hidden)] pub use decoders::RawLoader;

lazy_static! {
  static ref LOADER: RawLoader = decoders::RawLoader::new();
}

use std::path::Path;
use std::error::Error;
use std::fmt;

/// Error type for any reason for the decode to fail
#[derive(Debug)]
pub struct RawLoaderError {
  msg: String,
}

impl fmt::Display for RawLoaderError {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    write!(f, "RawLoaderError: \"{}\"", self.msg)
  }
}

impl Error for RawLoaderError {
}

impl RawLoaderError {
  fn new(msg: String) -> Self {
    Self {
      msg,
    }
  }
}

/// Take a path to a raw file and return a decoded image or an error
///
/// # Example
/// ```rust,ignore
/// let image = match rawloader::decode("path/to/your/file.RAW") {
///   Ok(val) => val,
///   Err(e) => ... some appropriate action when the file is unreadable ...
/// };
/// ```
pub fn decode<P: AsRef<Path>>(path: P) -> Result<RawImage,RawLoaderError> {
  LOADER.decode(path.as_ref()).map_err(|err| RawLoaderError::new(err))
}
<|endoftext|>


<commit_before>//
// yaml.rs
// Nothing to see here yet
//

#![crate_id="yaml"]

use std::iter::Iterator;
use std::option::Option;

pub struct Scanner<'a> {
    reader: Box<Reader + 'a>,
    done: bool,
}

impl <'a>Scanner<'a> {
    pub fn new<T: Reader + 'a>(reader: Box<T>) -> Scanner<'a> {
        Scanner { reader: reader, done: false }
    }

    pub fn get_next_token(&mut self) -> Option<Token> {
        loop {
            match self.reader.read_byte() {
                Err(error) => {
                    self.done = true;
                    return Some(Token::Eof);
                },
                Ok(byte) => {
                    return Some(Token::Character);
                }
            }
        }
    }
}

#[derive(PartialEq, Copy, Show)]
pub enum Token {
    Character,
    Eof,
}

impl <'a>Iterator for Scanner<'a> {

    type Item = Token;

    fn next(&mut self) -> Option<Token> {
        if self.done {
            return None;
        }
        self.get_next_token()
    }
}

mod test {

    use std::io::MemReader;
    use std::vec;

    use super::{Scanner, Token};

    #[test]
    fn test_scanner() {
        let stream = "---";
        let mut reader = MemReader::new(stream.bytes().collect());
        let mut scanner = Scanner::new(Box::new(reader));
        let tokens: Vec<Token> = scanner.collect();
        let expected = vec!(Token::Character, Token::Character, Token::Character, Token::Eof);
        assert!(tokens == expected);
    }
}
<commit_msg>Refactor to be more like examples<commit_after>//
// yaml.rs
// Nothing to see here yet
//

#![crate_id="yaml"]

use std::iter::Iterator;
use std::option::Option;

pub struct Scanner<'a> {
    reader: Box<Reader + 'a>,
    done: bool,
}

impl <'a>Scanner<'a> {
    pub fn new<T: Reader + 'a>(reader: Box<T>) -> Scanner<'a> {
        Scanner { reader: reader, done: false }
    }

    pub fn get_next_token(&mut self) -> Option<Token> {
        if self.done {
            return None;
        }

        let token = match self.reader.read_byte() {
            Err(error) => {
                self.done = true;
                Token::Eof
            },
            Ok(byte) => {
               Token::Character
            }
        };
        Some(token)
    }
}

#[derive(PartialEq, Copy, Show)]
pub enum Token {
    Character,
    Eof,
}

impl <'a>Iterator for Scanner<'a> {

    type Item = Token;

    fn next(&mut self) -> Option<Token> {
        self.get_next_token()
    }
}

mod test {

    use std::io::MemReader;
    use std::vec;

    use super::{Scanner, Token};

    #[test]
    fn test_scanner() {
        let stream = "---";
        let mut reader = MemReader::new(stream.bytes().collect());
        let mut scanner = Scanner::new(Box::new(reader));
        let tokens: Vec<Token> = scanner.collect();
        let expected = vec!(Token::Character, Token::Character, Token::Character, Token::Eof);
        assert!(tokens == expected);
    }
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate bitflags;

mod api;
mod constants;
mod ffi;

pub use self::api::*;
pub use self::constants::*;
<commit_msg>lib: add crate-level rustdoc TODO item<commit_after>//! TODO

#[macro_use]
extern crate bitflags;

mod api;
mod constants;
mod ffi;

pub use self::api::*;
pub use self::constants::*;
<|endoftext|>


<commit_before>use std::ops::{Add, Sub, Mul,Div,Neg,Rem};

pub struct LimitVal {
    val : Result<u8,u8>,
    min : u8,
    max : u8
}

impl LimitVal {
    fn new(val:u8) -> LimitVal {
        if val > 0 && val < 10 { //0 and 10 replaced by macro for realz
            LimitVal{val: Ok(val), min: 0, max: 10} //0 and 10 used for illustration
        } else {
            LimitVal{val: Err(val), min: 0, max: 10} //0 and 10 used for illustration
        }
    }

    fn unwrap(&self) -> u8 {
        self.val.unwrap()
    }

    fn unwrap_err(&self) -> u8 {
        self.val.unwrap_err()
    }

    fn expect(&self, msg: &str) -> u8 {
       self.val.expect(msg)
    }

    fn map(&self, op:fn(u8) -> u8) -> LimitVal {
        match self.val {
            Ok(x) => LimitVal::new(op(x)),
            Err(x) => LimitVal::new(op(x))
        }

    }

}

impl Add for LimitVal {
    type Output = LimitVal;

    fn add(self, other: LimitVal) -> LimitVal {
        match self.val {
            Ok(x) => {
                match other.val {
                    Ok(y) => LimitVal::new(x+y), //both are Ok
                    Err(y) => LimitVal::new(x+y)
                }
            }
            Err(x) => {
                match other.val {
                    Ok(y) => LimitVal::new(x+y),
                    Err(y) => LimitVal::new(x+y)
                }
            }

        }
    }
}

impl Sub for LimitVal {
    type Output = LimitVal;

    fn sub(self, other: LimitVal) -> LimitVal {
        match self.val {
            Ok(x) => {
                match other.val {
                    Ok(y) => LimitVal::new(x-y), //both are Ok
                    Err(y) => LimitVal::new(x-y)
                }
            }
            Err(x) => {
                match other.val {
                    Ok(y) => LimitVal::new(x-y),
                    Err(y) => LimitVal::new(x-y)
                }
            }

        }
    }
}

impl Div for LimitVal {
    type Output = LimitVal;

    fn div(self, other: LimitVal) -> LimitVal {
        match self.val {
            Ok(x) => {
                match other.val {
                    Ok(y) => LimitVal::new(x/y), //both are Ok
                    Err(y) => LimitVal::new(x/y)
                }
            }
            Err(x) => {
                match other.val {
                    Ok(y) => LimitVal::new(x/y),
                    Err(y) => LimitVal::new(x/y)
                }
            }

        }
    }
}

impl Mul for LimitVal {
    type Output = LimitVal;

    fn mul(self, other: LimitVal) -> LimitVal {
        match self.val {
            Ok(x) => {
                match other.val {
                    Ok(y) => LimitVal::new(x*y), //both are Ok
                    Err(y) => LimitVal::new(x*y)
                }
            }
            Err(x) => {
                match other.val {
                    Ok(y) => LimitVal::new(x*y),
                    Err(y) => LimitVal::new(x*y)
                }
            }

        }
    }
}

impl Rem for LimitVal {
    type Output = LimitVal;

    fn rem(self, other: LimitVal) -> LimitVal {
        match self.val {
            Ok(x) => {
                match other.val {
                    Ok(y) => LimitVal::new(x%y), //both are Ok
                    Err(y) => LimitVal::new(x%y)
                }
            }
            Err(x) => {
                match other.val {
                    Ok(y) => LimitVal::new(x%y),
                    Err(y) => LimitVal::new(x%y)
                }
            }

        }
    }
}

impl Neg for LimitVal {
    type Output = LimitVal;

    fn neg(self) -> LimitVal {
        match self.val {
            Ok(x) => LimitVal::new(-x),
            Err(y) => LimitVal::new(-y)
        }
    }
}

#[cfg(test)]
mod tests {
    /*
    Can always put in own file, then would need:
    extern crate adder; (or whatever)
    and then namespace:: the functions.
    */
    use super::*;

    #[test]
    fn test_add() {
        let x: LimitVal = LimitVal::new(2);
        let y: LimitVal = LimitVal::new(3);

        let z = x + y;
        assert_eq!(z.unwrap(),5);
    }

    #[test]
    fn test_sub() {
        let x: LimitVal = LimitVal::new(2);
        let y: LimitVal = LimitVal::new(3);

        let z = y - x;
        assert_eq!(z.unwrap(),1);
    }
    #[test]
    fn test_div() {
        let x: LimitVal = LimitVal::new(4);
        let y: LimitVal = LimitVal::new(2);

        let z = x/y;
        assert_eq!(z.unwrap(),2);
    }
    #[test]
    fn test_mul() {
        let x: LimitVal = LimitVal::new(2);
        let y: LimitVal = LimitVal::new(3);

        let z = y * x;
        assert_eq!(z.unwrap(),6);
    }
    #[test]
    fn test_rem() {
        let x: LimitVal = LimitVal::new(5);
        let y: LimitVal = LimitVal::new(2);

        let z = x % y;
        assert_eq!(z.unwrap(),1);
    }
    #[test]
    #[should_panic]
    fn test_neg() {
        let x: LimitVal = LimitVal::new(2);
        let z = -x;
        assert_eq!(z.unwrap(), -2);
    }
    #[test]
    fn test_map() {
        let x: LimitVal = LimitVal::new(2);
        fn f (x:u8)-> u8 {
            let y: u8 = x + 1;
            y
        };

        let z = x.map(f);
        assert_eq!(z.unwrap(),3);
    }
}
<commit_msg>Fix map to take closures.<commit_after>use std::ops::{Add, Sub, Mul,Div,Neg,Rem};

pub struct LimitVal {
    val : Result<u8,u8>,
    min : u8,
    max : u8
}

impl LimitVal {
    fn new(val:u8) -> LimitVal {
        if val > 0 && val < 10 { //0 and 10 replaced by macro for realz
            LimitVal{val: Ok(val), min: 0, max: 10} //0 and 10 used for illustration
        } else {
            LimitVal{val: Err(val), min: 0, max: 10} //0 and 10 used for illustration
        }
    }

    fn unwrap(&self) -> u8 {
        self.val.unwrap()
    }

    fn unwrap_err(&self) -> u8 {
        self.val.unwrap_err()
    }

    fn expect(&self, msg: &str) -> u8 {
       self.val.expect(msg)
    }

    fn map<F>(&self, op:F) -> LimitVal
        where F: Fn(u8) -> u8 {
        match self.val {
            Ok(x) => LimitVal::new(op(x)),
            Err(x) => LimitVal::new(op(x))
        }

    }

}

impl Add for LimitVal {
    type Output = LimitVal;

    fn add(self, other: LimitVal) -> LimitVal {
        match self.val {
            Ok(x) => {
                match other.val {
                    Ok(y) => LimitVal::new(x+y), //both are Ok
                    Err(y) => LimitVal::new(x+y)
                }
            }
            Err(x) => {
                match other.val {
                    Ok(y) => LimitVal::new(x+y),
                    Err(y) => LimitVal::new(x+y)
                }
            }

        }
    }
}

impl Sub for LimitVal {
    type Output = LimitVal;

    fn sub(self, other: LimitVal) -> LimitVal {
        match self.val {
            Ok(x) => {
                match other.val {
                    Ok(y) => LimitVal::new(x-y), //both are Ok
                    Err(y) => LimitVal::new(x-y)
                }
            }
            Err(x) => {
                match other.val {
                    Ok(y) => LimitVal::new(x-y),
                    Err(y) => LimitVal::new(x-y)
                }
            }

        }
    }
}

impl Div for LimitVal {
    type Output = LimitVal;

    fn div(self, other: LimitVal) -> LimitVal {
        match self.val {
            Ok(x) => {
                match other.val {
                    Ok(y) => LimitVal::new(x/y), //both are Ok
                    Err(y) => LimitVal::new(x/y)
                }
            }
            Err(x) => {
                match other.val {
                    Ok(y) => LimitVal::new(x/y),
                    Err(y) => LimitVal::new(x/y)
                }
            }

        }
    }
}

impl Mul for LimitVal {
    type Output = LimitVal;

    fn mul(self, other: LimitVal) -> LimitVal {
        match self.val {
            Ok(x) => {
                match other.val {
                    Ok(y) => LimitVal::new(x*y), //both are Ok
                    Err(y) => LimitVal::new(x*y)
                }
            }
            Err(x) => {
                match other.val {
                    Ok(y) => LimitVal::new(x*y),
                    Err(y) => LimitVal::new(x*y)
                }
            }

        }
    }
}

impl Rem for LimitVal {
    type Output = LimitVal;

    fn rem(self, other: LimitVal) -> LimitVal {
        match self.val {
            Ok(x) => {
                match other.val {
                    Ok(y) => LimitVal::new(x%y), //both are Ok
                    Err(y) => LimitVal::new(x%y)
                }
            }
            Err(x) => {
                match other.val {
                    Ok(y) => LimitVal::new(x%y),
                    Err(y) => LimitVal::new(x%y)
                }
            }

        }
    }
}

impl Neg for LimitVal {
    type Output = LimitVal;

    fn neg(self) -> LimitVal {
        match self.val {
            Ok(x) => LimitVal::new(-x),
            Err(y) => LimitVal::new(-y)
        }
    }
}

#[cfg(test)]
mod tests {
    /*
    Can always put in own file, then would need:
    extern crate adder; (or whatever)
    and then namespace:: the functions.
    */
    use super::*;

    #[test]
    fn test_add() {
        let x: LimitVal = LimitVal::new(2);
        let y: LimitVal = LimitVal::new(3);

        let z = x + y;
        assert_eq!(z.unwrap(),5);
    }

    #[test]
    fn test_sub() {
        let x: LimitVal = LimitVal::new(2);
        let y: LimitVal = LimitVal::new(3);

        let z = y - x;
        assert_eq!(z.unwrap(),1);
    }
    #[test]
    fn test_div() {
        let x: LimitVal = LimitVal::new(4);
        let y: LimitVal = LimitVal::new(2);

        let z = x/y;
        assert_eq!(z.unwrap(),2);
    }
    #[test]
    fn test_mul() {
        let x: LimitVal = LimitVal::new(2);
        let y: LimitVal = LimitVal::new(3);

        let z = y * x;
        assert_eq!(z.unwrap(),6);
    }
    #[test]
    fn test_rem() {
        let x: LimitVal = LimitVal::new(5);
        let y: LimitVal = LimitVal::new(2);

        let z = x % y;
        assert_eq!(z.unwrap(),1);
    }
    #[test]
    #[should_panic]
    fn test_neg() {
        let x: LimitVal = LimitVal::new(2);
        let z = -x;
        assert_eq!(z.unwrap(), -2);
    }
    #[test]
    fn test_map() {
        let x: LimitVal = LimitVal::new(2);
        let f = |t| { t + 1};

        let z = x.map(f);
        assert_eq!(z.unwrap(),3);
    }
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate log;
extern crate getopts;
extern crate unix_daemonize;
extern crate byteorder;
extern crate udt;
extern crate time;
extern crate sodiumoxide;
extern crate rustc_serialize;

pub mod connection;

use unix_daemonize::{daemonize_redirect, ChdirMode};
use std::process::Command;
use std::net::{SocketAddr, SocketAddrV4, IpAddr};
use std::str;
use std::env;
use std::thread;
use std::str::FromStr;
use std::fs::{OpenOptions, File};
use std::path::{Path, PathBuf};
use std::time::{Instant, Duration};
use std::io::{Cursor, Error, Seek, SeekFrom, stderr, Read, Write};
use log::{LogRecord, LogLevel, LogMetadata};
use std::sync::mpsc;
use byteorder::{ReadBytesExt, WriteBytesExt, LittleEndian};
use sodiumoxide::crypto::secretbox;
use sodiumoxide::crypto::secretbox::xsalsa20poly1305::Key;
use rustc_serialize::hex::{FromHex, ToHex};

// TODO config
const INITIAL_ACCEPT_TIMEOUT_SECONDS: u64 = 60;

macro_rules! overprint {
    ($fmt: expr) => {
        print!(concat!("\x1b[2K\r", $fmt));
        std::io::stdout().flush().unwrap();
    };
    ($fmt:expr, $($arg:tt)*) => {
        print!(concat!("\x1b[2K\r", $fmt) , $($arg)*);
        std::io::stdout().flush().unwrap();
    };
}

macro_rules! die {
    ($fmt: expr) => {
        error!($fmt);
        panic!($fmt);
    };
    ($fmt:expr, $($arg:tt)*) => {
        error!($fmt, $($arg)*);
        panic!($fmt, $($arg)*);
    };
}

pub struct Server<'a> {
    pub ip: String,
    filename: &'a str,
    conn: connection::Server,
}

#[allow(dead_code)]
enum ShoopErrKind {
    Severed,
    Fatal,
}

struct ShoopErr {
    kind: ShoopErrKind,
    msg: Option<String>,
    finished: u64,
}

pub struct ShoopLogger;

impl log::Log for ShoopLogger {
    fn enabled(&self, metadata: &LogMetadata) -> bool {
        metadata.level() <= LogLevel::Info
    }

    fn log(&self, record: &LogRecord) {
        if self.enabled(record.metadata()) {
            let line = format!("{} - {}\n", record.level(), record.args());
            print!("{}", line);
        }
    }
}

impl ShoopLogger {
    pub fn init() -> Result<(), log::SetLoggerError> {
        log::set_logger(|max_log_level| {
            max_log_level.set(log::LogLevelFilter::Info);
            Box::new(ShoopLogger)
        })
    }
}

impl ShoopErr {
    pub fn new(kind: ShoopErrKind, msg: &str, finished: u64) -> ShoopErr {
        ShoopErr { kind: kind, msg: Some(String::from(msg)), finished: finished }
    }

    #[allow(dead_code)]
    pub fn from(err: Error, finished: u64) -> ShoopErr {
        ShoopErr { kind: ShoopErrKind::Severed, msg: Some(format!("{:?}", err)), finished: finished }
    }
}

impl<'a> Server<'a> {
    pub fn new(port_range : connection::PortRange,
               filename   : &str)
        -> Server
    {
        let sshconnstr = match env::var("SSH_CONNECTION") {
            Ok(s) => s.trim().to_owned(),
            Err(_) => { die!("SSH_CONNECTION env variable unset and required. Quitting."); }
        };
        let sshconn: Vec<&str> = sshconnstr.split(" ").collect();
        let ip = sshconn[2].to_owned();
        let key = secretbox::gen_key();
        let Key(keybytes) = key;
        let port = connection::Server::get_open_port(&port_range).unwrap();
        println!("shoop 0 {} {} {}", ip, port, keybytes.to_hex());

        let stdout = Some(Path::new(&env::var("HOME").unwrap()).join(".shoop.log"));
        let stderr = Some(Path::new(&env::var("HOME").unwrap()).join(".shoop.log"));
        daemonize_redirect(stdout, stderr, ChdirMode::ChdirRoot).unwrap();

        let conn = connection::Server::new(IpAddr::from_str(&ip).unwrap(), port, key);
        Server { ip: ip, conn: conn, filename: filename }
    }

    pub fn start(&self) {
        self.conn.listen().unwrap();

        let mut connection_count: usize = 0;
        info!("listening...");
        loop {
            info!("waiting for connection...");
            let (tx, rx) = mpsc::channel();
            if connection_count == 0 {
                thread::spawn(move || {
                    thread::sleep(Duration::from_secs(INITIAL_ACCEPT_TIMEOUT_SECONDS));
                    if let Err(_) = rx.try_recv() {
                        error!("timed out waiting for initial connection. exiting.");
                        std::process::exit(1);
                    }
                });
            }
            let client = match self.conn.accept() {
                Ok(client) => client,
                Err(e) => { die!("error on sock accept() {:?}", e); }
            };
            connection_count += 1;
            tx.send(()).unwrap();
            info!("accepted connection!");
            if let Ok(starthdr) = client.recv() {
                let version = starthdr[0];
                let mut rdr = Cursor::new(starthdr);
                rdr.set_position(1);
                let offset = rdr.read_u64::<LittleEndian>().unwrap();
                if version == 0x00 {
                    match self.send_file(&client, offset) {
                        Ok(_) => {
                            info!("done sending file");
                            let _ = client.close();
                            break;
                        }
                        Err(ShoopErr{ kind: ShoopErrKind::Severed, msg, finished}) => {
                            info!("connection severed, msg: {:?}, finished: {}", msg, finished);
                            let _ = client.close();
                            continue;
                        }
                        Err(ShoopErr{ kind: ShoopErrKind::Fatal, msg, finished}) => {
                            info!("connection fatal, msg: {:?}, finished: {}", msg, finished);
                            panic!("{:?}", msg);
                        }
                    }
                } else {
                    die!("unrecognized version");
                }
            } else {
                die!("failed to receive version byte from client");
            }
        }
        info!("exiting listen loop.");
    }

    fn send_file(&self, client: &connection::ServerConnection, offset: u64) -> Result<(), ShoopErr> {
        let mut f = File::open(self.filename).unwrap();
        f.seek(SeekFrom::Start(offset)).unwrap();
        let metadata = f.metadata().unwrap();

        let remaining = metadata.len() - offset;
        info!("total {} bytes", remaining);

        let mut wtr = vec![];
        wtr.write_u64::<LittleEndian>(remaining).unwrap();
        match client.send(&wtr[..]) {
            Ok(()) => { info!("wrote filesize header.") },
            Err(e) => {
                return Err(ShoopErr::new(ShoopErrKind::Severed, &format!("{:?}", e), remaining))
            }
        }
        let mut payload = vec![0; 1300];
        f.seek(SeekFrom::Start(offset)).unwrap();
        info!("sending file...");
        loop {
            match f.read(&mut payload) {
                Ok(0) => {
                    break;
                }
                Ok(read) => {
                    match client.send(&payload[0..read]) {
                        Ok(()) => { },
                        Err(e) => {
                            return Err(ShoopErr::new(ShoopErrKind::Severed, &format!("{:?}", e), remaining))
                        }
                    }
                },
                Err(e) => {
                    client.close().expect("Error closing stream");
                    error!("failed to read from file.");
                    panic!("{:?}", e);
                }
            }
        }

        client.close().expect("Error closing stream.");
        Ok(())
    }
}

pub fn download(remote_ssh_host : &str,
                port_range      : connection::PortRange,
                remote_path     : &str,
                local_path      : PathBuf)
{
    let cmd = format!("shoop -s '{}' -p {}", remote_path, port_range);
    // println!("addr: {}, path: {}, cmd: {}", addr, path, cmd);

    overprint!(" - establishing SSH session...");
    assert!(command_exists("ssh"), "`ssh` is required!");
    let output = Command::new("ssh")
                         .arg(remote_ssh_host.to_owned())
                         .arg(cmd)
                         .output()
                         .unwrap_or_else(|e| {
                             panic!("failed to execute process: {}", e);
                         });
    let infostring = String::from_utf8_lossy(&output.stdout).to_owned().trim().to_owned();
    let info: Vec<&str> = infostring.split(" ").collect();
    if info.len() != 5 {
        panic!("Unexpected response from server. Are you suuuuure shoop is setup on the server?");
    }

    let (magic, version, ip, port, keyhex) = (info[0], info[1], info[2], info[3], info[4]);
    overprint!(" - opening UDT connection...");
    if magic != "shoop" || version != "0" {
        panic!("Unexpected response from server. Are you suuuuure shoop is setup on the server?");
    }

    let mut keybytes = [0u8; 32];
    keybytes.copy_from_slice(&keyhex.from_hex().unwrap()[..]);
    let key = Key(keybytes);
    let addr: SocketAddr = SocketAddr::V4(SocketAddrV4::from_str(&format!("{}:{}", ip, port)[..]).unwrap());
    let conn = connection::Client::new(addr, key);

    let mut offset = 0u64;
    let mut filesize = None;
    let start_ts = Instant::now();
    loop {
        match conn.connect() {
            Ok(()) => {
                overprint!(" - connection opened, shakin' hands, makin' frands");
            },
            Err(e) => {
                panic!("errrrrrrr connecting to {}:{} - {:?}", ip, port, e);
            }
        }
        let mut wtr = vec![];
        wtr.push(0);
        wtr.write_u64::<LittleEndian>(offset).unwrap();
        match conn.send(&wtr[..]) {
            Err(_) => { conn.close().unwrap(); continue; }
            _      => {}
        }

        match conn.recv() {
            Ok(msg) => {
                if msg.len() == 0 {
                    panic!("failed to get filesize from server, probable timeout.");
                }
                let mut rdr = Cursor::new(msg);
                filesize = filesize.or(Some(rdr.read_u64::<LittleEndian>().unwrap()));
                overprint!(" + downloading {} ({:.1}MB)\n", local_path.to_string_lossy(), (filesize.unwrap() as f64)/(1024f64*1024f64));
                match recv_file(&conn, filesize.unwrap(), &local_path, offset) {
                     Ok(_) => {
                         break;
                     }
                     Err(ShoopErr{ kind: ShoopErrKind::Severed, msg: _, finished}) => {
                         println!(" * [[SEVERED]]");
                         offset = finished;
                     }
                     Err(ShoopErr{ kind: ShoopErrKind::Fatal, msg, finished: _}) => {
                         panic!("{:?}", msg);
                     }
                }
            }
            Err(_) => {}
        }
        let _ = conn.close();
    }

    let elapsed = start_ts.elapsed().as_secs();
    let fmt_time = if elapsed < 60 {
        format!("{}s", elapsed)
    } else if elapsed < 60 * 60 {
        format!("{}m{}s", elapsed / 60, elapsed % 60)
    } else {
        format!("{}h{}m{}s", elapsed / (60 * 60), elapsed / 60, elapsed % 60)
    };
    println!("shooped it all up in {}", fmt_time);
}

fn command_exists(command: &str) -> bool {
    match Command::new("which").arg(command).output() {
        Ok(output) => output.status.success(),
        Err(_)     => false
    }
}

fn recv_file(conn: &connection::Client, filesize: u64, filename: &PathBuf, offset: u64) -> Result<(), ShoopErr> {
    let mut f = OpenOptions::new().write(true).create(true).truncate(false).open(filename).unwrap();
    f.seek(SeekFrom::Start(offset)).unwrap();
    let start = Instant::now();
    let mut ts = Instant::now();
    let mut total = offset;
    let mut speed_ts = Instant::now();
    let mut speed_total = total;
    let mut speed = 0u64;
    loop {
        let buf = try!(conn.recv()
                           .map_err(|e| ShoopErr::new(ShoopErrKind::Severed, &format!("{:?}", e), total)));
        if buf.len() < 1 {
            return Err(ShoopErr::new(ShoopErrKind::Severed, "empty msg", total));
        }

        f.write_all(&buf[..]).unwrap();
        total += buf.len() as u64;
        let speed_elapsed = speed_ts.elapsed();
        if speed_elapsed > Duration::new(1, 0) {
            speed = ((total - speed_total) as f64 / ((speed_elapsed.as_secs() as f64) + (speed_elapsed.subsec_nanos() as f64) / 1_000_000_000f64)) as u64;
            speed_ts = Instant::now();
            speed_total = total;
        }
        let speedfmt = if speed < 1024 {
            format!("{} b/s", speed)
        } else if speed < 1024 * 1024 {
            format!("{} kb/s", speed / 1024)
        } else {
            format!("{:.1} MB/s", ((speed / 1024) as f64) / 1024f64)
        };

        if ts.elapsed() > Duration::new(0, 100_000_000) {
            overprint!("   {:.1}M / {:.1}M ({:.1}%) [ {} ]", (total as f64)/(1024f64*1024f64), (filesize as f64)/(1024f64*1024f64), (total as f64) / (filesize as f64) * 100f64, speedfmt);
            ts = Instant::now();
        }
        if total >= filesize {
            overprint!("   {0:.1}M / {0:.1}M (100%) [ avg {1:.1} MB/s ]", (filesize as f64)/(1024f64*1024f64), ((total - offset) / start.elapsed().as_secs() / 1024) as f64 / 1024f64);
            println!("\ndone.");
            break;
        }
    }
    let _ = conn.close();
    Ok(())
}

<commit_msg>ok, looks like we have this refactor off the ground.<commit_after>#[macro_use]
extern crate log;
extern crate getopts;
extern crate unix_daemonize;
extern crate byteorder;
extern crate udt;
extern crate time;
extern crate sodiumoxide;
extern crate rustc_serialize;

pub mod connection;

use unix_daemonize::{daemonize_redirect, ChdirMode};
use std::process::Command;
use std::net::{SocketAddr, SocketAddrV4, IpAddr};
use std::str;
use std::env;
use std::thread;
use std::str::FromStr;
use std::fs::{OpenOptions, File};
use std::path::{Path, PathBuf};
use std::time::{Instant, Duration};
use std::io::{Cursor, Error, Seek, SeekFrom, stderr, Read, Write};
use log::{LogRecord, LogLevel, LogMetadata};
use std::sync::mpsc;
use byteorder::{ReadBytesExt, WriteBytesExt, LittleEndian};
use sodiumoxide::crypto::secretbox;
use sodiumoxide::crypto::secretbox::xsalsa20poly1305::Key;
use rustc_serialize::hex::{FromHex, ToHex};

// TODO config
const INITIAL_ACCEPT_TIMEOUT_SECONDS: u64 = 60;

macro_rules! overprint {
    ($fmt: expr) => {
        print!(concat!("\x1b[2K\r", $fmt));
        std::io::stdout().flush().unwrap();
    };
    ($fmt:expr, $($arg:tt)*) => {
        print!(concat!("\x1b[2K\r", $fmt) , $($arg)*);
        std::io::stdout().flush().unwrap();
    };
}

macro_rules! die {
    ($fmt: expr) => {
        error!($fmt);
        panic!($fmt);
    };
    ($fmt:expr, $($arg:tt)*) => {
        error!($fmt, $($arg)*);
        panic!($fmt, $($arg)*);
    };
}

pub struct Server<'a> {
    pub ip: String,
    filename: &'a str,
    conn: connection::Server,
}

#[allow(dead_code)]
enum ShoopErrKind {
    Severed,
    Fatal,
}

struct ShoopErr {
    kind: ShoopErrKind,
    msg: Option<String>,
    finished: u64,
}

pub struct ShoopLogger;

impl log::Log for ShoopLogger {
    fn enabled(&self, metadata: &LogMetadata) -> bool {
        metadata.level() <= LogLevel::Info
    }

    fn log(&self, record: &LogRecord) {
        if self.enabled(record.metadata()) {
            let line = format!("{} - {}\n", record.level(), record.args());
            print!("{}", line);
        }
    }
}

impl ShoopLogger {
    pub fn init() -> Result<(), log::SetLoggerError> {
        log::set_logger(|max_log_level| {
            max_log_level.set(log::LogLevelFilter::Info);
            Box::new(ShoopLogger)
        })
    }
}

impl ShoopErr {
    pub fn new(kind: ShoopErrKind, msg: &str, finished: u64) -> ShoopErr {
        ShoopErr { kind: kind, msg: Some(String::from(msg)), finished: finished }
    }

    #[allow(dead_code)]
    pub fn from(err: Error, finished: u64) -> ShoopErr {
        ShoopErr { kind: ShoopErrKind::Severed, msg: Some(format!("{:?}", err)), finished: finished }
    }
}

impl<'a> Server<'a> {
    pub fn new(port_range : connection::PortRange,
               filename   : &str)
        -> Server
    {
        let sshconnstr = match env::var("SSH_CONNECTION") {
            Ok(s) => s.trim().to_owned(),
            Err(_) => { die!("SSH_CONNECTION env variable unset and required. Quitting."); }
        };
        let sshconn: Vec<&str> = sshconnstr.split(" ").collect();
        let ip = sshconn[2].to_owned();
        let key = secretbox::gen_key();
        let Key(keybytes) = key;
        let port = connection::Server::get_open_port(&port_range).unwrap();
        println!("shoop 0 {} {} {}", ip, port, keybytes.to_hex());

        let stdout = Some(Path::new(&env::var("HOME").unwrap()).join(".shoop.log"));
        let stderr = Some(Path::new(&env::var("HOME").unwrap()).join(".shoop.log"));
        daemonize_redirect(stdout, stderr, ChdirMode::ChdirRoot).unwrap();

        let conn = connection::Server::new(IpAddr::from_str(&ip).unwrap(), port, key);
        Server { ip: ip, conn: conn, filename: filename }
    }

    pub fn start(&self) {
        self.conn.listen().unwrap();

        let mut connection_count: usize = 0;
        info!("listening...");
        loop {
            info!("waiting for connection...");
            let (tx, rx) = mpsc::channel();
            if connection_count == 0 {
                thread::spawn(move || {
                    thread::sleep(Duration::from_secs(INITIAL_ACCEPT_TIMEOUT_SECONDS));
                    if let Err(_) = rx.try_recv() {
                        error!("timed out waiting for initial connection. exiting.");
                        std::process::exit(1);
                    }
                });
            }
            let client = match self.conn.accept() {
                Ok(client) => client,
                Err(e) => { die!("error on sock accept() {:?}", e); }
            };
            connection_count += 1;
            tx.send(()).unwrap();
            info!("accepted connection!");
            if let Ok(starthdr) = client.recv() {
                let version = starthdr[0];
                let mut rdr = Cursor::new(starthdr);
                rdr.set_position(1);
                let offset = rdr.read_u64::<LittleEndian>().unwrap();
                if version == 0x00 {
                    match self.send_file(&client, offset) {
                        Ok(_) => {
                            info!("done sending file");
                            let _ = client.close();
                            break;
                        }
                        Err(ShoopErr{ kind: ShoopErrKind::Severed, msg, finished}) => {
                            info!("connection severed, msg: {:?}, finished: {}", msg, finished);
                            let _ = client.close();
                            continue;
                        }
                        Err(ShoopErr{ kind: ShoopErrKind::Fatal, msg, finished}) => {
                            info!("connection fatal, msg: {:?}, finished: {}", msg, finished);
                            panic!("{:?}", msg);
                        }
                    }
                } else {
                    die!("unrecognized version");
                }
            } else {
                die!("failed to receive version byte from client");
            }
        }
        info!("exiting listen loop.");
    }

    fn send_file(&self, client: &connection::ServerConnection, offset: u64) -> Result<(), ShoopErr> {
        let mut f = File::open(self.filename).unwrap();
        f.seek(SeekFrom::Start(offset)).unwrap();
        let metadata = f.metadata().unwrap();

        let remaining = metadata.len() - offset;
        info!("total {} bytes", remaining);

        let mut wtr = vec![];
        wtr.write_u64::<LittleEndian>(remaining).unwrap();
        match client.send(&wtr[..]) {
            Ok(()) => {
                info!("wrote filesize header.")
            },
            Err(e) => {
                return Err(ShoopErr::new(ShoopErrKind::Severed, &format!("{:?}", e), remaining))
            }
        }
        let mut payload = vec![0; 1300];
        f.seek(SeekFrom::Start(offset)).unwrap();
        info!("sending file...");
        loop {
            match f.read(&mut payload) {
                Ok(0) => {
                    break;
                }
                Ok(read) => {
                    if let Err(e) = client.send(&payload[0..read]) {
                        return Err(ShoopErr::new(ShoopErrKind::Severed, &format!("{:?}", e), remaining))
                    }
                },
                Err(e) => {
                    client.close().expect("Error closing stream");
                    error!("failed to read from file.");
                    panic!("{:?}", e);
                }
            }
        }

        client.close().expect("Error closing stream.");
        Ok(())
    }
}

pub fn download(remote_ssh_host : &str,
                port_range      : connection::PortRange,
                remote_path     : &str,
                local_path      : PathBuf)
{
    let cmd = format!("shoop -s '{}' -p {}", remote_path, port_range);
    // println!("addr: {}, path: {}, cmd: {}", addr, path, cmd);

    overprint!(" - establishing SSH session...");
    assert!(command_exists("ssh"), "`ssh` is required!");
    let output = Command::new("ssh")
                         .arg(remote_ssh_host.to_owned())
                         .arg(cmd)
                         .output()
                         .unwrap_or_else(|e| {
                             panic!("failed to execute process: {}", e);
                         });
    let infostring = String::from_utf8_lossy(&output.stdout).to_owned().trim().to_owned();
    let info: Vec<&str> = infostring.split(" ").collect();
    if info.len() != 5 {
        panic!("Unexpected response from server. Are you suuuuure shoop is setup on the server?");
    }

    let (magic, version, ip, port, keyhex) = (info[0], info[1], info[2], info[3], info[4]);
    overprint!(" - opening UDT connection...");
    if magic != "shoop" || version != "0" {
        panic!("Unexpected response from server. Are you suuuuure shoop is setup on the server?");
    }

    let mut keybytes = [0u8; 32];
    keybytes.copy_from_slice(&keyhex.from_hex().unwrap()[..]);
    let key = Key(keybytes);
    let addr: SocketAddr = SocketAddr::V4(SocketAddrV4::from_str(&format!("{}:{}", ip, port)[..]).unwrap());
    let conn = connection::Client::new(addr, key);

    let mut offset = 0u64;
    let mut filesize = None;
    let start_ts = Instant::now();
    loop {
        match conn.connect() {
            Ok(()) => {
                overprint!(" - connection opened, shakin' hands, makin' frands");
            },
            Err(e) => {
                panic!("errrrrrrr connecting to {}:{} - {:?}", ip, port, e);
            }
        }
        let mut wtr = vec![];
        wtr.push(0);
        wtr.write_u64::<LittleEndian>(offset).unwrap();
        match conn.send(&wtr[..]) {
            Err(_) => { conn.close().unwrap(); continue; }
            _      => {}
        }

        match conn.recv() {
            Ok(msg) => {
                if msg.len() == 0 {
                    panic!("failed to get filesize from server, probable timeout.");
                }
                let mut rdr = Cursor::new(msg);
                filesize = filesize.or(Some(rdr.read_u64::<LittleEndian>().unwrap()));
                overprint!(" + downloading {} ({:.1}MB)\n", local_path.to_string_lossy(), (filesize.unwrap() as f64)/(1024f64*1024f64));
                match recv_file(&conn, filesize.unwrap(), &local_path, offset) {
                     Ok(_) => {
                         break;
                     }
                     Err(ShoopErr{ kind: ShoopErrKind::Severed, msg: _, finished}) => {
                         println!(" * [[SEVERED]]");
                         offset = finished;
                     }
                     Err(ShoopErr{ kind: ShoopErrKind::Fatal, msg, finished: _}) => {
                         panic!("{:?}", msg);
                     }
                }
            }
            Err(_) => {}
        }
        let _ = conn.close();
    }

    let elapsed = start_ts.elapsed().as_secs();
    let fmt_time = if elapsed < 60 {
        format!("{}s", elapsed)
    } else if elapsed < 60 * 60 {
        format!("{}m{}s", elapsed / 60, elapsed % 60)
    } else {
        format!("{}h{}m{}s", elapsed / (60 * 60), elapsed / 60, elapsed % 60)
    };
    println!("shooped it all up in {}", fmt_time);
}

fn command_exists(command: &str) -> bool {
    match Command::new("which").arg(command).output() {
        Ok(output) => output.status.success(),
        Err(_)     => false
    }
}

fn recv_file(conn: &connection::Client, filesize: u64, filename: &PathBuf, offset: u64) -> Result<(), ShoopErr> {
    let mut f = OpenOptions::new().write(true).create(true).truncate(false).open(filename).unwrap();
    f.seek(SeekFrom::Start(offset)).unwrap();
    let start = Instant::now();
    let mut ts = Instant::now();
    let mut total = offset;
    let mut speed_ts = Instant::now();
    let mut speed_total = total;
    let mut speed = 0u64;
    loop {
        let buf = try!(conn.recv()
                           .map_err(|e| ShoopErr::new(ShoopErrKind::Severed, &format!("{:?}", e), total)));
        if buf.len() < 1 {
            return Err(ShoopErr::new(ShoopErrKind::Severed, "empty msg", total));
        }

        f.write_all(&buf[..]).unwrap();
        total += buf.len() as u64;
        let speed_elapsed = speed_ts.elapsed();
        if speed_elapsed > Duration::new(1, 0) {
            speed = ((total - speed_total) as f64 / ((speed_elapsed.as_secs() as f64) + (speed_elapsed.subsec_nanos() as f64) / 1_000_000_000f64)) as u64;
            speed_ts = Instant::now();
            speed_total = total;
        }
        let speedfmt = if speed < 1024 {
            format!("{} b/s", speed)
        } else if speed < 1024 * 1024 {
            format!("{} kb/s", speed / 1024)
        } else {
            format!("{:.1} MB/s", ((speed / 1024) as f64) / 1024f64)
        };

        if ts.elapsed() > Duration::new(0, 100_000_000) {
            overprint!("   {:.1}M / {:.1}M ({:.1}%) [ {} ]", (total as f64)/(1024f64*1024f64), (filesize as f64)/(1024f64*1024f64), (total as f64) / (filesize as f64) * 100f64, speedfmt);
            ts = Instant::now();
        }
        if total >= filesize {
            overprint!("   {0:.1}M / {0:.1}M (100%) [ avg {1:.1} MB/s ]", (filesize as f64)/(1024f64*1024f64), ((total - offset) / start.elapsed().as_secs() / 1024) as f64 / 1024f64);
            println!("\ndone.");
            break;
        }
    }
    let _ = conn.close();
    Ok(())
}

<|endoftext|>


<commit_before>//! Simple resampling library in pure Rust.
//!
//! # Examples
//!
//! ```ignore
//! extern crate resize;
//! use resize::Pixel::Gray8;
//! use resize::Type::Triangle;
//! let mut src = vec![0;w1*h1];
//! let mut dst = vec![0;w2*h2];
//! let mut resizer = resize::new(w1, h1, w2, h2, Gray8, Triangle);
//! resizer.resize(&src, &mut dst);
//! ```
// Current implementation is based on:
// * https://github.com/sekrit-twc/zimg/tree/master/src/zimg/resize
// * https://github.com/PistonDevelopers/image/blob/master/src/imageops/sample.rs
#![deny(missing_docs)]

use std::f32;

/// Resizing type to use.
pub enum Type {
    /// Point resizing.
    Point,
    /// Triangle (bilinear) resizing.
    Triangle,
    /// Catmull-Rom (bicubic) resizing.
    Catrom,
    /// Resize using Mitchell-Netravali filter.
    Mitchell,
    /// Resize using sinc-windowed filter with radius of 2.
    Lanczos2,
    /// Sinc-windowed filter with radius of 3.
    Lanczos3,
    /// Resize using custom filter.
    Custom(Filter),
}

/// Resampling filter.
pub struct Filter {
    kernel: Box<Fn(f32) -> f32>,
    support: f32,
}

impl Filter {
    /// Create a new filter.
    ///
    /// # Examples
    ///
    /// ```
    /// use resize::Filter;
    /// fn kernel(x: f32) -> f32 { f32::max(1.0 - x.abs(), 0.0) }
    /// let filter = Filter::new(Box::new(kernel), 1.0);
    /// ```
    pub fn new(kernel: Box<Fn(f32) -> f32>, support: f32) -> Filter {
        Filter {kernel: kernel, support: support}
    }
}

#[inline]
fn point_kernel(_: f32) -> f32 {
    1.0
}

#[inline]
fn triangle_kernel(x: f32) -> f32 {
    f32::max(1.0 - x.abs(), 0.0)
}

// Taken from
// https://github.com/PistonDevelopers/image/blob/2921cd7/src/imageops/sample.rs#L68
// Could be optimized for known B and C, see e.g.:
// https://github.com/sekrit-twc/zimg/blob/1a606c0/src/zimg/resize/filter.cpp#L149
#[inline]
fn cubic_bc(b: f32, c: f32, x: f32) -> f32 {
    let a = x.abs();
    let k = if a < 1.0 {
        (12.0 - 9.0 * b - 6.0 * c) * a.powi(3) +
        (-18.0 + 12.0 * b + 6.0 * c) * a.powi(2) +
        (6.0 - 2.0 * b)
    } else if a < 2.0 {
        (-b - 6.0 * c) * a.powi(3) +
        (6.0 * b + 30.0 * c) * a.powi(2) +
        (-12.0 * b - 48.0 * c) * a +
        (8.0 * b + 24.0 * c)
    } else {
        0.0
    };
    k / 6.0
}

#[inline]
fn sinc(x: f32) -> f32 {
    if x == 0.0 {
        1.0
    } else {
        let a = x * f32::consts::PI;
        a.sin() / a
    }
}

#[inline]
fn lanczos(taps: f32, x: f32) -> f32 {
    if x.abs() < taps {
        sinc(x) * sinc(x / taps)
    } else {
        0.0
    }
}

/// Supported pixel formats.
// TODO(Kagami): >8-bit formats.
#[derive(Debug, Clone, Copy)]
pub enum Pixel {
    /// Grayscale, 8-bit.
    Gray8,
    /// RGB, 8-bit per component.
    RGB24,
    /// RGBA, 8-bit per component.
    RGBA,
}

impl Pixel {
    /// Size of one pixel in that format in bytes.
    #[inline]
    pub fn get_size(&self) -> usize {
        match *self {
            Pixel::Gray8 => 1,
            Pixel::RGB24 => 3,
            Pixel::RGBA => 4,
        }
    }

    /// Return number of components of that format.
    #[inline]
    pub fn get_ncomponents(&self) -> usize {
        match *self {
            Pixel::Gray8 => 1,
            Pixel::RGB24 => 3,
            Pixel::RGBA => 4,
        }
    }
}

/// Resampler with preallocated buffers and coeffecients for the given
/// dimensions and filter type.
#[derive(Debug)]
pub struct Resizer {
    // Source/target dimensions.
    w1: usize,
    h1: usize,
    w2: usize,
    h2: usize,
    pix_fmt: Pixel,
    // Temporary/preallocated stuff.
    tmp: Vec<f32>,
    accum: Vec<f32>,
    coeffs_w: Vec<CoeffsLine>,
    coeffs_h: Vec<CoeffsLine>,
}

#[derive(Debug)]
struct CoeffsLine {
    left: usize,
    data: Vec<f32>,
}

impl Resizer {
    /// Create a new resizer instance.
    pub fn new(w1: usize, h1: usize, w2: usize, h2: usize, p: Pixel, t: Type) -> Resizer {
        let filter = match t {
            Type::Point     => Filter::new(Box::new(point_kernel),                      0.0),
            Type::Triangle  => Filter::new(Box::new(triangle_kernel),                   1.0),
            Type::Catrom    => Filter::new(Box::new(|x| cubic_bc(0.0,     0.5,     x)), 2.0),
            Type::Mitchell  => Filter::new(Box::new(|x| cubic_bc(1.0/3.0, 1.0/3.0, x)), 2.0),
            Type::Lanczos2  => Filter::new(Box::new(|x| lanczos(2.0, x)),               2.0),
            Type::Lanczos3  => Filter::new(Box::new(|x| lanczos(3.0, x)),               3.0),
            Type::Custom(f) => f,
        };
        Resizer {
            w1: w1,
            h1: h1,
            w2: w2,
            h2: h2,
            pix_fmt: p,
            tmp: vec![0.0;w1*h2*p.get_size()],
            accum: vec![0.0;p.get_ncomponents()],
            // TODO(Kagami): Use same coeffs if w1 = h1 = w2 = h2?
            coeffs_w: Self::calc_coeffs(w1, w2, &filter),
            coeffs_h: Self::calc_coeffs(h1, h2, &filter),
        }
    }

    fn calc_coeffs(s1: usize, s2: usize, f: &Filter) -> Vec<CoeffsLine> {
        let ratio = s1 as f32 / s2 as f32;
        // Scale the filter when downsampling.
        let filter_scale = if ratio > 1.0 { ratio } else { 1.0 };
        let filter_radius = (f.support * filter_scale).ceil();
        let mut coeffs = Vec::with_capacity(s2);
        for x2 in 0..s2 {
            let x1 = (x2 as f32 + 0.5) * ratio - 0.5;
            let left = (x1 - filter_radius).ceil() as isize;
            let left = Self::clamp(left, 0, s1 as isize - 1) as usize;
            let right = (x1 + filter_radius).floor() as isize;
            let right = Self::clamp(right, 0, s1 as isize - 1) as usize;
            let mut data = Vec::with_capacity(right - left + 1);
            let mut sum = 0.0;
            for i in left..right+1 {
                sum += (f.kernel)((i as f32 - x1) / filter_scale);
            }
            for i in left..right+1 {
                let v = (f.kernel)((i as f32 - x1) / filter_scale);
                data.push(v / sum);
            }
            coeffs.push(CoeffsLine {left: left, data: data});
        }
        coeffs
    }

    #[inline]
    fn clamp<N: PartialOrd>(v: N, min: N, max: N) -> N {
        if v <= min {
            min
        } else if v >= max {
            max
        } else {
            v
        }
    }

    #[inline]
    fn pack_u8(mut v: f32) -> u8 {
        v = v.round();
        v = f32::min(f32::max(v, 0.0), 255.0);
        v as u8
    }

    #[inline]
    fn clear_accum(&mut self) {
        for v in self.accum.iter_mut() {
            *v = 0.0;
        }
    }

    // Resample W1xH1 to W1xH2.
    fn sample_rows(&mut self, src: &[u8]) {
        let ncomp = self.pix_fmt.get_ncomponents();
        let mut offset = 0;
        for x1 in 0..self.w1 {
            for y2 in 0..self.h2 {
                self.clear_accum();
                let ref line = self.coeffs_h[y2];
                for (i, coeff) in line.data.iter().enumerate() {
                    let y0 = line.left + i;
                    let base = (y0 * self.w1 + x1) * ncomp;
                    for n in 0..ncomp {
                        let p = src[base + n] as f32;
                        self.accum[n] += p * coeff;
                    }
                }
                for &v in &self.accum {
                    self.tmp[offset] = v;
                    offset += 1;
                }
            }
        }
    }

    // Resample W1xH2 to W2xH2.
    fn sample_cols(&mut self, dst: &mut [u8]) {
        let ncomp = self.pix_fmt.get_ncomponents();
        let mut offset = 0;
        for y2 in 0..self.h2 {
            for x2 in 0..self.w2 {
                self.clear_accum();
                let ref line = self.coeffs_w[x2];
                for (i, coeff) in line.data.iter().enumerate() {
                    let x0 = line.left + i;
                    let base = (x0 * self.h2 + y2) * ncomp;
                    for n in 0..ncomp {
                        let p = self.tmp[base + n];
                        self.accum[n] += p * coeff;
                    }
                }
                for &v in &self.accum {
                    dst[offset] = Self::pack_u8(v);
                    offset += 1;
                }
            }
        }
    }

    /// Resize `src` image data into `dst`.
    pub fn resize(&mut self, src: &[u8], dst: &mut [u8]) {
        // TODO(Kagami):
        // * Multi-thread
        // * Bound checkings
        // * SIMD
        assert_eq!(src.len(), self.w1 * self.h1 * self.pix_fmt.get_size());
        assert_eq!(dst.len(), self.w2 * self.h2 * self.pix_fmt.get_size());
        self.sample_rows(src);
        self.sample_cols(dst)
    }
}

/// Create a new resizer instance. Alias for `Resizer::new`.
pub fn new(w1: usize, h1: usize, w2: usize, h2: usize, p: Pixel, t: Type) -> Resizer {
    Resizer::new(w1, h1, w2, h2, p, t)
}

/// Resize image data to the new dimension in a single step.
///
/// **NOTE:** If you need to resize to the same dimension multiple times,
/// consider creating an resizer instance since it's faster.
pub fn resize(
    w1: usize, h1: usize, w2: usize, h2: usize,
    p: Pixel, t: Type,
    src: &[u8], dst: &mut [u8],
) {
    Resizer::new(w1, h1, w2, h2, p, t).resize(src, dst)
}
<commit_msg>Add new_cubic, new_lanczos helpers<commit_after>//! Simple resampling library in pure Rust.
//!
//! # Examples
//!
//! ```ignore
//! extern crate resize;
//! use resize::Pixel::Gray8;
//! use resize::Type::Triangle;
//! let mut src = vec![0;w1*h1];
//! let mut dst = vec![0;w2*h2];
//! let mut resizer = resize::new(w1, h1, w2, h2, Gray8, Triangle);
//! resizer.resize(&src, &mut dst);
//! ```
// Current implementation is based on:
// * https://github.com/sekrit-twc/zimg/tree/master/src/zimg/resize
// * https://github.com/PistonDevelopers/image/blob/master/src/imageops/sample.rs
#![deny(missing_docs)]

use std::f32;

/// Resizing type to use.
pub enum Type {
    /// Point resizing.
    Point,
    /// Triangle (bilinear) resizing.
    Triangle,
    /// Catmull-Rom (bicubic) resizing.
    Catrom,
    /// Resize using Mitchell-Netravali filter.
    Mitchell,
    /// Resize using sinc-windowed filter with radius of 3.
    Lanczos3,
    /// Resize using custom filter.
    Custom(Filter),
}

/// Resampling filter.
pub struct Filter {
    kernel: Box<Fn(f32) -> f32>,
    support: f32,
}

impl Filter {
    /// Create a new filter.
    ///
    /// # Examples
    ///
    /// ```
    /// use resize::Filter;
    /// fn kernel(x: f32) -> f32 { f32::max(1.0 - x.abs(), 0.0) }
    /// let filter = Filter::new(Box::new(kernel), 1.0);
    /// ```
    pub fn new(kernel: Box<Fn(f32) -> f32>, support: f32) -> Filter {
        Filter {kernel: kernel, support: support}
    }

    /// Helper to create Cubic filter with custom B and C parameters.
    pub fn new_cubic(b: f32, c: f32) -> Filter {
        Filter::new(Box::new(move |x| cubic_bc(b, c, x)), 2.0)
    }

    /// Helper to create Lanczos filter with custom radius.
    pub fn new_lanczos(radius: f32) -> Filter {
        Filter::new(Box::new(move |x| lanczos(radius, x)), radius)
    }
}

#[inline]
fn point_kernel(_: f32) -> f32 {
    1.0
}

#[inline]
fn triangle_kernel(x: f32) -> f32 {
    f32::max(1.0 - x.abs(), 0.0)
}

// Taken from
// https://github.com/PistonDevelopers/image/blob/2921cd7/src/imageops/sample.rs#L68
// TODO(Kagami): Could be optimized for known B and C, see e.g.
// https://github.com/sekrit-twc/zimg/blob/1a606c0/src/zimg/resize/filter.cpp#L149
#[inline]
fn cubic_bc(b: f32, c: f32, x: f32) -> f32 {
    let a = x.abs();
    let k = if a < 1.0 {
        (12.0 - 9.0 * b - 6.0 * c) * a.powi(3) +
        (-18.0 + 12.0 * b + 6.0 * c) * a.powi(2) +
        (6.0 - 2.0 * b)
    } else if a < 2.0 {
        (-b - 6.0 * c) * a.powi(3) +
        (6.0 * b + 30.0 * c) * a.powi(2) +
        (-12.0 * b - 48.0 * c) * a +
        (8.0 * b + 24.0 * c)
    } else {
        0.0
    };
    k / 6.0
}

#[inline]
fn sinc(x: f32) -> f32 {
    if x == 0.0 {
        1.0
    } else {
        let a = x * f32::consts::PI;
        a.sin() / a
    }
}

#[inline]
fn lanczos(taps: f32, x: f32) -> f32 {
    if x.abs() < taps {
        sinc(x) * sinc(x / taps)
    } else {
        0.0
    }
}

/// Supported pixel formats.
// TODO(Kagami): >8-bit formats.
#[derive(Debug, Clone, Copy)]
pub enum Pixel {
    /// Grayscale, 8-bit.
    Gray8,
    /// RGB, 8-bit per component.
    RGB24,
    /// RGBA, 8-bit per component.
    RGBA,
}

impl Pixel {
    /// Size of one pixel in that format in bytes.
    #[inline]
    pub fn get_size(&self) -> usize {
        match *self {
            Pixel::Gray8 => 1,
            Pixel::RGB24 => 3,
            Pixel::RGBA => 4,
        }
    }

    /// Return number of components of that format.
    #[inline]
    pub fn get_ncomponents(&self) -> usize {
        match *self {
            Pixel::Gray8 => 1,
            Pixel::RGB24 => 3,
            Pixel::RGBA => 4,
        }
    }
}

/// Resampler with preallocated buffers and coeffecients for the given
/// dimensions and filter type.
#[derive(Debug)]
pub struct Resizer {
    // Source/target dimensions.
    w1: usize,
    h1: usize,
    w2: usize,
    h2: usize,
    pix_fmt: Pixel,
    // Temporary/preallocated stuff.
    tmp: Vec<f32>,
    accum: Vec<f32>,
    coeffs_w: Vec<CoeffsLine>,
    coeffs_h: Vec<CoeffsLine>,
}

#[derive(Debug)]
struct CoeffsLine {
    left: usize,
    data: Vec<f32>,
}

impl Resizer {
    /// Create a new resizer instance.
    pub fn new(w1: usize, h1: usize, w2: usize, h2: usize, p: Pixel, t: Type) -> Resizer {
        let filter = match t {
            Type::Point => Filter::new(Box::new(point_kernel), 0.0),
            Type::Triangle => Filter::new(Box::new(triangle_kernel), 1.0),
            Type::Catrom => Filter::new_cubic(0.0, 0.5),
            Type::Mitchell => Filter::new_cubic(1.0/3.0, 1.0/3.0),
            Type::Lanczos3 => Filter::new_lanczos(3.0),
            Type::Custom(f) => f,
        };
        Resizer {
            w1: w1,
            h1: h1,
            w2: w2,
            h2: h2,
            pix_fmt: p,
            tmp: vec![0.0;w1*h2*p.get_size()],
            accum: vec![0.0;p.get_ncomponents()],
            // TODO(Kagami): Use same coeffs if w1 = h1 = w2 = h2?
            coeffs_w: Self::calc_coeffs(w1, w2, &filter),
            coeffs_h: Self::calc_coeffs(h1, h2, &filter),
        }
    }

    fn calc_coeffs(s1: usize, s2: usize, f: &Filter) -> Vec<CoeffsLine> {
        let ratio = s1 as f32 / s2 as f32;
        // Scale the filter when downsampling.
        let filter_scale = if ratio > 1.0 { ratio } else { 1.0 };
        let filter_radius = (f.support * filter_scale).ceil();
        let mut coeffs = Vec::with_capacity(s2);
        for x2 in 0..s2 {
            let x1 = (x2 as f32 + 0.5) * ratio - 0.5;
            let left = (x1 - filter_radius).ceil() as isize;
            let left = Self::clamp(left, 0, s1 as isize - 1) as usize;
            let right = (x1 + filter_radius).floor() as isize;
            let right = Self::clamp(right, 0, s1 as isize - 1) as usize;
            let mut data = Vec::with_capacity(right - left + 1);
            let mut sum = 0.0;
            for i in left..right+1 {
                sum += (f.kernel)((i as f32 - x1) / filter_scale);
            }
            for i in left..right+1 {
                let v = (f.kernel)((i as f32 - x1) / filter_scale);
                data.push(v / sum);
            }
            coeffs.push(CoeffsLine {left: left, data: data});
        }
        coeffs
    }

    #[inline]
    fn clamp<N: PartialOrd>(v: N, min: N, max: N) -> N {
        if v <= min {
            min
        } else if v >= max {
            max
        } else {
            v
        }
    }

    #[inline]
    fn pack_u8(mut v: f32) -> u8 {
        v = v.round();
        v = f32::min(f32::max(v, 0.0), 255.0);
        v as u8
    }

    #[inline]
    fn clear_accum(&mut self) {
        for v in self.accum.iter_mut() {
            *v = 0.0;
        }
    }

    // Resample W1xH1 to W1xH2.
    fn sample_rows(&mut self, src: &[u8]) {
        let ncomp = self.pix_fmt.get_ncomponents();
        let mut offset = 0;
        for x1 in 0..self.w1 {
            for y2 in 0..self.h2 {
                self.clear_accum();
                let ref line = self.coeffs_h[y2];
                for (i, coeff) in line.data.iter().enumerate() {
                    let y0 = line.left + i;
                    let base = (y0 * self.w1 + x1) * ncomp;
                    for n in 0..ncomp {
                        let p = src[base + n] as f32;
                        self.accum[n] += p * coeff;
                    }
                }
                for &v in &self.accum {
                    self.tmp[offset] = v;
                    offset += 1;
                }
            }
        }
    }

    // Resample W1xH2 to W2xH2.
    fn sample_cols(&mut self, dst: &mut [u8]) {
        let ncomp = self.pix_fmt.get_ncomponents();
        let mut offset = 0;
        for y2 in 0..self.h2 {
            for x2 in 0..self.w2 {
                self.clear_accum();
                let ref line = self.coeffs_w[x2];
                for (i, coeff) in line.data.iter().enumerate() {
                    let x0 = line.left + i;
                    let base = (x0 * self.h2 + y2) * ncomp;
                    for n in 0..ncomp {
                        let p = self.tmp[base + n];
                        self.accum[n] += p * coeff;
                    }
                }
                for &v in &self.accum {
                    dst[offset] = Self::pack_u8(v);
                    offset += 1;
                }
            }
        }
    }

    /// Resize `src` image data into `dst`.
    pub fn resize(&mut self, src: &[u8], dst: &mut [u8]) {
        // TODO(Kagami):
        // * Multi-thread
        // * Bound checkings
        // * SIMD
        assert_eq!(src.len(), self.w1 * self.h1 * self.pix_fmt.get_size());
        assert_eq!(dst.len(), self.w2 * self.h2 * self.pix_fmt.get_size());
        self.sample_rows(src);
        self.sample_cols(dst)
    }
}

/// Create a new resizer instance. Alias for `Resizer::new`.
pub fn new(w1: usize, h1: usize, w2: usize, h2: usize, p: Pixel, t: Type) -> Resizer {
    Resizer::new(w1, h1, w2, h2, p, t)
}

/// Resize image data to the new dimension in a single step.
///
/// **NOTE:** If you need to resize to the same dimension multiple times,
/// consider creating an resizer instance since it's faster.
pub fn resize(
    w1: usize, h1: usize, w2: usize, h2: usize,
    p: Pixel, t: Type,
    src: &[u8], dst: &mut [u8],
) {
    Resizer::new(w1, h1, w2, h2, p, t).resize(src, dst)
}
<|endoftext|>