<commit_before>use {Error, Errno, Result, NixPath};
use libc::{self, c_int, c_uint, c_char, size_t, ssize_t};
use sys::stat::Mode;
use std::os::unix::io::RawFd;
use std::ffi::OsStr;
use std::os::unix::ffi::OsStrExt;

#[cfg(any(target_os = "linux", target_os = "android"))]
use sys::uio::IoVec;  // For vmsplice

pub use self::consts::*;

// TODO: The remainder of the ffi module should be removed afer work on
// https://github.com/rust-lang/libc/issues/235 is resolved.
#[allow(dead_code)]
mod ffi {
    use libc::c_int;

    pub const F_ADD_SEALS: c_int = 1033;
    pub const F_GET_SEALS: c_int = 1034;
}

#[cfg(not(any(target_os = "ios", target_os = "macos")))]
libc_bitflags!{
    pub flags AtFlags: c_int {
        AT_SYMLINK_NOFOLLOW,
        #[cfg(any(target_os = "linux", target_os = "android"))]
        AT_NO_AUTOMOUNT,
        #[cfg(any(target_os = "linux", target_os = "android"))]
        AT_EMPTY_PATH
    }
}

#[cfg(any(target_os = "ios", target_os = "macos"))]
bitflags!(
    pub struct AtFlags: c_int {
        // hack because bitflags require one entry
        const EMPTY = 0x0;
    }
);

pub fn open<P: ?Sized + NixPath>(path: &P, oflag: OFlag, mode: Mode) -> Result<RawFd> {
    let fd = try!(path.with_nix_path(|cstr| {
        unsafe { libc::open(cstr.as_ptr(), oflag.bits(), mode.bits() as c_uint) }
    }));

    Errno::result(fd)
}

pub fn openat<P: ?Sized + NixPath>(dirfd: RawFd, path: &P, oflag: OFlag, mode: Mode) -> Result<RawFd> {
    let fd = try!(path.with_nix_path(|cstr| {
        unsafe { libc::openat(dirfd, cstr.as_ptr(), oflag.bits(), mode.bits() as c_uint) }
    }));
    Errno::result(fd)
}

fn wrap_readlink_result<'a>(buffer: &'a mut[u8], res: ssize_t)
  -> Result<&'a OsStr> {
    match Errno::result(res) {
        Err(err) => Err(err),
        Ok(len) => {
            if (len as usize) >= buffer.len() {
                Err(Error::Sys(Errno::ENAMETOOLONG))
            } else {
                Ok(OsStr::from_bytes(&buffer[..(len as usize)]))
            }
        }
    }
}

pub fn readlink<'a, P: ?Sized + NixPath>(path: &P, buffer: &'a mut [u8]) -> Result<&'a OsStr> {
    let res = try!(path.with_nix_path(|cstr| {
        unsafe { libc::readlink(cstr.as_ptr(), buffer.as_mut_ptr() as *mut c_char, buffer.len() as size_t) }
    }));

    wrap_readlink_result(buffer, res)
}

pub fn readlinkat<'a, P: ?Sized + NixPath>(dirfd: RawFd, path: &P, buffer: &'a mut [u8]) -> Result<&'a OsStr> {
    let res = try!(path.with_nix_path(|cstr| {
        unsafe { libc::readlinkat(dirfd, cstr.as_ptr(), buffer.as_mut_ptr() as *mut c_char, buffer.len() as size_t) }
    }));

    wrap_readlink_result(buffer, res)
}

pub enum FcntlArg<'a> {
    F_DUPFD(RawFd),
    F_DUPFD_CLOEXEC(RawFd),
    F_GETFD,
    F_SETFD(FdFlag), // FD_FLAGS
    F_GETFL,
    F_SETFL(OFlag), // O_NONBLOCK
    F_SETLK(&'a libc::flock),
    F_SETLKW(&'a libc::flock),
    F_GETLK(&'a mut libc::flock),
    #[cfg(any(target_os = "linux", target_os = "android"))]
    F_OFD_SETLK(&'a libc::flock),
    #[cfg(any(target_os = "linux", target_os = "android"))]
    F_OFD_SETLKW(&'a libc::flock),
    #[cfg(any(target_os = "linux", target_os = "android"))]
    F_OFD_GETLK(&'a mut libc::flock),
    #[cfg(target_os = "linux")]
    F_ADD_SEALS(SealFlag),
    #[cfg(target_os = "linux")]
    F_GET_SEALS,
    #[cfg(any(target_os = "macos", target_os = "ios"))]
    F_FULLFSYNC,
    #[cfg(any(target_os = "linux", target_os = "android"))]
    F_GETPIPE_SZ,
    #[cfg(any(target_os = "linux", target_os = "android"))]
    F_SETPIPE_SZ(libc::c_int),

    // TODO: Rest of flags
}
pub use self::FcntlArg::*;

// TODO: Figure out how to handle value fcntl returns
pub fn fcntl(fd: RawFd, arg: FcntlArg) -> Result<c_int> {
    let res = unsafe {
        match arg {
            F_DUPFD(rawfd) => libc::fcntl(fd, libc::F_DUPFD, rawfd),
            F_DUPFD_CLOEXEC(rawfd) => libc::fcntl(fd, libc::F_DUPFD_CLOEXEC, rawfd),
            F_GETFD => libc::fcntl(fd, libc::F_GETFD),
            F_SETFD(flag) => libc::fcntl(fd, libc::F_SETFD, flag.bits()),
            F_GETFL => libc::fcntl(fd, libc::F_GETFL),
            F_SETFL(flag) => libc::fcntl(fd, libc::F_SETFL, flag.bits()),
            F_SETLK(flock) => libc::fcntl(fd, libc::F_SETLK, flock),
            F_SETLKW(flock) => libc::fcntl(fd, libc::F_SETLKW, flock),
            F_GETLK(flock) => libc::fcntl(fd, libc::F_GETLK, flock),
            #[cfg(target_os = "linux")]
            F_ADD_SEALS(flag) => libc::fcntl(fd, ffi::F_ADD_SEALS, flag.bits()),
            #[cfg(target_os = "linux")]
            F_GET_SEALS => libc::fcntl(fd, ffi::F_GET_SEALS),
            #[cfg(any(target_os = "macos", target_os = "ios"))]
            F_FULLFSYNC => libc::fcntl(fd, libc::F_FULLFSYNC),
            #[cfg(any(target_os = "linux", target_os = "android"))]
            F_GETPIPE_SZ => libc::fcntl(fd, libc::F_GETPIPE_SZ),
            #[cfg(any(target_os = "linux", target_os = "android"))]
            F_SETPIPE_SZ(size) => libc::fcntl(fd, libc::F_SETPIPE_SZ, size),
            #[cfg(any(target_os = "linux", target_os = "android"))]
            _ => unimplemented!()
        }
    };

    Errno::result(res)
}

pub enum FlockArg {
    LockShared,
    LockExclusive,
    Unlock,
    LockSharedNonblock,
    LockExclusiveNonblock,
    UnlockNonblock,
}

pub fn flock(fd: RawFd, arg: FlockArg) -> Result<()> {
    use self::FlockArg::*;

    let res = unsafe {
        match arg {
            LockShared => libc::flock(fd, libc::LOCK_SH),
            LockExclusive => libc::flock(fd, libc::LOCK_EX),
            Unlock => libc::flock(fd, libc::LOCK_UN),
            LockSharedNonblock => libc::flock(fd, libc::LOCK_SH | libc::LOCK_NB),
            LockExclusiveNonblock => libc::flock(fd, libc::LOCK_EX | libc::LOCK_NB),
            UnlockNonblock => libc::flock(fd, libc::LOCK_UN | libc::LOCK_NB),
        }
    };

    Errno::result(res).map(drop)
}

#[cfg(any(target_os = "linux", target_os = "android"))]
pub fn splice(fd_in: RawFd, off_in: Option<&mut libc::loff_t>,
          fd_out: RawFd, off_out: Option<&mut libc::loff_t>,
          len: usize, flags: SpliceFFlags) -> Result<usize> {
    use std::ptr;
    let off_in = off_in.map(|offset| offset as *mut _).unwrap_or(ptr::null_mut());
    let off_out = off_out.map(|offset| offset as *mut _).unwrap_or(ptr::null_mut());

    let ret = unsafe { libc::splice(fd_in, off_in, fd_out, off_out, len, flags.bits()) };
    Errno::result(ret).map(|r| r as usize)
}

#[cfg(any(target_os = "linux", target_os = "android"))]
pub fn tee(fd_in: RawFd, fd_out: RawFd, len: usize, flags: SpliceFFlags) -> Result<usize> {
    let ret = unsafe { libc::tee(fd_in, fd_out, len, flags.bits()) };
    Errno::result(ret).map(|r| r as usize)
}

#[cfg(any(target_os = "linux", target_os = "android"))]
pub fn vmsplice(fd: RawFd, iov: &[IoVec<&[u8]>], flags: SpliceFFlags) -> Result<usize> {
    let ret = unsafe {
        libc::vmsplice(fd, iov.as_ptr() as *const libc::iovec, iov.len(), flags.bits())
    };
    Errno::result(ret).map(|r| r as usize)
}

#[cfg(any(target_os = "linux", target_os = "android"))]
mod consts {
    use libc::{self, c_int, c_uint};

    libc_bitflags! {
        pub flags SpliceFFlags: c_uint {
            SPLICE_F_MOVE,
            SPLICE_F_NONBLOCK,
            SPLICE_F_MORE,
            SPLICE_F_GIFT,
        }
    }

    bitflags!(
        pub struct OFlag: c_int {
            const O_ACCMODE   = libc::O_ACCMODE;
            const O_RDONLY    = libc::O_RDONLY;
            const O_WRONLY    = libc::O_WRONLY;
            const O_RDWR      = libc::O_RDWR;
            const O_CREAT     = libc::O_CREAT;
            const O_EXCL      = libc::O_EXCL;
            const O_NOCTTY    = libc::O_NOCTTY;
            const O_TRUNC     = libc::O_TRUNC;
            const O_APPEND    = libc::O_APPEND;
            const O_NONBLOCK  = libc::O_NONBLOCK;
            const O_DSYNC     = libc::O_DSYNC;
            const O_DIRECT    = libc::O_DIRECT;
            const O_LARGEFILE = 0o00100000;
            const O_DIRECTORY = libc::O_DIRECTORY;
            const O_NOFOLLOW  = libc::O_NOFOLLOW;
            const O_NOATIME   = 0o01000000;
            const O_CLOEXEC   = libc::O_CLOEXEC;
            const O_SYNC      = libc::O_SYNC;
            const O_PATH      = 0o10000000;
            const O_TMPFILE   = libc::O_TMPFILE;
            const O_NDELAY    = libc::O_NDELAY;
        }
    );

    libc_bitflags!(
        pub flags FdFlag: c_int {
            FD_CLOEXEC
        }
    );

    bitflags!(
        pub struct SealFlag: c_int {
            const F_SEAL_SEAL = 1;
            const F_SEAL_SHRINK = 2;
            const F_SEAL_GROW = 4;
            const F_SEAL_WRITE = 8;
        }
    );

}

#[cfg(any(target_os = "netbsd", target_os = "dragonfly", target_os = "openbsd", target_os = "freebsd", target_os = "macos", target_os = "ios"))]
mod consts {
    use libc::{self,c_int};

    libc_bitflags!(
        pub flags OFlag: c_int {
            O_ACCMODE,
            O_RDONLY,
            O_WRONLY,
            O_RDWR,
            O_NONBLOCK,
            O_APPEND,
            O_SHLOCK,
            O_EXLOCK,
            O_ASYNC,
            O_SYNC,
            O_NOFOLLOW,
            O_CREAT,
            O_TRUNC,
            O_EXCL,
            O_NOCTTY,
            O_DIRECTORY,
            O_CLOEXEC,
            O_FSYNC,
            O_NDELAY,
            #[cfg(any(target_os = "netbsd", target_os = "openbsd", target_os = "macos", target_os = "ios"))]
            O_DSYNC,
            #[cfg(any(target_os = "netbsd", target_os = "dragonfly", target_os = "freebsd"))]
            O_DIRECT,
            #[cfg(any(target_os = "netbsd", target_os = "openbsd"))]
            O_RSYNC,
            #[cfg(target_os = "freebsd")]
            O_EXEC,
            #[cfg(target_os = "freebsd")]
            O_TTY_INIT,
            #[cfg(target_os = "netbsd")]
            O_ALT_IO,
            #[cfg(target_os = "netbsd")]
            O_NOSIGPIPE,
            #[cfg(target_os = "netbsd")]
            O_SEARCH,
        }
    );

    libc_bitflags!(
        pub flags FdFlag: c_int {
            FD_CLOEXEC
        }
    );
}
<commit_msg>Wrap lines to keep them under 100 chars<commit_after>use {Error, Errno, Result, NixPath};
use libc::{self, c_int, c_uint, c_char, size_t, ssize_t};
use sys::stat::Mode;
use std::os::unix::io::RawFd;
use std::ffi::OsStr;
use std::os::unix::ffi::OsStrExt;

#[cfg(any(target_os = "linux", target_os = "android"))]
use sys::uio::IoVec;  // For vmsplice

pub use self::consts::*;

// TODO: The remainder of the ffi module should be removed afer work on
// https://github.com/rust-lang/libc/issues/235 is resolved.
#[allow(dead_code)]
mod ffi {
    use libc::c_int;

    pub const F_ADD_SEALS: c_int = 1033;
    pub const F_GET_SEALS: c_int = 1034;
}

#[cfg(not(any(target_os = "ios", target_os = "macos")))]
libc_bitflags!{
    pub flags AtFlags: c_int {
        AT_SYMLINK_NOFOLLOW,
        #[cfg(any(target_os = "linux", target_os = "android"))]
        AT_NO_AUTOMOUNT,
        #[cfg(any(target_os = "linux", target_os = "android"))]
        AT_EMPTY_PATH
    }
}

#[cfg(any(target_os = "ios", target_os = "macos"))]
bitflags!(
    pub struct AtFlags: c_int {
        // hack because bitflags require one entry
        const EMPTY = 0x0;
    }
);

pub fn open<P: ?Sized + NixPath>(path: &P, oflag: OFlag, mode: Mode) -> Result<RawFd> {
    let fd = try!(path.with_nix_path(|cstr| {
        unsafe { libc::open(cstr.as_ptr(), oflag.bits(), mode.bits() as c_uint) }
    }));

    Errno::result(fd)
}

pub fn openat<P: ?Sized + NixPath>(dirfd: RawFd, path: &P, oflag: OFlag, mode: Mode) -> Result<RawFd> {
    let fd = try!(path.with_nix_path(|cstr| {
        unsafe { libc::openat(dirfd, cstr.as_ptr(), oflag.bits(), mode.bits() as c_uint) }
    }));
    Errno::result(fd)
}

fn wrap_readlink_result<'a>(buffer: &'a mut[u8], res: ssize_t)
  -> Result<&'a OsStr> {
    match Errno::result(res) {
        Err(err) => Err(err),
        Ok(len) => {
            if (len as usize) >= buffer.len() {
                Err(Error::Sys(Errno::ENAMETOOLONG))
            } else {
                Ok(OsStr::from_bytes(&buffer[..(len as usize)]))
            }
        }
    }
}

pub fn readlink<'a, P: ?Sized + NixPath>(path: &P, buffer: &'a mut [u8]) -> Result<&'a OsStr> {
    let res = try!(path.with_nix_path(|cstr| {
        unsafe { libc::readlink(cstr.as_ptr(), buffer.as_mut_ptr() as *mut c_char, buffer.len() as size_t) }
    }));

    wrap_readlink_result(buffer, res)
}

pub fn readlinkat<'a, P: ?Sized + NixPath>(dirfd: RawFd, path: &P, buffer: &'a mut [u8]) -> Result<&'a OsStr> {
    let res = try!(path.with_nix_path(|cstr| {
        unsafe { libc::readlinkat(dirfd, cstr.as_ptr(), buffer.as_mut_ptr() as *mut c_char, buffer.len() as size_t) }
    }));

    wrap_readlink_result(buffer, res)
}

pub enum FcntlArg<'a> {
    F_DUPFD(RawFd),
    F_DUPFD_CLOEXEC(RawFd),
    F_GETFD,
    F_SETFD(FdFlag), // FD_FLAGS
    F_GETFL,
    F_SETFL(OFlag), // O_NONBLOCK
    F_SETLK(&'a libc::flock),
    F_SETLKW(&'a libc::flock),
    F_GETLK(&'a mut libc::flock),
    #[cfg(any(target_os = "linux", target_os = "android"))]
    F_OFD_SETLK(&'a libc::flock),
    #[cfg(any(target_os = "linux", target_os = "android"))]
    F_OFD_SETLKW(&'a libc::flock),
    #[cfg(any(target_os = "linux", target_os = "android"))]
    F_OFD_GETLK(&'a mut libc::flock),
    #[cfg(target_os = "linux")]
    F_ADD_SEALS(SealFlag),
    #[cfg(target_os = "linux")]
    F_GET_SEALS,
    #[cfg(any(target_os = "macos", target_os = "ios"))]
    F_FULLFSYNC,
    #[cfg(any(target_os = "linux", target_os = "android"))]
    F_GETPIPE_SZ,
    #[cfg(any(target_os = "linux", target_os = "android"))]
    F_SETPIPE_SZ(libc::c_int),

    // TODO: Rest of flags
}
pub use self::FcntlArg::*;

// TODO: Figure out how to handle value fcntl returns
pub fn fcntl(fd: RawFd, arg: FcntlArg) -> Result<c_int> {
    let res = unsafe {
        match arg {
            F_DUPFD(rawfd) => libc::fcntl(fd, libc::F_DUPFD, rawfd),
            F_DUPFD_CLOEXEC(rawfd) => libc::fcntl(fd, libc::F_DUPFD_CLOEXEC, rawfd),
            F_GETFD => libc::fcntl(fd, libc::F_GETFD),
            F_SETFD(flag) => libc::fcntl(fd, libc::F_SETFD, flag.bits()),
            F_GETFL => libc::fcntl(fd, libc::F_GETFL),
            F_SETFL(flag) => libc::fcntl(fd, libc::F_SETFL, flag.bits()),
            F_SETLK(flock) => libc::fcntl(fd, libc::F_SETLK, flock),
            F_SETLKW(flock) => libc::fcntl(fd, libc::F_SETLKW, flock),
            F_GETLK(flock) => libc::fcntl(fd, libc::F_GETLK, flock),
            #[cfg(target_os = "linux")]
            F_ADD_SEALS(flag) => libc::fcntl(fd, ffi::F_ADD_SEALS, flag.bits()),
            #[cfg(target_os = "linux")]
            F_GET_SEALS => libc::fcntl(fd, ffi::F_GET_SEALS),
            #[cfg(any(target_os = "macos", target_os = "ios"))]
            F_FULLFSYNC => libc::fcntl(fd, libc::F_FULLFSYNC),
            #[cfg(any(target_os = "linux", target_os = "android"))]
            F_GETPIPE_SZ => libc::fcntl(fd, libc::F_GETPIPE_SZ),
            #[cfg(any(target_os = "linux", target_os = "android"))]
            F_SETPIPE_SZ(size) => libc::fcntl(fd, libc::F_SETPIPE_SZ, size),
            #[cfg(any(target_os = "linux", target_os = "android"))]
            _ => unimplemented!()
        }
    };

    Errno::result(res)
}

pub enum FlockArg {
    LockShared,
    LockExclusive,
    Unlock,
    LockSharedNonblock,
    LockExclusiveNonblock,
    UnlockNonblock,
}

pub fn flock(fd: RawFd, arg: FlockArg) -> Result<()> {
    use self::FlockArg::*;

    let res = unsafe {
        match arg {
            LockShared => libc::flock(fd, libc::LOCK_SH),
            LockExclusive => libc::flock(fd, libc::LOCK_EX),
            Unlock => libc::flock(fd, libc::LOCK_UN),
            LockSharedNonblock => libc::flock(fd, libc::LOCK_SH | libc::LOCK_NB),
            LockExclusiveNonblock => libc::flock(fd, libc::LOCK_EX | libc::LOCK_NB),
            UnlockNonblock => libc::flock(fd, libc::LOCK_UN | libc::LOCK_NB),
        }
    };

    Errno::result(res).map(drop)
}

#[cfg(any(target_os = "linux", target_os = "android"))]
pub fn splice(fd_in: RawFd, off_in: Option<&mut libc::loff_t>,
          fd_out: RawFd, off_out: Option<&mut libc::loff_t>,
          len: usize, flags: SpliceFFlags) -> Result<usize> {
    use std::ptr;
    let off_in = off_in.map(|offset| offset as *mut _).unwrap_or(ptr::null_mut());
    let off_out = off_out.map(|offset| offset as *mut _).unwrap_or(ptr::null_mut());

    let ret = unsafe { libc::splice(fd_in, off_in, fd_out, off_out, len, flags.bits()) };
    Errno::result(ret).map(|r| r as usize)
}

#[cfg(any(target_os = "linux", target_os = "android"))]
pub fn tee(fd_in: RawFd, fd_out: RawFd, len: usize, flags: SpliceFFlags) -> Result<usize> {
    let ret = unsafe { libc::tee(fd_in, fd_out, len, flags.bits()) };
    Errno::result(ret).map(|r| r as usize)
}

#[cfg(any(target_os = "linux", target_os = "android"))]
pub fn vmsplice(fd: RawFd, iov: &[IoVec<&[u8]>], flags: SpliceFFlags) -> Result<usize> {
    let ret = unsafe {
        libc::vmsplice(fd, iov.as_ptr() as *const libc::iovec, iov.len(), flags.bits())
    };
    Errno::result(ret).map(|r| r as usize)
}

#[cfg(any(target_os = "linux", target_os = "android"))]
mod consts {
    use libc::{self, c_int, c_uint};

    libc_bitflags! {
        pub flags SpliceFFlags: c_uint {
            SPLICE_F_MOVE,
            SPLICE_F_NONBLOCK,
            SPLICE_F_MORE,
            SPLICE_F_GIFT,
        }
    }

    bitflags!(
        pub struct OFlag: c_int {
            const O_ACCMODE   = libc::O_ACCMODE;
            const O_RDONLY    = libc::O_RDONLY;
            const O_WRONLY    = libc::O_WRONLY;
            const O_RDWR      = libc::O_RDWR;
            const O_CREAT     = libc::O_CREAT;
            const O_EXCL      = libc::O_EXCL;
            const O_NOCTTY    = libc::O_NOCTTY;
            const O_TRUNC     = libc::O_TRUNC;
            const O_APPEND    = libc::O_APPEND;
            const O_NONBLOCK  = libc::O_NONBLOCK;
            const O_DSYNC     = libc::O_DSYNC;
            const O_DIRECT    = libc::O_DIRECT;
            const O_LARGEFILE = 0o00100000;
            const O_DIRECTORY = libc::O_DIRECTORY;
            const O_NOFOLLOW  = libc::O_NOFOLLOW;
            const O_NOATIME   = 0o01000000;
            const O_CLOEXEC   = libc::O_CLOEXEC;
            const O_SYNC      = libc::O_SYNC;
            const O_PATH      = 0o10000000;
            const O_TMPFILE   = libc::O_TMPFILE;
            const O_NDELAY    = libc::O_NDELAY;
        }
    );

    libc_bitflags!(
        pub flags FdFlag: c_int {
            FD_CLOEXEC
        }
    );

    bitflags!(
        pub struct SealFlag: c_int {
            const F_SEAL_SEAL = 1;
            const F_SEAL_SHRINK = 2;
            const F_SEAL_GROW = 4;
            const F_SEAL_WRITE = 8;
        }
    );

}

#[cfg(any(target_os = "netbsd", target_os = "dragonfly", target_os = "openbsd",
          target_os = "freebsd", target_os = "macos", target_os = "ios"))]
mod consts {
    use libc::{self,c_int};

    libc_bitflags!(
        pub flags OFlag: c_int {
            O_ACCMODE,
            O_RDONLY,
            O_WRONLY,
            O_RDWR,
            O_NONBLOCK,
            O_APPEND,
            O_SHLOCK,
            O_EXLOCK,
            O_ASYNC,
            O_SYNC,
            O_NOFOLLOW,
            O_CREAT,
            O_TRUNC,
            O_EXCL,
            O_NOCTTY,
            O_DIRECTORY,
            O_CLOEXEC,
            O_FSYNC,
            O_NDELAY,
            #[cfg(any(target_os = "netbsd", target_os = "openbsd", target_os = "macos",
                      target_os = "ios"))]
            O_DSYNC,
            #[cfg(any(target_os = "netbsd", target_os = "dragonfly", target_os = "freebsd"))]
            O_DIRECT,
            #[cfg(any(target_os = "netbsd", target_os = "openbsd"))]
            O_RSYNC,
            #[cfg(target_os = "freebsd")]
            O_EXEC,
            #[cfg(target_os = "freebsd")]
            O_TTY_INIT,
            #[cfg(target_os = "netbsd")]
            O_ALT_IO,
            #[cfg(target_os = "netbsd")]
            O_NOSIGPIPE,
            #[cfg(target_os = "netbsd")]
            O_SEARCH,
        }
    );

    libc_bitflags!(
        pub flags FdFlag: c_int {
            FD_CLOEXEC
        }
    );
}
<|endoftext|>


<commit_before>use ::*;

#[derive(Copy, Clone)]
pub struct Tile {
    pub wall : Option<char>,
}

pub struct Floor {
    pub rows: usize,
    pub cols: usize,
    pub tiles: Vec<Vec<Tile>>
}

impl Floor {
    pub fn new(cols : usize, rows : usize) -> Self {
        // Tiles to use
        let horizontal   = Tile { wall : Some('─') }; // U-2500
        let vertical     = Tile { wall : Some('│') }; // U-2502
        let top_left     = Tile { wall : Some('┌') }; // U-250c
        let top_right    = Tile { wall : Some('┐') }; // U-2510
        let bottom_left  = Tile { wall : Some('└') }; // U-2514
        let bottom_right = Tile { wall : Some('┘') }; // U-2518
        let blank        = Tile { wall : None };

        // Create tiles 2D Vec and populate with empty rows
        let mut tiles = Vec::<Vec<Tile>>::with_capacity(rows);
        for _ in 0..rows {
            tiles.push(Vec::<Tile>::with_capacity(rows));
        }
        // First row is all wall
        tiles[0].push(top_left);
        for _ in 1..cols-1 {
            tiles[0].push(horizontal);
        }
        tiles[0].push(top_right);
        // Middle rows are vertical sides and blank middles
        for row in 1..rows-1 {
            tiles[row].push(vertical);
            for _ in 1..cols-1 {
                tiles[row].push(blank);
            }
            tiles[row].push(vertical);
        }
        // Bottom row is also all wall
        tiles[rows-1].push(bottom_left);
        for _ in 1..cols-1 {
            tiles[rows-1].push(horizontal);
        }
        tiles[rows-1].push(bottom_right);

        // Create the floor
        Self {
            rows: rows,
            cols: cols,
            tiles: tiles,
        }
    }
    pub fn get_symbol(&self, coord : &Coord) -> String {
        if let Some(wall) = self.tiles[coord.row as usize][coord.col as usize].wall {
            wall.to_string()
        } else {
            " ".to_string()
        }
    }
    pub fn is_wall(&self, coord : &Coord) -> bool {
        self.tiles[coord.row as usize][coord.col as usize].wall.is_some()
    }
}
<commit_msg>Be consistent with spacing around colons<commit_after>use ::*;

#[derive(Copy, Clone)]
pub struct Tile {
    pub wall : Option<char>,
}

pub struct Floor {
    pub rows : usize,
    pub cols : usize,
    pub tiles : Vec<Vec<Tile>>
}

impl Floor {
    pub fn new(cols : usize, rows : usize) -> Self {
        // Tiles to use
        let horizontal   = Tile { wall : Some('─') }; // U-2500
        let vertical     = Tile { wall : Some('│') }; // U-2502
        let top_left     = Tile { wall : Some('┌') }; // U-250c
        let top_right    = Tile { wall : Some('┐') }; // U-2510
        let bottom_left  = Tile { wall : Some('└') }; // U-2514
        let bottom_right = Tile { wall : Some('┘') }; // U-2518
        let blank        = Tile { wall : None };

        // Create tiles 2D Vec and populate with empty rows
        let mut tiles = Vec::<Vec<Tile>>::with_capacity(rows);
        for _ in 0..rows {
            tiles.push(Vec::<Tile>::with_capacity(rows));
        }
        // First row is all wall
        tiles[0].push(top_left);
        for _ in 1..cols-1 {
            tiles[0].push(horizontal);
        }
        tiles[0].push(top_right);
        // Middle rows are vertical sides and blank middles
        for row in 1..rows-1 {
            tiles[row].push(vertical);
            for _ in 1..cols-1 {
                tiles[row].push(blank);
            }
            tiles[row].push(vertical);
        }
        // Bottom row is also all wall
        tiles[rows-1].push(bottom_left);
        for _ in 1..cols-1 {
            tiles[rows-1].push(horizontal);
        }
        tiles[rows-1].push(bottom_right);

        // Create the floor
        Self {
            rows : rows,
            cols : cols,
            tiles : tiles,
        }
    }
    pub fn get_symbol(&self, coord : &Coord) -> String {
        if let Some(wall) = self.tiles[coord.row as usize][coord.col as usize].wall {
            wall.to_string()
        } else {
            " ".to_string()
        }
    }
    pub fn is_wall(&self, coord : &Coord) -> bool {
        self.tiles[coord.row as usize][coord.col as usize].wall.is_some()
    }
}
<|endoftext|>


<commit_before>use std::collections::{HashMap, HashSet, VecDeque};
use std::hash::Hash;

use crate::colour::Colour;
use crate::grid::Grid;

// Represent the state of the game as a graph.  One node for each cell.  There is an edge between
// two cells if and only if they are the same colour and have a distance of 1 according to the
// Manhattan metric.
//
// The game has only one real operation (i.e. not counting level generation): Switching the colour
// of the connected component containing the top left cell to whichever colour the clicked cell
// has.  This results in said component growing by every component it neighbours which has its new
// colour.  In essence, the aim of the gaim is to join connected components until only one is
// left.  Fewer clicks is better.
//
// So, we represent the game state as a collection of connected components, each of which has a
// colour, a list of coordinates of the constituent cells, and a list of it s neighbours.  As we
// do all our operations on connected components, we actually work not with the graph of
// neighbouring cells but with a graph of connected components.  The graph of connected components
// is made up of one node representing each connected component and an edge between any two
// connected components that have cells which share an edge, i.e. two cells which would have been
// connected by an edge in the original graph if they had had the same colour (which they did
// not).

#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub struct Position {
    pub row: usize,
    pub column: usize,
}

#[derive(Debug, Clone)]
pub struct ConnectedComponent {
    id: usize,
    pub colour: Colour,
    pub cells: HashSet<Position>,
}

impl PartialEq for ConnectedComponent {
    fn eq(&self, other: &Self) -> bool {
        self.id == other.id
    }
}

impl Eq for ConnectedComponent {}

impl Hash for ConnectedComponent {
    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
        self.id.hash(state);
    }
}

#[derive(Debug)]
pub struct Graph {
    pub components: HashMap<usize, ConnectedComponent>,
    pub neighbours: HashMap<usize, HashSet<usize>>,
}

fn find_connected_components(grid: &Grid) -> Vec<(ConnectedComponent, HashSet<usize>)> {
    let rows = grid.number_of_rows;
    let columns = grid.number_of_columns;

    let mut remaining_cells: HashSet<usize> = (0..grid.cells.len()).into_iter().collect();

    let mut components = vec![];
    let mut counter = 0;

    // Take any element to land in any component.
    while let Some(&start_cell_index) = remaining_cells.iter().next() {
        let mut visited = HashSet::new();
        let mut neighbours = HashSet::new();
        let mut queue = VecDeque::new();

        queue.push_back(start_cell_index);

        // Gather all cells in the connected component of start_cell_index.
        while let Some(i) = queue.pop_front() {
            visited.insert(i);
            remaining_cells.remove(&i);

            let mut neighbouring_cells = vec![];
            if i % columns > 0 {
                neighbouring_cells.push(i - 1);
            }
            if i % columns < columns - 1 {
                neighbouring_cells.push(i + 1);
            }
            if i >= columns {
                neighbouring_cells.push(i - columns)
            }
            if i < (rows - 1) * columns {
                neighbouring_cells.push(i + columns)
            }

            for neighbour in neighbouring_cells {
                if visited.contains(&neighbour) {
                    continue;
                }

                if grid.cells[i] == grid.cells[neighbour] {
                    queue.push_back(neighbour);
                } else {
                    neighbours.insert(neighbour);
                }
            }
        }

        let cells = visited
            .iter()
            .map(|&i| Position {
                column: i % columns,
                row: i / columns,
            })
            .collect();

        let component = ConnectedComponent {
            id: counter,
            colour: grid.cells[*visited.iter().next().unwrap()],
            cells,
        };
        counter += 1;

        components.push((component, neighbours));
    }

    return components;
}

impl Graph {
    pub fn create(grid: &Grid) -> Self {
        let components_and_neighbours = find_connected_components(&grid);

        let map_cell_to_component: HashMap<Position, ConnectedComponent> =
            components_and_neighbours
                .iter()
                .flat_map(|(component, _)| {
                    component
                        .cells
                        .iter()
                        .map(|cell| (cell.clone(), component.clone()))
                        .collect::<Vec<(Position, ConnectedComponent)>>()
                })
                .collect();

        let mut neighbours: HashMap<usize, HashSet<usize>> = HashMap::new();

        for (component, neighbour_cells) in &components_and_neighbours {
            let mut neighbour_components = HashSet::new();

            for cell in neighbour_cells {
                let position = Position {
                    column: cell % grid.number_of_columns,
                    row: cell / grid.number_of_columns,
                };
                neighbour_components.insert(map_cell_to_component[&position].id);
            }
            neighbours.insert(component.id, neighbour_components);
        }

        let mut components = HashMap::with_capacity(components_and_neighbours.len());

        for (c, n) in components_and_neighbours.into_iter() {
            components.insert(c.id, c);
        }

        Self {
            components,
            neighbours
        }
    }

    fn to_grid(&self, rows: usize, columns: usize) -> Grid {
        let mut cells = vec![Colour::Red; rows * columns];

        for component in self.neighbours.keys() {
            for position in &self.components[component].cells {
                cells[position.column + position.row * columns] = self.components[component].colour;
            }
        }

        Grid {
            number_of_rows: rows,
            number_of_columns: columns,
            cells,
        }
    }

    pub fn find_component(&self, position: &Position) -> &ConnectedComponent {
        for component in self.neighbours.keys() {
            if self.components[component].cells.contains(&position) {
                return &self.components[component];
            }
        }

        unreachable!()
    }
}

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

    #[test]
    fn should_return_same_grid_as_input() {
        let size = 4;
        let grid = Grid::generate(size);

        let graph = Graph::create(&grid);
        let reconstituted_grid = graph.to_grid(size, size);

        assert_eq!(reconstituted_grid, grid);
    }

    #[test]
    fn should_have_one_component() {
        let grid = Grid {
            number_of_columns: 2,
            number_of_rows: 2,
            cells: vec![Colour::Red; 4],
        };

        let graph = Graph::create(&grid);

        assert_eq!(graph.neighbours.len(), 1);
    }

    #[test]
    fn should_have_four_component() {
        let grid = Grid {
            number_of_columns: 2,
            number_of_rows: 2,
            cells: vec![Colour::Red, Colour::Yellow, Colour::Yellow, Colour::Red],
        };

        let graph = Graph::create(&grid);

        assert_eq!(graph.neighbours.len(), 4);
    }

    #[test]
    fn should_have_two_component() {
        let grid = Grid {
            number_of_columns: 2,
            number_of_rows: 2,
            cells: vec![Colour::Red, Colour::Red, Colour::Yellow, Colour::Yellow],
        };

        let graph = Graph::create(&grid);

        assert_eq!(graph.neighbours.len(), 2);
    }

    #[test]
    fn should_have_same_keys_for_components_and_neighbours_maps() {
        let size = 4;
        let grid = Grid::generate(size);

        let graph = Graph::create(&grid);

        let components_keys: HashSet<_> = graph.components.keys().collect();
        let neighbours_keys: HashSet<_> = graph.neighbours.keys().collect();
        assert_eq!(components_keys, neighbours_keys);
    }

    #[test]
    fn should_contain_exactly_the_component_ids_as_keys() {
        let size = 4;
        let grid = Grid::generate(size);

        let graph = Graph::create(&grid);

        let component_keys: HashSet<_> = graph.components.keys().collect();
        let component_ids: HashSet<_> = graph.components.values().map(|x| &x.id).collect();
        assert_eq!(component_keys, component_ids);

        for (id, component) in graph.components {
            assert_eq!(id, component.id);
        }
    }
}
<commit_msg>Test that neighbours contains the correct IDs<commit_after>use std::collections::{HashMap, HashSet, VecDeque};
use std::hash::Hash;

use crate::colour::Colour;
use crate::grid::Grid;

// Represent the state of the game as a graph.  One node for each cell.  There is an edge between
// two cells if and only if they are the same colour and have a distance of 1 according to the
// Manhattan metric.
//
// The game has only one real operation (i.e. not counting level generation): Switching the colour
// of the connected component containing the top left cell to whichever colour the clicked cell
// has.  This results in said component growing by every component it neighbours which has its new
// colour.  In essence, the aim of the gaim is to join connected components until only one is
// left.  Fewer clicks is better.
//
// So, we represent the game state as a collection of connected components, each of which has a
// colour, a list of coordinates of the constituent cells, and a list of it s neighbours.  As we
// do all our operations on connected components, we actually work not with the graph of
// neighbouring cells but with a graph of connected components.  The graph of connected components
// is made up of one node representing each connected component and an edge between any two
// connected components that have cells which share an edge, i.e. two cells which would have been
// connected by an edge in the original graph if they had had the same colour (which they did
// not).

#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub struct Position {
    pub row: usize,
    pub column: usize,
}

#[derive(Debug, Clone)]
pub struct ConnectedComponent {
    id: usize,
    pub colour: Colour,
    pub cells: HashSet<Position>,
}

impl PartialEq for ConnectedComponent {
    fn eq(&self, other: &Self) -> bool {
        self.id == other.id
    }
}

impl Eq for ConnectedComponent {}

impl Hash for ConnectedComponent {
    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
        self.id.hash(state);
    }
}

#[derive(Debug)]
pub struct Graph {
    pub components: HashMap<usize, ConnectedComponent>,
    pub neighbours: HashMap<usize, HashSet<usize>>,
}

fn find_connected_components(grid: &Grid) -> Vec<(ConnectedComponent, HashSet<usize>)> {
    let rows = grid.number_of_rows;
    let columns = grid.number_of_columns;

    let mut remaining_cells: HashSet<usize> = (0..grid.cells.len()).into_iter().collect();

    let mut components = vec![];
    let mut counter = 0;

    // Take any element to land in any component.
    while let Some(&start_cell_index) = remaining_cells.iter().next() {
        let mut visited = HashSet::new();
        let mut neighbours = HashSet::new();
        let mut queue = VecDeque::new();

        queue.push_back(start_cell_index);

        // Gather all cells in the connected component of start_cell_index.
        while let Some(i) = queue.pop_front() {
            visited.insert(i);
            remaining_cells.remove(&i);

            let mut neighbouring_cells = vec![];
            if i % columns > 0 {
                neighbouring_cells.push(i - 1);
            }
            if i % columns < columns - 1 {
                neighbouring_cells.push(i + 1);
            }
            if i >= columns {
                neighbouring_cells.push(i - columns)
            }
            if i < (rows - 1) * columns {
                neighbouring_cells.push(i + columns)
            }

            for neighbour in neighbouring_cells {
                if visited.contains(&neighbour) {
                    continue;
                }

                if grid.cells[i] == grid.cells[neighbour] {
                    queue.push_back(neighbour);
                } else {
                    neighbours.insert(neighbour);
                }
            }
        }

        let cells = visited
            .iter()
            .map(|&i| Position {
                column: i % columns,
                row: i / columns,
            })
            .collect();

        let component = ConnectedComponent {
            id: counter,
            colour: grid.cells[*visited.iter().next().unwrap()],
            cells,
        };
        counter += 1;

        components.push((component, neighbours));
    }

    return components;
}

impl Graph {
    pub fn create(grid: &Grid) -> Self {
        let components_and_neighbours = find_connected_components(&grid);

        let map_cell_to_component: HashMap<Position, ConnectedComponent> =
            components_and_neighbours
                .iter()
                .flat_map(|(component, _)| {
                    component
                        .cells
                        .iter()
                        .map(|cell| (cell.clone(), component.clone()))
                        .collect::<Vec<(Position, ConnectedComponent)>>()
                })
                .collect();

        let mut neighbours: HashMap<usize, HashSet<usize>> = HashMap::new();

        for (component, neighbour_cells) in &components_and_neighbours {
            let mut neighbour_components = HashSet::new();

            for cell in neighbour_cells {
                let position = Position {
                    column: cell % grid.number_of_columns,
                    row: cell / grid.number_of_columns,
                };
                neighbour_components.insert(map_cell_to_component[&position].id);
            }
            neighbours.insert(component.id, neighbour_components);
        }

        let mut components = HashMap::with_capacity(components_and_neighbours.len());

        for (c, n) in components_and_neighbours.into_iter() {
            components.insert(c.id, c);
        }

        Self {
            components,
            neighbours
        }
    }

    fn to_grid(&self, rows: usize, columns: usize) -> Grid {
        let mut cells = vec![Colour::Red; rows * columns];

        for component in self.neighbours.keys() {
            for position in &self.components[component].cells {
                cells[position.column + position.row * columns] = self.components[component].colour;
            }
        }

        Grid {
            number_of_rows: rows,
            number_of_columns: columns,
            cells,
        }
    }

    pub fn find_component(&self, position: &Position) -> &ConnectedComponent {
        for component in self.neighbours.keys() {
            if self.components[component].cells.contains(&position) {
                return &self.components[component];
            }
        }

        unreachable!()
    }
}

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

    #[test]
    fn should_return_same_grid_as_input() {
        let size = 4;
        let grid = Grid::generate(size);

        let graph = Graph::create(&grid);
        let reconstituted_grid = graph.to_grid(size, size);

        assert_eq!(reconstituted_grid, grid);
    }

    #[test]
    fn should_have_one_component() {
        let grid = Grid {
            number_of_columns: 2,
            number_of_rows: 2,
            cells: vec![Colour::Red; 4],
        };

        let graph = Graph::create(&grid);

        assert_eq!(graph.neighbours.len(), 1);
    }

    #[test]
    fn should_have_four_component() {
        let grid = Grid {
            number_of_columns: 2,
            number_of_rows: 2,
            cells: vec![Colour::Red, Colour::Yellow, Colour::Yellow, Colour::Red],
        };

        let graph = Graph::create(&grid);

        assert_eq!(graph.neighbours.len(), 4);
    }

    #[test]
    fn should_have_two_component() {
        let grid = Grid {
            number_of_columns: 2,
            number_of_rows: 2,
            cells: vec![Colour::Red, Colour::Red, Colour::Yellow, Colour::Yellow],
        };

        let graph = Graph::create(&grid);

        assert_eq!(graph.neighbours.len(), 2);
    }

    #[test]
    fn should_have_same_keys_for_components_and_neighbours_maps() {
        let size = 4;
        let grid = Grid::generate(size);

        let graph = Graph::create(&grid);

        let components_keys: HashSet<_> = graph.components.keys().collect();
        let neighbours_keys: HashSet<_> = graph.neighbours.keys().collect();
        assert_eq!(components_keys, neighbours_keys);
    }

    #[test]
    fn should_contain_exactly_the_component_ids_as_keys() {
        let size = 4;
        let grid = Grid::generate(size);

        let graph = Graph::create(&grid);

        let component_keys: HashSet<_> = graph.components.keys().collect();
        let component_ids: HashSet<_> = graph.components.values().map(|x| &x.id).collect();
        assert_eq!(component_keys, component_ids);

        for (id, component) in graph.components {
            assert_eq!(id, component.id);
        }
    }

    #[test]
    fn should_contain_only_component_ids_as_values_in_neighbours_map() {
        let size = 4;
        let grid = Grid::generate(size);

        let graph = Graph::create(&grid);

        let component_ids: HashSet<_> = graph.components.keys().map(|&x| x).collect();

        for (_, neighbours) in graph.neighbours {
            assert_eq!(neighbours.difference(&component_ids).collect::<HashSet<_>>().len(), 0);
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use crate::database::Database;
use crate::image::Image;
use crate::stats::ScopedDuration;
use crate::vec::*;
use crate::view::View;
use crate::Stopwatch;
use crate::TileRef;
use crate::R;
use crate::{Metadata, MetadataState};
use piston_window::Transformed;
use piston_window::{DrawState, G2d, G2dTexture, G2dTextureContext, Texture, TextureSettings};
use std::cmp::Ordering;
use std::collections::{BTreeMap, VecDeque};

#[derive(Debug)]
pub struct Group {
    pub extents: [Vector2<u32>; 2],
    pub tiles: BTreeMap<TileRef, G2dTexture>,
    pub images: BTreeMap<Vector2<u32>, Image>,
    pub cache_todo: VecDeque<Vector2<u32>>,
    pub thumb_todo: VecDeque<Vector2<u32>>,
}

impl Group {
    pub fn new(extents: [Vector2<u32>; 2]) -> Self {
        Self {
            extents,
            tiles: BTreeMap::new(),
            images: BTreeMap::new(),
            cache_todo: VecDeque::new(),
            thumb_todo: VecDeque::new(),
        }
    }

    pub fn insert(&mut self, coords: Vector2<u32>, image: Image) {
        self.images.insert(coords, image);
    }

    pub fn reset(&mut self) {
        for image in self.images.values_mut() {
            image.reset();
        }
        self.tiles.clear();
        self.thumb_todo.clear();
        self.cache_todo.clear();
    }

    pub fn recheck(&mut self, view: &View) {
        self.thumb_todo.clear();
        self.cache_todo.clear();

        let mut mouse_dist: Vec<(&Vector2<u32>, &Image)> = Vec::with_capacity(self.images.len());
        mouse_dist.extend(self.images.iter());
        mouse_dist.sort_by_key(|(&coords, _)| vec2_square_len(view.mouse_dist(coords)) as isize);

        for (&coords, image) in &mouse_dist {
            match image.metadata {
                MetadataState::Some(_) => self.cache_todo.push_back(coords),
                MetadataState::Missing => self.thumb_todo.push_back(coords),
                MetadataState::Errored => continue,
            }
        }
    }

    pub fn load_cache(
        &mut self,
        view: &View,
        db: &Database,
        texture_context: &mut G2dTextureContext,
        stopwatch: &Stopwatch,
    ) -> bool {
        let _s3 = ScopedDuration::new("Group::load_cache");

        let target_size = view.target_size();

        let texture_settings = TextureSettings::new();

        for coords in self.cache_todo.pop_front() {
            let image = self.images.get_mut(&coords).unwrap();

            let metadata = image.get_metadata().expect("Image::get_metadata");

            let is_visible = view.is_visible(view.coords(image.i));

            let shift = if is_visible {
                0
            } else {
                let ratio = view.visible_ratio(view.coords(image.i));
                f64::max(0.0, ratio - 1.0).floor() as usize
            };

            let new_size = metadata.nearest(target_size >> shift);

            let current_size = image.size.unwrap_or(0);

            // Progressive resizing.
            let new_size = match new_size.cmp(&current_size) {
                Ordering::Less => current_size - 1,
                Ordering::Equal => {
                    // Already loaded target size.
                    continue;
                }
                Ordering::Greater => current_size + 1,
            };

            // Load new tiles.
            for tile_ref in &metadata.thumbs[new_size].tile_refs {
                // Already loaded.
                if self.tiles.contains_key(tile_ref) {
                    continue;
                }

                if stopwatch.done() {
                    self.cache_todo.push_front(coords);
                    return false;
                }

                let data = db.get(*tile_ref).expect("db get").expect("missing tile");

                let image = ::image::load_from_memory(&data).expect("load image");

                // TODO: Would be great to move off thread.
                let image =
                    Texture::from_image(texture_context, &image.to_rgba(), &texture_settings)
                        .expect("texture");

                self.tiles.insert(*tile_ref, image);
            }

            // Unload old tiles.
            for (j, thumb) in metadata.thumbs.iter().enumerate() {
                if j == new_size {
                    continue;
                }
                for tile_ref in &thumb.tile_refs {
                    self.tiles.remove(tile_ref);
                }
            }

            image.size = Some(new_size);
            self.cache_todo.push_back(coords);
        }

        true
    }

    pub fn make_thumbs(&mut self, thumbnailer: &mut crate::Thumbnailer) -> bool {
        loop {
            if thumbnailer.is_full() {
                return false;
            }

            if let Some(coords) = self.thumb_todo.pop_front() {
                let image = self.images.get(&coords).unwrap();
                if !thumbnailer.make_thumbs(image) {
                    return false;
                }
            } else {
                break true;
            }
        }
    }

    pub fn update_metadata(&mut self, coords: Vector2<u32>, metadata_res: R<Metadata>) {
        let image = self.images.get_mut(&coords).unwrap();

        image.metadata = match metadata_res {
            Ok(metadata) => {
                self.cache_todo.push_front(coords);
                MetadataState::Some(metadata)
            }
            Err(e) => {
                error!("make_thumb: {}", e);
                MetadataState::Errored
            }
        };
    }

    pub fn draw(&self, trans: [[f64; 3]; 2], view: &View, draw_state: &DrawState, g: &mut G2d) {
        //{
        //    let [min, max] = self.extents;
        //    let op_color = color::hex("FF0000");
        //    let [x, y] = view.trans(min);
        //    let [w, h] = vec2_scale(vec2_f64(vec2_sub(max, min)), view.zoom);
        //    let trans = trans.trans(x, y);
        //    rectangle(op_color, [0.0, 0.0, w, 1.0], trans, g);
        //    rectangle(op_color, [0.0, 0.0, 1.0, h], trans, g);
        //    rectangle(op_color, [w, 0.0, 1.0, h], trans, g);
        //    rectangle(op_color, [0.0, h, w, 1.0], trans, g);
        //}

        for (&coords, image) in &self.images {
            let coords = view.trans(coords);

            if !view.is_visible(coords) {
                continue;
            }

            let trans = trans.trans(coords[0], coords[1]);

            if image.draw(trans, view, &self.tiles, &draw_state, g) {
                continue;
            }
        }
    }

    pub fn mouse_dist(&self, view: &View) -> usize {
        let midpoint = vec2_div(vec2_add(self.extents[0], self.extents[1]), [2, 2]);
        let mouse_dist = view.mouse_dist(midpoint);
        vec2_square_len(mouse_dist) as usize
    }
}
<commit_msg>Refactor out a duplicate view.coords() call<commit_after>// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use crate::database::Database;
use crate::image::Image;
use crate::stats::ScopedDuration;
use crate::vec::*;
use crate::view::View;
use crate::Stopwatch;
use crate::TileRef;
use crate::R;
use crate::{Metadata, MetadataState};
use piston_window::Transformed;
use piston_window::{DrawState, G2d, G2dTexture, G2dTextureContext, Texture, TextureSettings};
use std::cmp::Ordering;
use std::collections::{BTreeMap, VecDeque};

#[derive(Debug)]
pub struct Group {
    pub extents: [Vector2<u32>; 2],
    pub tiles: BTreeMap<TileRef, G2dTexture>,
    pub images: BTreeMap<Vector2<u32>, Image>,
    pub cache_todo: VecDeque<Vector2<u32>>,
    pub thumb_todo: VecDeque<Vector2<u32>>,
}

impl Group {
    pub fn new(extents: [Vector2<u32>; 2]) -> Self {
        Self {
            extents,
            tiles: BTreeMap::new(),
            images: BTreeMap::new(),
            cache_todo: VecDeque::new(),
            thumb_todo: VecDeque::new(),
        }
    }

    pub fn insert(&mut self, coords: Vector2<u32>, image: Image) {
        self.images.insert(coords, image);
    }

    pub fn reset(&mut self) {
        for image in self.images.values_mut() {
            image.reset();
        }
        self.tiles.clear();
        self.thumb_todo.clear();
        self.cache_todo.clear();
    }

    pub fn recheck(&mut self, view: &View) {
        self.thumb_todo.clear();
        self.cache_todo.clear();

        let mut mouse_dist: Vec<(&Vector2<u32>, &Image)> = Vec::with_capacity(self.images.len());
        mouse_dist.extend(self.images.iter());
        mouse_dist.sort_by_key(|(&coords, _)| vec2_square_len(view.mouse_dist(coords)) as isize);

        for (&coords, image) in &mouse_dist {
            match image.metadata {
                MetadataState::Some(_) => self.cache_todo.push_back(coords),
                MetadataState::Missing => self.thumb_todo.push_back(coords),
                MetadataState::Errored => continue,
            }
        }
    }

    pub fn load_cache(
        &mut self,
        view: &View,
        db: &Database,
        texture_context: &mut G2dTextureContext,
        stopwatch: &Stopwatch,
    ) -> bool {
        let _s3 = ScopedDuration::new("Group::load_cache");

        let target_size = view.target_size();

        let texture_settings = TextureSettings::new();

        for coords in self.cache_todo.pop_front() {
            let image = self.images.get_mut(&coords).unwrap();

            let metadata = image.get_metadata().expect("Image::get_metadata");

            let view_coords = view.trans(coords);

            let shift = if view.is_visible(view_coords) {
                0
            } else {
                let ratio = view.visible_ratio(view_coords);
                f64::max(0.0, ratio - 1.0).floor() as usize
            };

            let new_size = metadata.nearest(target_size >> shift);

            let current_size = image.size.unwrap_or(0);

            // Progressive resizing.
            let new_size = match new_size.cmp(&current_size) {
                Ordering::Less => current_size - 1,
                Ordering::Equal => {
                    // Already loaded target size.
                    continue;
                }
                Ordering::Greater => current_size + 1,
            };

            // Load new tiles.
            for tile_ref in &metadata.thumbs[new_size].tile_refs {
                // Already loaded.
                if self.tiles.contains_key(tile_ref) {
                    continue;
                }

                if stopwatch.done() {
                    self.cache_todo.push_front(coords);
                    return false;
                }

                let data = db.get(*tile_ref).expect("db get").expect("missing tile");

                let image = ::image::load_from_memory(&data).expect("load image");

                // TODO: Would be great to move off thread.
                let image =
                    Texture::from_image(texture_context, &image.to_rgba(), &texture_settings)
                        .expect("texture");

                self.tiles.insert(*tile_ref, image);
            }

            // Unload old tiles.
            for (j, thumb) in metadata.thumbs.iter().enumerate() {
                if j == new_size {
                    continue;
                }
                for tile_ref in &thumb.tile_refs {
                    self.tiles.remove(tile_ref);
                }
            }

            image.size = Some(new_size);
            self.cache_todo.push_back(coords);
        }

        true
    }

    pub fn make_thumbs(&mut self, thumbnailer: &mut crate::Thumbnailer) -> bool {
        loop {
            if thumbnailer.is_full() {
                return false;
            }

            if let Some(coords) = self.thumb_todo.pop_front() {
                let image = self.images.get(&coords).unwrap();
                if !thumbnailer.make_thumbs(image) {
                    return false;
                }
            } else {
                break true;
            }
        }
    }

    pub fn update_metadata(&mut self, coords: Vector2<u32>, metadata_res: R<Metadata>) {
        let image = self.images.get_mut(&coords).unwrap();

        image.metadata = match metadata_res {
            Ok(metadata) => {
                self.cache_todo.push_front(coords);
                MetadataState::Some(metadata)
            }
            Err(e) => {
                error!("make_thumb: {}", e);
                MetadataState::Errored
            }
        };
    }

    pub fn draw(&self, trans: [[f64; 3]; 2], view: &View, draw_state: &DrawState, g: &mut G2d) {
        //{
        //    let [min, max] = self.extents;
        //    let op_color = color::hex("FF0000");
        //    let [x, y] = view.trans(min);
        //    let [w, h] = vec2_scale(vec2_f64(vec2_sub(max, min)), view.zoom);
        //    let trans = trans.trans(x, y);
        //    rectangle(op_color, [0.0, 0.0, w, 1.0], trans, g);
        //    rectangle(op_color, [0.0, 0.0, 1.0, h], trans, g);
        //    rectangle(op_color, [w, 0.0, 1.0, h], trans, g);
        //    rectangle(op_color, [0.0, h, w, 1.0], trans, g);
        //}

        for (&coords, image) in &self.images {
            let coords = view.trans(coords);

            if !view.is_visible(coords) {
                continue;
            }

            let trans = trans.trans(coords[0], coords[1]);

            if image.draw(trans, view, &self.tiles, &draw_state, g) {
                continue;
            }
        }
    }

    pub fn mouse_dist(&self, view: &View) -> usize {
        let midpoint = vec2_div(vec2_add(self.extents[0], self.extents[1]), [2, 2]);
        let mouse_dist = view.mouse_dist(midpoint);
        vec2_square_len(mouse_dist) as usize
    }
}
<|endoftext|>


<commit_before>use proc_macro2::{Delimiter, Span};

use error::Result;
use parse::ParseBuffer;
#[cfg(any(feature = "full", feature = "derive"))]
use parse::ParseStream;
use private;
use token;

// Not public API.
#[doc(hidden)]
pub struct Parens<'a> {
    pub token: token::Paren,
    pub content: ParseBuffer<'a>,
}

// Not public API.
#[doc(hidden)]
pub struct Braces<'a> {
    pub token: token::Brace,
    pub content: ParseBuffer<'a>,
}

// Not public API.
#[doc(hidden)]
pub struct Brackets<'a> {
    pub token: token::Bracket,
    pub content: ParseBuffer<'a>,
}

// Not public API.
#[cfg(any(feature = "full", feature = "derive"))]
#[doc(hidden)]
pub struct Group<'a> {
    pub token: token::Group,
    pub content: ParseBuffer<'a>,
}

// Not public API.
#[doc(hidden)]
pub fn parse_parens(input: ParseStream) -> Result<Parens> {
    parse_delimited(input, Delimiter::Parenthesis).map(|(span, content)| Parens {
        token: token::Paren(span),
        content: content,
    })
}

// Not public API.
#[doc(hidden)]
pub fn parse_braces(input: ParseStream) -> Result<Braces> {
    parse_delimited(input, Delimiter::Brace).map(|(span, content)| Braces {
        token: token::Brace(span),
        content: content,
    })
}

// Not public API.
#[doc(hidden)]
pub fn parse_brackets(input: ParseStream) -> Result<Brackets> {
    parse_delimited(input, Delimiter::Bracket).map(|(span, content)| Brackets {
        token: token::Bracket(span),
        content: content,
    })
}

#[cfg(any(feature = "full", feature = "derive"))]
impl private {
    pub fn parse_group(input: ParseStream) -> Result<Group> {
        parse_delimited(input, Delimiter::None).map(|(span, content)| Group {
            token: token::Group(span),
            content: content,
        })
    }
}

fn parse_delimited(input: ParseStream, delimiter: Delimiter) -> Result<(Span, ParseBuffer)> {
    input.step(|cursor| {
        if let Some((content, span, rest)) = cursor.group(delimiter) {
            let unexpected = private::get_unexpected(input);
            let content = private::new_parse_buffer(span, cursor.advance(content), unexpected);
            Ok(((span, content), rest))
        } else {
            let message = match delimiter {
                Delimiter::Parenthesis => "expected parentheses",
                Delimiter::Brace => "expected curly braces",
                Delimiter::Bracket => "expected square brackets",
                Delimiter::None => "expected invisible group",
            };
            Err(cursor.error(message))
        }
    })
}

/// Parse a set of parentheses and expose their content to subsequent parsers.
///
/// # Example
///
/// ```rust
/// # #[macro_use]
/// # extern crate quote;
/// #
/// extern crate syn;
///
/// use syn::{parenthesized, token, Ident, Token, Type};
/// use syn::parse::{Parse, ParseStream, Result};
/// use syn::punctuated::Punctuated;
///
/// // Parse a simplified tuple struct syntax like:
/// //
/// //     struct S(A, B);
/// struct TupleStruct {
///     struct_token: Token![struct],
///     ident: Ident,
///     paren_token: token::Paren,
///     fields: Punctuated<Type, Token![,]>,
///     semi_token: Token![;],
/// }
///
/// impl Parse for TupleStruct {
///     fn parse(input: ParseStream) -> Result<Self> {
///         let content;
///         Ok(TupleStruct {
///             struct_token: input.parse()?,
///             ident: input.parse()?,
///             paren_token: parenthesized!(content in input),
///             fields: content.parse_terminated(Type::parse)?,
///             semi_token: input.parse()?,
///         })
///     }
/// }
/// #
/// # fn main() {
/// #     let input = quote! {
/// #         struct S(A, B);
/// #     };
/// #     syn::parse2::<TupleStruct>(input).unwrap();
/// # }
/// ```
#[macro_export]
macro_rules! parenthesized {
    ($content:ident in $cursor:expr) => {
        match $crate::group::parse_parens(&$cursor) {
            $crate::export::Ok(parens) => {
                $content = parens.content;
                parens.token
            }
            $crate::export::Err(error) => {
                return $crate::export::Err(error);
            }
        }
    };
}

/// Parse a set of curly braces and expose their content to subsequent parsers.
///
/// # Example
///
/// ```rust
/// # extern crate quote;
/// # use quote::quote;
/// #
/// extern crate syn;
///
/// use syn::{braced, token, Ident, Token, Type};
/// use syn::parse::{Parse, ParseStream, Result};
/// use syn::punctuated::Punctuated;
///
/// // Parse a simplified struct syntax like:
/// //
/// //     struct S {
/// //         a: A,
/// //         b: B,
/// //     }
/// struct Struct {
///     struct_token: Token![struct],
///     ident: Ident,
///     brace_token: token::Brace,
///     fields: Punctuated<Field, Token![,]>,
/// }
///
/// struct Field {
///     name: Ident,
///     colon_token: Token![:],
///     ty: Type,
/// }
///
/// impl Parse for Struct {
///     fn parse(input: ParseStream) -> Result<Self> {
///         let content;
///         Ok(Struct {
///             struct_token: input.parse()?,
///             ident: input.parse()?,
///             brace_token: braced!(content in input),
///             fields: content.parse_terminated(Field::parse)?,
///         })
///     }
/// }
///
/// impl Parse for Field {
///     fn parse(input: ParseStream) -> Result<Self> {
///         Ok(Field {
///             name: input.parse()?,
///             colon_token: input.parse()?,
///             ty: input.parse()?,
///         })
///     }
/// }
/// #
/// # fn main() {
/// #     let input = quote! {
/// #         struct S {
/// #             a: A,
/// #             b: B,
/// #         }
/// #     };
/// #     syn::parse2::<Struct>(input).unwrap();
/// # }
/// ```
#[macro_export]
macro_rules! braced {
    ($content:ident in $cursor:expr) => {
        match $crate::group::parse_braces(&$cursor) {
            $crate::export::Ok(braces) => {
                $content = braces.content;
                braces.token
            }
            $crate::export::Err(error) => {
                return $crate::export::Err(error);
            }
        }
    };
}

/// Parse a set of square brackets and expose their content to subsequent
/// parsers.
///
/// # Example
///
/// ```rust
/// # #[macro_use]
/// # extern crate quote;
/// #
/// extern crate proc_macro2;
/// extern crate syn;
///
/// use proc_macro2::TokenStream;
/// use syn::{bracketed, token, Token};
/// use syn::parse::{Parse, ParseStream, Result};
///
/// // Parse an outer attribute like:
/// //
/// //     #[repr(C, packed)]
/// struct OuterAttribute {
///     pound_token: Token![#],
///     bracket_token: token::Bracket,
///     content: TokenStream,
/// }
///
/// impl Parse for OuterAttribute {
///     fn parse(input: ParseStream) -> Result<Self> {
///         let content;
///         Ok(OuterAttribute {
///             pound_token: input.parse()?,
///             bracket_token: bracketed!(content in input),
///             content: content.parse()?,
///         })
///     }
/// }
/// #
/// # fn main() {
/// #     let input = quote! {
/// #         #[repr(C, packed)]
/// #     };
/// #     syn::parse2::<OuterAttribute>(input).unwrap();
/// # }
/// ```
#[macro_export]
macro_rules! bracketed {
    ($content:ident in $cursor:expr) => {
        match $crate::group::parse_brackets(&$cursor) {
            $crate::export::Ok(brackets) => {
                $content = brackets.content;
                brackets.token
            }
            $crate::export::Err(error) => {
                return $crate::export::Err(error);
            }
        }
    };
}
<commit_msg>Unconditionally require ParseStream for parsing groups<commit_after>use proc_macro2::{Delimiter, Span};

use error::Result;
use parse::{ParseBuffer, ParseStream};
use private;
use token;

// Not public API.
#[doc(hidden)]
pub struct Parens<'a> {
    pub token: token::Paren,
    pub content: ParseBuffer<'a>,
}

// Not public API.
#[doc(hidden)]
pub struct Braces<'a> {
    pub token: token::Brace,
    pub content: ParseBuffer<'a>,
}

// Not public API.
#[doc(hidden)]
pub struct Brackets<'a> {
    pub token: token::Bracket,
    pub content: ParseBuffer<'a>,
}

// Not public API.
#[cfg(any(feature = "full", feature = "derive"))]
#[doc(hidden)]
pub struct Group<'a> {
    pub token: token::Group,
    pub content: ParseBuffer<'a>,
}

// Not public API.
#[doc(hidden)]
pub fn parse_parens(input: ParseStream) -> Result<Parens> {
    parse_delimited(input, Delimiter::Parenthesis).map(|(span, content)| Parens {
        token: token::Paren(span),
        content: content,
    })
}

// Not public API.
#[doc(hidden)]
pub fn parse_braces(input: ParseStream) -> Result<Braces> {
    parse_delimited(input, Delimiter::Brace).map(|(span, content)| Braces {
        token: token::Brace(span),
        content: content,
    })
}

// Not public API.
#[doc(hidden)]
pub fn parse_brackets(input: ParseStream) -> Result<Brackets> {
    parse_delimited(input, Delimiter::Bracket).map(|(span, content)| Brackets {
        token: token::Bracket(span),
        content: content,
    })
}

#[cfg(any(feature = "full", feature = "derive"))]
impl private {
    pub fn parse_group(input: ParseStream) -> Result<Group> {
        parse_delimited(input, Delimiter::None).map(|(span, content)| Group {
            token: token::Group(span),
            content: content,
        })
    }
}

fn parse_delimited(input: ParseStream, delimiter: Delimiter) -> Result<(Span, ParseBuffer)> {
    input.step(|cursor| {
        if let Some((content, span, rest)) = cursor.group(delimiter) {
            let unexpected = private::get_unexpected(input);
            let content = private::new_parse_buffer(span, cursor.advance(content), unexpected);
            Ok(((span, content), rest))
        } else {
            let message = match delimiter {
                Delimiter::Parenthesis => "expected parentheses",
                Delimiter::Brace => "expected curly braces",
                Delimiter::Bracket => "expected square brackets",
                Delimiter::None => "expected invisible group",
            };
            Err(cursor.error(message))
        }
    })
}

/// Parse a set of parentheses and expose their content to subsequent parsers.
///
/// # Example
///
/// ```rust
/// # #[macro_use]
/// # extern crate quote;
/// #
/// extern crate syn;
///
/// use syn::{parenthesized, token, Ident, Token, Type};
/// use syn::parse::{Parse, ParseStream, Result};
/// use syn::punctuated::Punctuated;
///
/// // Parse a simplified tuple struct syntax like:
/// //
/// //     struct S(A, B);
/// struct TupleStruct {
///     struct_token: Token![struct],
///     ident: Ident,
///     paren_token: token::Paren,
///     fields: Punctuated<Type, Token![,]>,
///     semi_token: Token![;],
/// }
///
/// impl Parse for TupleStruct {
///     fn parse(input: ParseStream) -> Result<Self> {
///         let content;
///         Ok(TupleStruct {
///             struct_token: input.parse()?,
///             ident: input.parse()?,
///             paren_token: parenthesized!(content in input),
///             fields: content.parse_terminated(Type::parse)?,
///             semi_token: input.parse()?,
///         })
///     }
/// }
/// #
/// # fn main() {
/// #     let input = quote! {
/// #         struct S(A, B);
/// #     };
/// #     syn::parse2::<TupleStruct>(input).unwrap();
/// # }
/// ```
#[macro_export]
macro_rules! parenthesized {
    ($content:ident in $cursor:expr) => {
        match $crate::group::parse_parens(&$cursor) {
            $crate::export::Ok(parens) => {
                $content = parens.content;
                parens.token
            }
            $crate::export::Err(error) => {
                return $crate::export::Err(error);
            }
        }
    };
}

/// Parse a set of curly braces and expose their content to subsequent parsers.
///
/// # Example
///
/// ```rust
/// # extern crate quote;
/// # use quote::quote;
/// #
/// extern crate syn;
///
/// use syn::{braced, token, Ident, Token, Type};
/// use syn::parse::{Parse, ParseStream, Result};
/// use syn::punctuated::Punctuated;
///
/// // Parse a simplified struct syntax like:
/// //
/// //     struct S {
/// //         a: A,
/// //         b: B,
/// //     }
/// struct Struct {
///     struct_token: Token![struct],
///     ident: Ident,
///     brace_token: token::Brace,
///     fields: Punctuated<Field, Token![,]>,
/// }
///
/// struct Field {
///     name: Ident,
///     colon_token: Token![:],
///     ty: Type,
/// }
///
/// impl Parse for Struct {
///     fn parse(input: ParseStream) -> Result<Self> {
///         let content;
///         Ok(Struct {
///             struct_token: input.parse()?,
///             ident: input.parse()?,
///             brace_token: braced!(content in input),
///             fields: content.parse_terminated(Field::parse)?,
///         })
///     }
/// }
///
/// impl Parse for Field {
///     fn parse(input: ParseStream) -> Result<Self> {
///         Ok(Field {
///             name: input.parse()?,
///             colon_token: input.parse()?,
///             ty: input.parse()?,
///         })
///     }
/// }
/// #
/// # fn main() {
/// #     let input = quote! {
/// #         struct S {
/// #             a: A,
/// #             b: B,
/// #         }
/// #     };
/// #     syn::parse2::<Struct>(input).unwrap();
/// # }
/// ```
#[macro_export]
macro_rules! braced {
    ($content:ident in $cursor:expr) => {
        match $crate::group::parse_braces(&$cursor) {
            $crate::export::Ok(braces) => {
                $content = braces.content;
                braces.token
            }
            $crate::export::Err(error) => {
                return $crate::export::Err(error);
            }
        }
    };
}

/// Parse a set of square brackets and expose their content to subsequent
/// parsers.
///
/// # Example
///
/// ```rust
/// # #[macro_use]
/// # extern crate quote;
/// #
/// extern crate proc_macro2;
/// extern crate syn;
///
/// use proc_macro2::TokenStream;
/// use syn::{bracketed, token, Token};
/// use syn::parse::{Parse, ParseStream, Result};
///
/// // Parse an outer attribute like:
/// //
/// //     #[repr(C, packed)]
/// struct OuterAttribute {
///     pound_token: Token![#],
///     bracket_token: token::Bracket,
///     content: TokenStream,
/// }
///
/// impl Parse for OuterAttribute {
///     fn parse(input: ParseStream) -> Result<Self> {
///         let content;
///         Ok(OuterAttribute {
///             pound_token: input.parse()?,
///             bracket_token: bracketed!(content in input),
///             content: content.parse()?,
///         })
///     }
/// }
/// #
/// # fn main() {
/// #     let input = quote! {
/// #         #[repr(C, packed)]
/// #     };
/// #     syn::parse2::<OuterAttribute>(input).unwrap();
/// # }
/// ```
#[macro_export]
macro_rules! bracketed {
    ($content:ident in $cursor:expr) => {
        match $crate::group::parse_brackets(&$cursor) {
            $crate::export::Ok(brackets) => {
                $content = brackets.content;
                brackets.token
            }
            $crate::export::Err(error) => {
                return $crate::export::Err(error);
            }
        }
    };
}
<|endoftext|>


<commit_before>use cdchunking::{Chunker, ChunkInput, ZPAQ};
use rusqlite;
use rusqlite::{Connection, Transaction};
use rusqlite::types::ToSql;
use sha1::Sha1;
use std::fs::File;
use std::path::{Path, PathBuf};

use crate::{Error, HashDigest};

const SCHEMA: &'static str = "
    CREATE TABLE version(
        name VARCHAR(8) NOT NULL,
        version VARCHAR(16) NOT NULL
    );
    INSERT INTO version(name, version) VALUES('rs-sync', '0.1');

    CREATE TABLE files(
        file_id INTEGER NOT NULL PRIMARY KEY,
        name VARCHAR(512) NOT NULL,
        modified DATETIME NOT NULL
    );
    CREATE INDEX idx_files_name ON files(name);

    CREATE TABLE blocks(
        hash VARCHAR(40) NOT NULL,
        file_id INTEGER NOT NULL,
        offset INTEGER NOT NULL,
        PRIMARY KEY(file_id, offset)
    );
    CREATE INDEX idx_blocks_hash ON blocks(hash);
    CREATE INDEX idx_blocks_file ON blocks(file_id);
    CREATE INDEX idx_blocks_file_offset ON blocks(file_id, offset);
";

/// Index of files and blocks
pub struct Index {
    db: Connection,
}

impl Index {
    /// Open an index from a file
    pub fn open(filename: &Path) -> Result<Index, Error> {
        let exists = filename.exists();
        let db = Connection::open(filename)?;
        if !exists {
            warn!("Database doesn't exist, creating tables...");
            db.execute_batch(SCHEMA)?;
        }
        Ok(Index { db })
    }

    /// Open an in-memory index
    pub fn open_in_memory() -> Result<Index, Error> {
        let db = Connection::open_in_memory()?;
        db.execute_batch(SCHEMA)?;
        Ok(Index { db })
    }

    /// Try to find a block in the indexed files
    pub fn get_block(
        &self,
        hash: HashDigest,
    ) -> Result<Option<(PathBuf, usize)>, Error>
    {
        let mut stmt = self.db.prepare(
            "
            SELECT files.name, blocks.offset
            FROM blocks
            INNER JOIN files ON blocks.file_id = files.file_id
            WHERE blocks.hash = ?;
            ",
        )?;
        let mut rows = stmt.query(&[&hash as &dyn ToSql])?;
        if let Some(row) = rows.next() {
            let row = row?;
            let path: String = row.get(0);
            let path: PathBuf = path.into();
            let offset: i64 = row.get(1);
            let offset = offset as usize;
            Ok(Some((path, offset)))
        } else {
            Ok(None)
        }
    }

    /// Start a transaction to update the index
    pub fn transaction<'a>(
        &'a mut self
    ) -> Result<IndexTransaction<'a>, rusqlite::Error>
    {
        let tx = self.db.transaction()?;
        Ok(IndexTransaction { tx })
    }
}

pub struct IndexTransaction<'a> {
    tx: Transaction<'a>,
}

impl<'a> IndexTransaction<'a> {
    /// Add a file to the index
    ///
    /// This returns a tuple `(file_id, up_to_date)` where `file_id` can be
    /// used to insert blocks, and `up_to_date` indicates whether the file's
    /// modification date has changed and it should be re-indexed.
    pub fn add_file(
        &mut self,
        name: &Path,
        modified: chrono::DateTime<chrono::Utc>,
    ) -> Result<(u32, bool), Error>
    {
        let mut stmt = self.tx.prepare(
            "
            SELECT file_id, modified FROM files
            WHERE name = ?;
            ",
        )?;
        let mut rows = stmt.query(&[name.to_str().expect("encoding")])?;
        if let Some(row) = rows.next() {
            let row = row?;
            let file_id: u32 = row.get(0);
            let old_modified: chrono::DateTime<chrono::Utc> = row.get(1);
            if old_modified != modified {
                info!("Resetting file {:?}, modified", name);
                // Delete blocks
                self.tx.execute(
                    "
                    DELETE FROM blocks WHERE file_id = ?;
                    ",
                    &[&file_id],
                )?;
                // Update modification time
                self.tx.execute(
                    "
                    UPDATE files SET modified = ? WHERE file_id = ?;
                    ",
                    &[&modified as &dyn ToSql, &file_id],
                )?;
                Ok((file_id, false))
            } else {
                debug!("File {:?} up to date", name);
                Ok((file_id, true))
            }
        } else {
            info!("Inserting new file {:?}", name);
            self.tx.execute(
                "
                INSERT INTO files(name, modified)
                VALUES(?, ?);
                ",
                &[&name.to_str().expect("encoding") as &dyn ToSql, &modified],
            )?;
            let file_id = self.tx.last_insert_rowid();
            Ok((file_id as u32, false))
        }
    }

    /// Remove a file and all its blocks from the index
    pub fn remove_file(
        &mut self,
        file_id: u32,
    ) -> Result<(), Error>
    {
        self.tx.execute(
            "
            DELETE FROM blocks WHERE file_id = ?;
            ",
            &[&file_id],
        )?;
        self.tx.execute(
            "
            DELETE FROM files WHERE file_id = ?;
            ",
            &[&file_id],
        )?;
        Ok(())
    }

    /// Get a list of all the files in the index
    pub fn list_files(&self) -> Result<Vec<(u32, PathBuf)>, Error> {
        let mut stmt = self.tx.prepare(
            "
            SELECT file_id, name FROM files;
            ",
        )?;
        let mut rows = stmt.query(rusqlite::NO_PARAMS)?;
        let mut results = Vec::new();
        loop {
            match rows.next() {
                Some(Ok(row)) => {
                    let path: String = row.get(1);
                    results.push((row.get(0), path.into()))
                }
                Some(Err(e)) => return Err(e.into()),
                None => break,
            }
        }
        Ok(results)
    }

    /// Add a block to the index
    pub fn add_block(
        &mut self,
        hash: HashDigest,
        file_id: u32,
        offset: usize,
    ) -> Result<(), Error>
    {
        self.tx.execute(
            "
            INSERT INTO blocks(hash, file_id, offset)
            VALUES(?, ?, ?);
            ",
            &[&hash as &dyn ToSql, &file_id, &(offset as i64)],
        )?;
        Ok(())
    }

    /// Cut up a file into blocks and add them to the index
    pub fn index_file(
        &mut self,
        name: &Path,
    ) -> Result<(), Error>
    {
        let file = File::open(name)?;
        let (file_id, up_to_date) = self.add_file(
            name,
            file.metadata()?.modified()?.into(),
        )?;
        if !up_to_date {
            // Use ZPAQ to cut the stream into blocks
            let chunker = Chunker::new(
                ZPAQ::new(13) // 13 bits = 8 KiB block average
            );
            let mut chunk_iterator = chunker.stream(file);
            let mut start_offset = 0;
            let mut offset = 0;
            let mut sha1 = Sha1::new();
            while let Some(chunk) = chunk_iterator.read() {
                match chunk? {
                    ChunkInput::Data(d) => {
                        sha1.update(d);
                        offset += d.len();
                    }
                    ChunkInput::End => {
                        let digest = HashDigest(sha1.digest().bytes());
                        debug!(
                            "Adding block, offset={}, size={}, sha1={}",
                            start_offset, offset - start_offset, sha1.digest(),
                        );
                        self.add_block(digest, file_id, start_offset)?;
                        start_offset = offset;
                        sha1.reset();
                    }
                }
            }
        }
        Ok(())
    }

    /// Commit the transaction
    pub fn commit(self) -> Result<(), rusqlite::Error> {
        self.tx.commit()
    }
}

#[cfg(test)]
mod tests {
    use std::io::Write;
    use tempfile::NamedTempFile;

    use crate::HashDigest;
    use super::Index;

    #[test]
    fn test() {
        let mut file = NamedTempFile::new().expect("tempfile");
        for i in 0..2000 {
            write!(file, "Line {}\n", i + 1).expect("tempfile");
        }
        file.flush().expect("tempfile");
        let mut index = Index::open_in_memory().expect("db");
        {
            let mut tx = index.transaction().expect("db");
            tx.index_file(file.path()).expect("index");
            tx.commit().expect("db");
        }
        assert!(
            index.get_block(HashDigest(*b"12345678901234567890")).expect("get")
                .is_none()
        );
        assert_eq!(
            index.get_block(HashDigest(
                *b"\xfb\x5e\xf7\xeb\xad\xd8\x2c\x80\x85\xc5\
                   \xff\x63\x82\x36\x22\xba\xe0\xe2\x63\xf6"
            )).expect("get"),
            Some((file.path().into(), 0)),
        );
        assert_eq!(
            index.get_block(HashDigest(
                *b"\xa4\x32\x47\x91\xdc\xfc\x03\xdc\xea\xd1\
                   \xd5\x76\x9c\xbc\x3a\x9a\x23\x5e\x25\x8f"
            )).expect("get"),
            Some((file.path().into(), 11579)),
        );
    }
}
<commit_msg>Limit size of blocks<commit_after>use cdchunking::{Chunker, ChunkInput, ZPAQ};
use rusqlite;
use rusqlite::{Connection, Transaction};
use rusqlite::types::ToSql;
use sha1::Sha1;
use std::fs::File;
use std::path::{Path, PathBuf};

use crate::{Error, HashDigest};

const SCHEMA: &'static str = "
    CREATE TABLE version(
        name VARCHAR(8) NOT NULL,
        version VARCHAR(16) NOT NULL
    );
    INSERT INTO version(name, version) VALUES('rs-sync', '0.1');

    CREATE TABLE files(
        file_id INTEGER NOT NULL PRIMARY KEY,
        name VARCHAR(512) NOT NULL,
        modified DATETIME NOT NULL
    );
    CREATE INDEX idx_files_name ON files(name);

    CREATE TABLE blocks(
        hash VARCHAR(40) NOT NULL,
        file_id INTEGER NOT NULL,
        offset INTEGER NOT NULL,
        PRIMARY KEY(file_id, offset)
    );
    CREATE INDEX idx_blocks_hash ON blocks(hash);
    CREATE INDEX idx_blocks_file ON blocks(file_id);
    CREATE INDEX idx_blocks_file_offset ON blocks(file_id, offset);
";

/// Index of files and blocks
pub struct Index {
    db: Connection,
}

impl Index {
    /// Open an index from a file
    pub fn open(filename: &Path) -> Result<Index, Error> {
        let exists = filename.exists();
        let db = Connection::open(filename)?;
        if !exists {
            warn!("Database doesn't exist, creating tables...");
            db.execute_batch(SCHEMA)?;
        }
        Ok(Index { db })
    }

    /// Open an in-memory index
    pub fn open_in_memory() -> Result<Index, Error> {
        let db = Connection::open_in_memory()?;
        db.execute_batch(SCHEMA)?;
        Ok(Index { db })
    }

    /// Try to find a block in the indexed files
    pub fn get_block(
        &self,
        hash: HashDigest,
    ) -> Result<Option<(PathBuf, usize)>, Error>
    {
        let mut stmt = self.db.prepare(
            "
            SELECT files.name, blocks.offset
            FROM blocks
            INNER JOIN files ON blocks.file_id = files.file_id
            WHERE blocks.hash = ?;
            ",
        )?;
        let mut rows = stmt.query(&[&hash as &dyn ToSql])?;
        if let Some(row) = rows.next() {
            let row = row?;
            let path: String = row.get(0);
            let path: PathBuf = path.into();
            let offset: i64 = row.get(1);
            let offset = offset as usize;
            Ok(Some((path, offset)))
        } else {
            Ok(None)
        }
    }

    /// Start a transaction to update the index
    pub fn transaction<'a>(
        &'a mut self
    ) -> Result<IndexTransaction<'a>, rusqlite::Error>
    {
        let tx = self.db.transaction()?;
        Ok(IndexTransaction { tx })
    }
}

pub struct IndexTransaction<'a> {
    tx: Transaction<'a>,
}

const ZPAQ_BITS: usize = 13; // 13 bits = 8 KiB block average
const MAX_BLOCK_SIZE: usize = 1 << 15; // 32 KiB

impl<'a> IndexTransaction<'a> {
    /// Add a file to the index
    ///
    /// This returns a tuple `(file_id, up_to_date)` where `file_id` can be
    /// used to insert blocks, and `up_to_date` indicates whether the file's
    /// modification date has changed and it should be re-indexed.
    pub fn add_file(
        &mut self,
        name: &Path,
        modified: chrono::DateTime<chrono::Utc>,
    ) -> Result<(u32, bool), Error>
    {
        let mut stmt = self.tx.prepare(
            "
            SELECT file_id, modified FROM files
            WHERE name = ?;
            ",
        )?;
        let mut rows = stmt.query(&[name.to_str().expect("encoding")])?;
        if let Some(row) = rows.next() {
            let row = row?;
            let file_id: u32 = row.get(0);
            let old_modified: chrono::DateTime<chrono::Utc> = row.get(1);
            if old_modified != modified {
                info!("Resetting file {:?}, modified", name);
                // Delete blocks
                self.tx.execute(
                    "
                    DELETE FROM blocks WHERE file_id = ?;
                    ",
                    &[&file_id],
                )?;
                // Update modification time
                self.tx.execute(
                    "
                    UPDATE files SET modified = ? WHERE file_id = ?;
                    ",
                    &[&modified as &dyn ToSql, &file_id],
                )?;
                Ok((file_id, false))
            } else {
                debug!("File {:?} up to date", name);
                Ok((file_id, true))
            }
        } else {
            info!("Inserting new file {:?}", name);
            self.tx.execute(
                "
                INSERT INTO files(name, modified)
                VALUES(?, ?);
                ",
                &[&name.to_str().expect("encoding") as &dyn ToSql, &modified],
            )?;
            let file_id = self.tx.last_insert_rowid();
            Ok((file_id as u32, false))
        }
    }

    /// Remove a file and all its blocks from the index
    pub fn remove_file(
        &mut self,
        file_id: u32,
    ) -> Result<(), Error>
    {
        self.tx.execute(
            "
            DELETE FROM blocks WHERE file_id = ?;
            ",
            &[&file_id],
        )?;
        self.tx.execute(
            "
            DELETE FROM files WHERE file_id = ?;
            ",
            &[&file_id],
        )?;
        Ok(())
    }

    /// Get a list of all the files in the index
    pub fn list_files(&self) -> Result<Vec<(u32, PathBuf)>, Error> {
        let mut stmt = self.tx.prepare(
            "
            SELECT file_id, name FROM files;
            ",
        )?;
        let mut rows = stmt.query(rusqlite::NO_PARAMS)?;
        let mut results = Vec::new();
        loop {
            match rows.next() {
                Some(Ok(row)) => {
                    let path: String = row.get(1);
                    results.push((row.get(0), path.into()))
                }
                Some(Err(e)) => return Err(e.into()),
                None => break,
            }
        }
        Ok(results)
    }

    /// Add a block to the index
    pub fn add_block(
        &mut self,
        hash: HashDigest,
        file_id: u32,
        offset: usize,
    ) -> Result<(), Error>
    {
        self.tx.execute(
            "
            INSERT INTO blocks(hash, file_id, offset)
            VALUES(?, ?, ?);
            ",
            &[&hash as &dyn ToSql, &file_id, &(offset as i64)],
        )?;
        Ok(())
    }

    /// Cut up a file into blocks and add them to the index
    pub fn index_file(
        &mut self,
        name: &Path,
    ) -> Result<(), Error>
    {
        let file = File::open(name)?;
        let (file_id, up_to_date) = self.add_file(
            name,
            file.metadata()?.modified()?.into(),
        )?;
        if !up_to_date {
            // Use ZPAQ to cut the stream into blocks
            let chunker = Chunker::new(
                ZPAQ::new(ZPAQ_BITS) // 13 bits = 8 KiB block average
            );
            let mut chunk_iterator = chunker.stream(file);
            let mut start_offset = 0;
            let mut offset = 0;
            let mut sha1 = Sha1::new();
            while let Some(chunk) = chunk_iterator.read() {
                match chunk? {
                    ChunkInput::Data(mut d) => {
                        while offset - start_offset + d.len()
                            >= MAX_BLOCK_SIZE
                        {
                            let end = MAX_BLOCK_SIZE
                                + start_offset - offset;
                            sha1.update(&d[0..end]);
                            let digest = HashDigest(sha1.digest().bytes());
                            debug!(
                                "Max block size reached, adding block, \
                                 offset={}, size={}, sha1={}",
                                start_offset, offset + end - start_offset, sha1.digest(),
                            );
                            self.add_block(digest, file_id, start_offset)?;
                            offset += end;
                            start_offset = offset;
                            d = &d[end..];
                        }
                        sha1.update(d);
                        offset += d.len();
                    }
                    ChunkInput::End => {
                        let digest = HashDigest(sha1.digest().bytes());
                        debug!(
                            "Adding block, offset={}, size={}, sha1={}",
                            start_offset, offset - start_offset, sha1.digest(),
                        );
                        self.add_block(digest, file_id, start_offset)?;
                        start_offset = offset;
                        sha1.reset();
                    }
                }
            }
        }
        Ok(())
    }

    /// Commit the transaction
    pub fn commit(self) -> Result<(), rusqlite::Error> {
        self.tx.commit()
    }
}

#[cfg(test)]
mod tests {
    use std::io::Write;
    use tempfile::NamedTempFile;

    use crate::HashDigest;
    use super::{Index, MAX_BLOCK_SIZE};

    #[test]
    fn test() {
        let mut file = NamedTempFile::new().expect("tempfile");
        for i in 0..2000 {
            write!(file, "Line {}\n", i + 1).expect("tempfile");
        }
        for _ in 0..2000 {
            write!(file, "Test content\n").expect("tempfile");
        }
        file.flush().expect("tempfile");
        let mut index = Index::open_in_memory().expect("db");
        {
            let mut tx = index.transaction().expect("db");
            tx.index_file(file.path()).expect("index");
            tx.commit().expect("db");
        }
        assert!(
            index.get_block(HashDigest(*b"12345678901234567890")).expect("get")
                .is_none()
        );
        let block1 = index.get_block(HashDigest(
            *b"\xfb\x5e\xf7\xeb\xad\xd8\x2c\x80\x85\xc5\
               \xff\x63\x82\x36\x22\xba\xe0\xe2\x63\xf6"
        )).expect("get");
        assert_eq!(
            block1,
            Some((file.path().into(), 0)),
        );
        let block2 = index.get_block(HashDigest(
            *b"\x57\x0d\x8b\x30\xfc\xfd\x58\x5e\x41\x27\
               \xb5\x61\xf5\xec\xd3\x76\xff\x4d\x01\x01"
        )).expect("get");
        assert_eq!(
            block2,
            Some((file.path().into(), 11579)),
        );
        let block3 = index.get_block(HashDigest(
            *b"\xa6\xe1\x6d\x65\x26\xbd\x1d\x94\xec\x47\
               \xd2\xeb\x3f\xa4\x6f\x34\xb9\x71\x70\xa7"
        )).expect("get");
        assert_eq!(
            block3,
            Some((file.path().into(), 44347)),
        );
        assert_eq!(block3.unwrap().1 - block2.unwrap().1, MAX_BLOCK_SIZE);
    }
}
<|endoftext|>