<commit_before>#![allow(dead_code)]

use super::err2io;
use crate::io::{self, IoSlice, IoSliceMut, SeekFrom};
use crate::mem;
use crate::net::Shutdown;

#[derive(Debug)]
pub struct WasiFd {
    fd: wasi::Fd,
}

fn iovec<'a>(a: &'a mut [IoSliceMut<'_>]) -> &'a [wasi::Iovec] {
    assert_eq!(mem::size_of::<IoSliceMut<'_>>(), mem::size_of::<wasi::Iovec>());
    assert_eq!(mem::align_of::<IoSliceMut<'_>>(), mem::align_of::<wasi::Iovec>());
    /// SAFETY: `IoSliceMut` and `IoVec` have exactly the same memory layout
    unsafe {
        mem::transmute(a)
    }
}

fn ciovec<'a>(a: &'a [IoSlice<'_>]) -> &'a [wasi::Ciovec] {
    assert_eq!(mem::size_of::<IoSlice<'_>>(), mem::size_of::<wasi::Ciovec>());
    assert_eq!(mem::align_of::<IoSlice<'_>>(), mem::align_of::<wasi::Ciovec>());
    /// SAFETY: `IoSlice` and `CIoVec` have exactly the same memory layout
    unsafe {
        mem::transmute(a)
    }
}

impl WasiFd {
    pub unsafe fn from_raw(fd: wasi::Fd) -> WasiFd {
        WasiFd { fd }
    }

    pub fn into_raw(self) -> wasi::Fd {
        let ret = self.fd;
        mem::forget(self);
        ret
    }

    pub fn as_raw(&self) -> wasi::Fd {
        self.fd
    }

    pub fn datasync(&self) -> io::Result<()> {
        unsafe { wasi::fd_datasync(self.fd).map_err(err2io) }
    }

    pub fn pread(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize> {
        unsafe { wasi::fd_pread(self.fd, iovec(bufs), offset).map_err(err2io) }
    }

    pub fn pwrite(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> {
        unsafe { wasi::fd_pwrite(self.fd, ciovec(bufs), offset).map_err(err2io) }
    }

    pub fn read(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
        unsafe { wasi::fd_read(self.fd, iovec(bufs)).map_err(err2io) }
    }

    pub fn write(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
        unsafe { wasi::fd_write(self.fd, ciovec(bufs)).map_err(err2io) }
    }

    pub fn seek(&self, pos: SeekFrom) -> io::Result<u64> {
        let (whence, offset) = match pos {
            SeekFrom::Start(pos) => (wasi::WHENCE_SET, pos as i64),
            SeekFrom::End(pos) => (wasi::WHENCE_END, pos),
            SeekFrom::Current(pos) => (wasi::WHENCE_CUR, pos),
        };
        unsafe { wasi::fd_seek(self.fd, offset, whence).map_err(err2io) }
    }

    pub fn tell(&self) -> io::Result<u64> {
        unsafe { wasi::fd_tell(self.fd).map_err(err2io) }
    }

    // FIXME: __wasi_fd_fdstat_get

    pub fn set_flags(&self, flags: wasi::Fdflags) -> io::Result<()> {
        unsafe { wasi::fd_fdstat_set_flags(self.fd, flags).map_err(err2io) }
    }

    pub fn set_rights(&self, base: wasi::Rights, inheriting: wasi::Rights) -> io::Result<()> {
        unsafe { wasi::fd_fdstat_set_rights(self.fd, base, inheriting).map_err(err2io) }
    }

    pub fn sync(&self) -> io::Result<()> {
        unsafe { wasi::fd_sync(self.fd).map_err(err2io) }
    }

    pub fn advise(&self, offset: u64, len: u64, advice: wasi::Advice) -> io::Result<()> {
        unsafe { wasi::fd_advise(self.fd, offset, len, advice).map_err(err2io) }
    }

    pub fn allocate(&self, offset: u64, len: u64) -> io::Result<()> {
        unsafe { wasi::fd_allocate(self.fd, offset, len).map_err(err2io) }
    }

    pub fn create_directory(&self, path: &str) -> io::Result<()> {
        unsafe { wasi::path_create_directory(self.fd, path).map_err(err2io) }
    }

    pub fn link(
        &self,
        old_flags: wasi::Lookupflags,
        old_path: &str,
        new_fd: &WasiFd,
        new_path: &str,
    ) -> io::Result<()> {
        unsafe {
            wasi::path_link(self.fd, old_flags, old_path, new_fd.fd, new_path).map_err(err2io)
        }
    }

    pub fn open(
        &self,
        dirflags: wasi::Lookupflags,
        path: &str,
        oflags: wasi::Oflags,
        fs_rights_base: wasi::Rights,
        fs_rights_inheriting: wasi::Rights,
        fs_flags: wasi::Fdflags,
    ) -> io::Result<WasiFd> {
        unsafe {
            wasi::path_open(
                self.fd,
                dirflags,
                path,
                oflags,
                fs_rights_base,
                fs_rights_inheriting,
                fs_flags,
            )
            .map(|fd| WasiFd::from_raw(fd))
            .map_err(err2io)
        }
    }

    pub fn readdir(&self, buf: &mut [u8], cookie: wasi::Dircookie) -> io::Result<usize> {
        unsafe { wasi::fd_readdir(self.fd, buf.as_mut_ptr(), buf.len(), cookie).map_err(err2io) }
    }

    pub fn readlink(&self, path: &str, buf: &mut [u8]) -> io::Result<usize> {
        unsafe { wasi::path_readlink(self.fd, path, buf.as_mut_ptr(), buf.len()).map_err(err2io) }
    }

    pub fn rename(&self, old_path: &str, new_fd: &WasiFd, new_path: &str) -> io::Result<()> {
        unsafe { wasi::path_rename(self.fd, old_path, new_fd.fd, new_path).map_err(err2io) }
    }

    pub fn filestat_get(&self) -> io::Result<wasi::Filestat> {
        unsafe { wasi::fd_filestat_get(self.fd).map_err(err2io) }
    }

    pub fn filestat_set_times(
        &self,
        atim: wasi::Timestamp,
        mtim: wasi::Timestamp,
        fstflags: wasi::Fstflags,
    ) -> io::Result<()> {
        unsafe { wasi::fd_filestat_set_times(self.fd, atim, mtim, fstflags).map_err(err2io) }
    }

    pub fn filestat_set_size(&self, size: u64) -> io::Result<()> {
        unsafe { wasi::fd_filestat_set_size(self.fd, size).map_err(err2io) }
    }

    pub fn path_filestat_get(
        &self,
        flags: wasi::Lookupflags,
        path: &str,
    ) -> io::Result<wasi::Filestat> {
        unsafe { wasi::path_filestat_get(self.fd, flags, path).map_err(err2io) }
    }

    pub fn path_filestat_set_times(
        &self,
        flags: wasi::Lookupflags,
        path: &str,
        atim: wasi::Timestamp,
        mtim: wasi::Timestamp,
        fstflags: wasi::Fstflags,
    ) -> io::Result<()> {
        unsafe {
            wasi::path_filestat_set_times(self.fd, flags, path, atim, mtim, fstflags)
                .map_err(err2io)
        }
    }

    pub fn symlink(&self, old_path: &str, new_path: &str) -> io::Result<()> {
        unsafe { wasi::path_symlink(old_path, self.fd, new_path).map_err(err2io) }
    }

    pub fn unlink_file(&self, path: &str) -> io::Result<()> {
        unsafe { wasi::path_unlink_file(self.fd, path).map_err(err2io) }
    }

    pub fn remove_directory(&self, path: &str) -> io::Result<()> {
        unsafe { wasi::path_remove_directory(self.fd, path).map_err(err2io) }
    }

    pub fn sock_recv(
        &self,
        ri_data: &mut [IoSliceMut<'_>],
        ri_flags: wasi::Riflags,
    ) -> io::Result<(usize, wasi::Roflags)> {
        unsafe { wasi::sock_recv(self.fd, iovec(ri_data), ri_flags).map_err(err2io) }
    }

    pub fn sock_send(&self, si_data: &[IoSlice<'_>], si_flags: wasi::Siflags) -> io::Result<usize> {
        unsafe { wasi::sock_send(self.fd, ciovec(si_data), si_flags).map_err(err2io) }
    }

    pub fn sock_shutdown(&self, how: Shutdown) -> io::Result<()> {
        let how = match how {
            Shutdown::Read => wasi::SDFLAGS_RD,
            Shutdown::Write => wasi::SDFLAGS_WR,
            Shutdown::Both => wasi::SDFLAGS_WR | wasi::SDFLAGS_RD,
        };
        unsafe { wasi::sock_shutdown(self.fd, how).map_err(err2io) }
    }
}

impl Drop for WasiFd {
    fn drop(&mut self) {
        // FIXME: can we handle the return code here even though we can't on
        // unix?
        let _ = unsafe { wasi::fd_close(self.fd) };
    }
}
<commit_msg>make doc comments regular comments<commit_after>#![allow(dead_code)]

use super::err2io;
use crate::io::{self, IoSlice, IoSliceMut, SeekFrom};
use crate::mem;
use crate::net::Shutdown;

#[derive(Debug)]
pub struct WasiFd {
    fd: wasi::Fd,
}

fn iovec<'a>(a: &'a mut [IoSliceMut<'_>]) -> &'a [wasi::Iovec] {
    assert_eq!(mem::size_of::<IoSliceMut<'_>>(), mem::size_of::<wasi::Iovec>());
    assert_eq!(mem::align_of::<IoSliceMut<'_>>(), mem::align_of::<wasi::Iovec>());
    // SAFETY: `IoSliceMut` and `IoVec` have exactly the same memory layout
    unsafe {
        mem::transmute(a)
    }
}

fn ciovec<'a>(a: &'a [IoSlice<'_>]) -> &'a [wasi::Ciovec] {
    assert_eq!(mem::size_of::<IoSlice<'_>>(), mem::size_of::<wasi::Ciovec>());
    assert_eq!(mem::align_of::<IoSlice<'_>>(), mem::align_of::<wasi::Ciovec>());
    // SAFETY: `IoSlice` and `CIoVec` have exactly the same memory layout
    unsafe {
        mem::transmute(a)
    }
}

impl WasiFd {
    pub unsafe fn from_raw(fd: wasi::Fd) -> WasiFd {
        WasiFd { fd }
    }

    pub fn into_raw(self) -> wasi::Fd {
        let ret = self.fd;
        mem::forget(self);
        ret
    }

    pub fn as_raw(&self) -> wasi::Fd {
        self.fd
    }

    pub fn datasync(&self) -> io::Result<()> {
        unsafe { wasi::fd_datasync(self.fd).map_err(err2io) }
    }

    pub fn pread(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize> {
        unsafe { wasi::fd_pread(self.fd, iovec(bufs), offset).map_err(err2io) }
    }

    pub fn pwrite(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> {
        unsafe { wasi::fd_pwrite(self.fd, ciovec(bufs), offset).map_err(err2io) }
    }

    pub fn read(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
        unsafe { wasi::fd_read(self.fd, iovec(bufs)).map_err(err2io) }
    }

    pub fn write(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
        unsafe { wasi::fd_write(self.fd, ciovec(bufs)).map_err(err2io) }
    }

    pub fn seek(&self, pos: SeekFrom) -> io::Result<u64> {
        let (whence, offset) = match pos {
            SeekFrom::Start(pos) => (wasi::WHENCE_SET, pos as i64),
            SeekFrom::End(pos) => (wasi::WHENCE_END, pos),
            SeekFrom::Current(pos) => (wasi::WHENCE_CUR, pos),
        };
        unsafe { wasi::fd_seek(self.fd, offset, whence).map_err(err2io) }
    }

    pub fn tell(&self) -> io::Result<u64> {
        unsafe { wasi::fd_tell(self.fd).map_err(err2io) }
    }

    // FIXME: __wasi_fd_fdstat_get

    pub fn set_flags(&self, flags: wasi::Fdflags) -> io::Result<()> {
        unsafe { wasi::fd_fdstat_set_flags(self.fd, flags).map_err(err2io) }
    }

    pub fn set_rights(&self, base: wasi::Rights, inheriting: wasi::Rights) -> io::Result<()> {
        unsafe { wasi::fd_fdstat_set_rights(self.fd, base, inheriting).map_err(err2io) }
    }

    pub fn sync(&self) -> io::Result<()> {
        unsafe { wasi::fd_sync(self.fd).map_err(err2io) }
    }

    pub fn advise(&self, offset: u64, len: u64, advice: wasi::Advice) -> io::Result<()> {
        unsafe { wasi::fd_advise(self.fd, offset, len, advice).map_err(err2io) }
    }

    pub fn allocate(&self, offset: u64, len: u64) -> io::Result<()> {
        unsafe { wasi::fd_allocate(self.fd, offset, len).map_err(err2io) }
    }

    pub fn create_directory(&self, path: &str) -> io::Result<()> {
        unsafe { wasi::path_create_directory(self.fd, path).map_err(err2io) }
    }

    pub fn link(
        &self,
        old_flags: wasi::Lookupflags,
        old_path: &str,
        new_fd: &WasiFd,
        new_path: &str,
    ) -> io::Result<()> {
        unsafe {
            wasi::path_link(self.fd, old_flags, old_path, new_fd.fd, new_path).map_err(err2io)
        }
    }

    pub fn open(
        &self,
        dirflags: wasi::Lookupflags,
        path: &str,
        oflags: wasi::Oflags,
        fs_rights_base: wasi::Rights,
        fs_rights_inheriting: wasi::Rights,
        fs_flags: wasi::Fdflags,
    ) -> io::Result<WasiFd> {
        unsafe {
            wasi::path_open(
                self.fd,
                dirflags,
                path,
                oflags,
                fs_rights_base,
                fs_rights_inheriting,
                fs_flags,
            )
            .map(|fd| WasiFd::from_raw(fd))
            .map_err(err2io)
        }
    }

    pub fn readdir(&self, buf: &mut [u8], cookie: wasi::Dircookie) -> io::Result<usize> {
        unsafe { wasi::fd_readdir(self.fd, buf.as_mut_ptr(), buf.len(), cookie).map_err(err2io) }
    }

    pub fn readlink(&self, path: &str, buf: &mut [u8]) -> io::Result<usize> {
        unsafe { wasi::path_readlink(self.fd, path, buf.as_mut_ptr(), buf.len()).map_err(err2io) }
    }

    pub fn rename(&self, old_path: &str, new_fd: &WasiFd, new_path: &str) -> io::Result<()> {
        unsafe { wasi::path_rename(self.fd, old_path, new_fd.fd, new_path).map_err(err2io) }
    }

    pub fn filestat_get(&self) -> io::Result<wasi::Filestat> {
        unsafe { wasi::fd_filestat_get(self.fd).map_err(err2io) }
    }

    pub fn filestat_set_times(
        &self,
        atim: wasi::Timestamp,
        mtim: wasi::Timestamp,
        fstflags: wasi::Fstflags,
    ) -> io::Result<()> {
        unsafe { wasi::fd_filestat_set_times(self.fd, atim, mtim, fstflags).map_err(err2io) }
    }

    pub fn filestat_set_size(&self, size: u64) -> io::Result<()> {
        unsafe { wasi::fd_filestat_set_size(self.fd, size).map_err(err2io) }
    }

    pub fn path_filestat_get(
        &self,
        flags: wasi::Lookupflags,
        path: &str,
    ) -> io::Result<wasi::Filestat> {
        unsafe { wasi::path_filestat_get(self.fd, flags, path).map_err(err2io) }
    }

    pub fn path_filestat_set_times(
        &self,
        flags: wasi::Lookupflags,
        path: &str,
        atim: wasi::Timestamp,
        mtim: wasi::Timestamp,
        fstflags: wasi::Fstflags,
    ) -> io::Result<()> {
        unsafe {
            wasi::path_filestat_set_times(self.fd, flags, path, atim, mtim, fstflags)
                .map_err(err2io)
        }
    }

    pub fn symlink(&self, old_path: &str, new_path: &str) -> io::Result<()> {
        unsafe { wasi::path_symlink(old_path, self.fd, new_path).map_err(err2io) }
    }

    pub fn unlink_file(&self, path: &str) -> io::Result<()> {
        unsafe { wasi::path_unlink_file(self.fd, path).map_err(err2io) }
    }

    pub fn remove_directory(&self, path: &str) -> io::Result<()> {
        unsafe { wasi::path_remove_directory(self.fd, path).map_err(err2io) }
    }

    pub fn sock_recv(
        &self,
        ri_data: &mut [IoSliceMut<'_>],
        ri_flags: wasi::Riflags,
    ) -> io::Result<(usize, wasi::Roflags)> {
        unsafe { wasi::sock_recv(self.fd, iovec(ri_data), ri_flags).map_err(err2io) }
    }

    pub fn sock_send(&self, si_data: &[IoSlice<'_>], si_flags: wasi::Siflags) -> io::Result<usize> {
        unsafe { wasi::sock_send(self.fd, ciovec(si_data), si_flags).map_err(err2io) }
    }

    pub fn sock_shutdown(&self, how: Shutdown) -> io::Result<()> {
        let how = match how {
            Shutdown::Read => wasi::SDFLAGS_RD,
            Shutdown::Write => wasi::SDFLAGS_WR,
            Shutdown::Both => wasi::SDFLAGS_WR | wasi::SDFLAGS_RD,
        };
        unsafe { wasi::sock_shutdown(self.fd, how).map_err(err2io) }
    }
}

impl Drop for WasiFd {
    fn drop(&mut self) {
        // FIXME: can we handle the return code here even though we can't on
        // unix?
        let _ = unsafe { wasi::fd_close(self.fd) };
    }
}
<|endoftext|>


<commit_before>use clap::{Arg, App, SubCommand};

pub fn build_list_cli_component<'a, 'b>() -> App<'a, 'b> {
    SubCommand::with_name(list_subcommand_name())
        .author("Matthias Beyer <mail@beyermatthias.de>")
        .version("0.1")
        .about("List entries")
}

pub fn list_subcommand_name() -> &'static str {
    "list"
}

<commit_msg>Add default arguments for builtin lister backends<commit_after>use clap::{Arg, App, SubCommand};

pub fn build_list_cli_component<'a, 'b>() -> App<'a, 'b> {
    SubCommand::with_name(list_subcommand_name())
        .author("Matthias Beyer <mail@beyermatthias.de>")
        .version("0.1")
        .about("List entries")

        .arg(Arg::with_name(list_backend_line())
             .short("l")
             .long("line")
             .takes_value(false)
             .required(false)
             .multiple(false)
             .help("Use backend: Line"))

        .arg(Arg::with_name(list_backend_path())
             .short("p")
             .long("path")
             .takes_value(false)
             .required(false)
             .multiple(false)
             .help("Use backend: Path"))

        .arg(Arg::with_name(list_backend_path_absolute())
             .short("P")
             .long("path-absolute")
             .takes_value(false)
             .required(false)
             .multiple(false)
             .help("Use backend: Path (absolute)"))

}

pub fn list_subcommand_name() -> &'static str {
    "list"
}

pub fn list_backend_line() -> &'static str {
    "line"
}

pub fn list_backend_path() -> &'static str {
    "path"
}

pub fn list_backend_path_absolute() -> &'static str {
    "path-absolute"
}

<|endoftext|>


<commit_before>#[cfg(any(target_os = "freebsd", target_os = "dragonfly", target_os = "ios",
          target_os = "netbsd", target_os = "macos", target_os = "linux"))]
pub mod aio;

#[cfg(any(target_os = "linux", target_os = "android"))]
pub mod epoll;

#[cfg(any(target_os = "macos", target_os = "ios", target_os = "freebsd",
    target_os = "dragonfly", target_os = "openbsd", target_os = "netbsd"))]
pub mod event;

#[cfg(target_os = "linux")]
pub mod eventfd;

#[cfg(target_os = "linux")]
pub mod memfd;

#[macro_use]
pub mod ioctl;

// TODO: Add support for dragonfly, freebsd, and ios/macos.
#[cfg(any(target_os = "android", target_os = "linux"))]
pub mod sendfile;

pub mod signal;

// FIXME: Add to Android once libc#671 lands in a release
#[cfg(target_os = "linux")]
pub mod signalfd;

pub mod socket;

pub mod stat;

#[cfg(any(target_os = "linux", target_os = "android"))]
pub mod syscall;

#[cfg(any(target_os = "linux"))]
pub mod reboot;

pub mod termios;

pub mod utsname;

pub mod wait;

pub mod mman;

pub mod uio;

pub mod time;

#[cfg(any(target_os = "linux", target_os = "android"))]
pub mod ptrace;

pub mod select;

#[cfg(all(target_os = "linux",
          any(target_arch = "x86",
              target_arch = "x86_64",
              target_arch = "arm")),
          )]
pub mod quota;

#[cfg(all(target_os = "linux",
          any(target_arch = "x86",
              target_arch = "x86_64",
              target_arch = "arm")),
          )]
pub mod statfs;

#[cfg(all(any(target_os = "linux",
              target_os = "macos"),
          any(target_arch = "x86",
              target_arch = "x86_64",
              target_arch = "arm")),
          )]
pub mod statvfs;
pub mod pthread;
<commit_msg>Expose signalfd on Android<commit_after>#[cfg(any(target_os = "freebsd", target_os = "dragonfly", target_os = "ios",
          target_os = "netbsd", target_os = "macos", target_os = "linux"))]
pub mod aio;

#[cfg(any(target_os = "linux", target_os = "android"))]
pub mod epoll;

#[cfg(any(target_os = "macos", target_os = "ios", target_os = "freebsd",
    target_os = "dragonfly", target_os = "openbsd", target_os = "netbsd"))]
pub mod event;

#[cfg(target_os = "linux")]
pub mod eventfd;

#[cfg(target_os = "linux")]
pub mod memfd;

#[macro_use]
pub mod ioctl;

// TODO: Add support for dragonfly, freebsd, and ios/macos.
#[cfg(any(target_os = "android", target_os = "linux"))]
pub mod sendfile;

pub mod signal;

#[cfg(any(target_os = "android", target_os = "linux"))]
pub mod signalfd;

pub mod socket;

pub mod stat;

#[cfg(any(target_os = "linux", target_os = "android"))]
pub mod syscall;

#[cfg(any(target_os = "linux"))]
pub mod reboot;

pub mod termios;

pub mod utsname;

pub mod wait;

pub mod mman;

pub mod uio;

pub mod time;

#[cfg(any(target_os = "linux", target_os = "android"))]
pub mod ptrace;

pub mod select;

#[cfg(all(target_os = "linux",
          any(target_arch = "x86",
              target_arch = "x86_64",
              target_arch = "arm")),
          )]
pub mod quota;

#[cfg(all(target_os = "linux",
          any(target_arch = "x86",
              target_arch = "x86_64",
              target_arch = "arm")),
          )]
pub mod statfs;

#[cfg(all(any(target_os = "linux",
              target_os = "macos"),
          any(target_arch = "x86",
              target_arch = "x86_64",
              target_arch = "arm")),
          )]
pub mod statvfs;
pub mod pthread;
<|endoftext|>


<commit_before>
#[derive(Copy)]
pub enum Feature {
	Fpu,
	Virtual8086,
	DebugExtension,
	PageSizeExtension,
	TimeStampCounter,
	ModelSpecificRegister,
	PhysicalAddressExtension,
	MachineCheckException,
	Cx8, // CMPXCHG8
	Apic,
	SysEnter,
	MemoryTypeRange,
	PageGlobal,
	MachineCheckArchitecture,
	CMov,
	PageAttributeTable,
	PageSizeExtension36,
	ProcessorSerial,
	CacheFlush,
	DebugStore,
	Acpi,
	Mmx,
	FxSave,
	Sse,
	Sse2,
	SelfSnoop,
	HyperThreading,
	ThermalMonitor,
	Ia64,
	PendingBreak,
	Sse3,
	PclMulQdq, // what
	DebugStore64,
	Monitor,
	CplDebugStore,
	Vmx,
	SaferMode,
	EnhancedSpeedStep,
	ThermalMonitor2,
	Ssse3,
	L1ContextId,
	Fma,
	Cx16, // CMPXCHG16B
	Xtpr, // I have no idea what this is
	PerformanceMonitor,
	ProcessContextId,
	DirectCache,
	Sse41,
	Sse42,
	X2Apic,
	MovBe,
	PopulationCount,
	TscDeadline,
	AesNi,
	XSave,
	OsXSave,
	Avx,
	HalfPrecision,
	HwRandom
}

#[derive(Copy, FromPrimitive)]
pub enum Exception {
	DivisionByZero = 0,
	Debug = 1,
	Nmi = 2,
	Breakpoint = 3,
	Overflow = 4,
	Bounds = 5,
	InvalidOpcode = 6,
	NotAvailable = 7,
	DoubleFault = 8,
	CoprocessorSegment = 9,
	Tss = 10,
	NotPresent = 11,
	StackSegment = 12,
	GeneralProtection = 13,
	PageFault = 14,
	Fpu = 16,
	Alignment = 17,
	MachineCheck = 18,
	Simd = 19,
	Virtualization = 20,
	Security = 30
}

#[derive(Copy)]
pub enum Msr {
	ApicBase = 0x1B
}

#[inline(always)]
pub fn cpuid(function: u32) -> (u32, u32, u32, u32) {
	unsafe {
		let (eax, ebx, ecx, edx): (u32, u32, u32, u32);
		asm!("cpuid" : "={eax}"(eax), "={ebx}"(ebx), "={ecx}"(ecx), "={edx}"(edx) : "{eax}"(function));
		(eax, ebx, ecx, edx)
	}
}

#[inline(always)]
pub fn supports(feature: Feature) -> bool {
	let (_, _, feature_ecx, feature_edx) = cpuid(1);
	0 != match feature { // this is bad
		Feature::Fpu => feature_edx & (1 << 0),
		Feature::Virtual8086 => feature_edx & (1 << 1),
		Feature::DebugExtension => feature_edx & (1 << 2),
		Feature::PageSizeExtension => feature_edx & (1 << 3),
		Feature::TimeStampCounter => feature_edx & (1 << 4),
		Feature::ModelSpecificRegister => feature_edx & (1 << 5),
		Feature::PhysicalAddressExtension => feature_edx & (1 << 6),
		Feature::MachineCheckException => feature_edx & (1 << 7),
		Feature::Cx8 => feature_edx & (1 << 8),
		Feature::Apic => feature_edx & (1 << 9),
		Feature::SysEnter => feature_edx & (1 << 11),
		Feature::MemoryTypeRange => feature_edx & (1 << 12),
		Feature::PageGlobal => feature_edx & (1 << 13),
		Feature::MachineCheckArchitecture => feature_edx & (1 << 14),
		Feature::CMov => feature_edx & (1 << 15),
		Feature::PageAttributeTable => feature_edx & (1 << 16),
		Feature::PageSizeExtension36 => feature_edx & (1 << 17),
		Feature::ProcessorSerial => feature_edx & (1 << 18),
		Feature::CacheFlush => feature_edx & (1 << 19),
		Feature::DebugStore => feature_edx & (1 << 21),
		Feature::Acpi => feature_edx & (1 << 22),
		Feature::Mmx => feature_edx & (1 << 23),
		Feature::FxSave => feature_edx & (1 << 24),
		Feature::Sse => feature_edx & (1 << 25),
		Feature::Sse2 => feature_edx & (1 << 26),
		Feature::SelfSnoop => feature_edx & (1 << 27),
		Feature::HyperThreading => feature_edx & (1 << 28),
		Feature::ThermalMonitor => feature_edx & (1 << 29),
		Feature::Ia64 => feature_edx & (1 << 30),
		Feature::PendingBreak => feature_edx & (1 << 31),

		Feature::Sse3 => feature_ecx & (1 << 0),
		Feature::PclMulQdq => feature_ecx & (1 << 1),
		Feature::DebugStore64 => feature_ecx & (1 << 2),
		Feature::Monitor => feature_ecx & (1 << 3),
		Feature::CplDebugStore => feature_ecx & (1 << 4),
		Feature::Vmx => feature_ecx & (1 << 5),
		Feature::SaferMode => feature_ecx & (1 << 6),
		Feature::EnhancedSpeedStep => feature_ecx & (1 << 7),
		Feature::ThermalMonitor2 => feature_ecx & (1 << 8),
		Feature::Ssse3 => feature_ecx & (1 << 9),
		Feature::L1ContextId => feature_ecx & (1 << 10),
		Feature::Fma => feature_ecx & (1 << 12),
		Feature::Cx16 => feature_ecx & (1 << 13),
		Feature::Xtpr => feature_ecx & (1 << 14),
		Feature::PerformanceMonitor => feature_ecx & (1 << 15),
		Feature::ProcessContextId => feature_ecx & (1 << 17),
		Feature::DirectCache => feature_ecx & (1 << 18),
		Feature::Sse41 => feature_ecx & (1 << 19),
		Feature::Sse42 => feature_ecx & (1 << 20),
		Feature::X2Apic => feature_ecx & (1 << 21),
		Feature::MovBe => feature_ecx & (1 << 22),
		Feature::PopulationCount => feature_ecx & (1 << 23),
		Feature::TscDeadline => feature_ecx & (1 << 24),
		Feature::AesNi => feature_ecx & (1 << 25),
		Feature::XSave => feature_ecx & (1 << 26),
		Feature::OsXSave => feature_ecx & (1 << 27),
		Feature::Avx => feature_ecx & (1 << 28),
		Feature::HalfPrecision => feature_ecx & (1 << 29),
		Feature::HwRandom => feature_ecx & (1 << 30)
	}
}

#[inline(always)]
pub unsafe fn read_msr(msr: Msr) -> u64 {
	let (r1, r2): (u32, u32);
	asm!("rdmsr" : "={eax}"(r1), "={edx}"(r2) : "{ecx}"(msr as u32) :: "intel");
	r1 as u64 | ((r2 as u64) << 32)
}

#[derive(Copy)]
#[repr(C, packed)]
pub struct SegmentSelector {
	data: u16
}

impl SegmentSelector {
	#[inline(always)]
	pub fn new(index: u16, rpl: u8) -> SegmentSelector {
		SegmentSelector {
			data: index << 3 | rpl as u16
		}
	}

	pub fn bits(&self) -> u16 {
		self.data
	}
}

#[inline(always)]
pub unsafe fn set_tr(selector: SegmentSelector) {
	asm!("ltr $0" :: "r"(selector.bits()) :: "volatile", "intel");
}

#[inline(always)]
pub unsafe fn set_ss(selector: SegmentSelector) {
	asm!("mov ss, $0" :: "r"(selector.bits()) :: "volatile", "intel");
}

#[inline(always)]
pub unsafe fn set_ds(selector: SegmentSelector) {
	asm!("mov ds, $0" :: "r"(selector.bits()) :: "volatile", "intel");
}

#[inline(always)]
pub unsafe fn set_es(selector: SegmentSelector) {
	asm!("mov es, $0" :: "r"(selector.bits()) :: "volatile", "intel");
}

#[inline(always)]
pub unsafe fn set_gs(selector: SegmentSelector) {
	asm!("mov gs, $0" :: "r"(selector.bits()) :: "volatile", "intel");
}

#[inline(always)]
pub unsafe fn set_fs(selector: SegmentSelector) {
	asm!("mov fs, $0" :: "r"(selector.bits()) :: "volatile", "intel");
}

#[inline(always)]
pub unsafe fn enable_interrupts() {
	asm!("sti" :::: "volatile", "intel");
}

#[inline(always)]
pub unsafe fn disable_interrupts() {
	asm!("cli" :::: "volatile", "intel");
}

#[inline(always)]
pub unsafe fn halt() {
	asm!("hlt" :::: "volatile", "intel");
}

#[inline(always)]
pub unsafe fn out8(port: u16, value: u8) {
	asm!("out $0, $1" :: "{dx}"(port), "{al}"(value) :: "volatile", "intel");
}

#[inline(always)]
pub unsafe fn out16(port: u16, value: u16) {
	asm!("out $0, $1" :: "{dx}"(port), "{ax}"(value) :: "volatile", "intel");
}

#[inline(always)]
pub unsafe fn out32(port: u16, value: u32) {
	asm!("out $0, $1" :: "{dx}"(port), "{eax}"(value) :: "volatile", "intel");
}

#[inline(always)]
pub unsafe fn in8(port: u16) -> u8 { // unsafe since devices change state upon being read
	let r: u8;
	asm!("in $0, $1" : "={al}"(r) : "{dx}"(port) :: "intel");
	r
}

#[inline(always)]
pub unsafe fn in16(port: u16) -> u16 {
	let r: u16;
	asm!("in $0, $1" : "={ax}"(r) : "{dx}"(port) :: "intel");
	r
}

#[inline(always)]
pub unsafe fn in32(port: u16) -> u32 {
	let r: u32;
	asm!("in $0, $1" : "={eax}"(r) : "{dx}"(port) :: "intel");
	r
}
<commit_msg>Features are returned in a bitset<commit_after>#![allow(non_upper_case_globals)]

bitflags!(
	flags Features: u64 {
		static Fpu = 1 << 0,
		static Virtual8086 = 1 << 1,
		static DebugExtension = 1 << 2,
		static PageSizeExtension = 1 << 3,
		static TimeStampCounter = 1 << 4,
		static ModelSpecificRegister = 1 << 5,
		static PhysicalAddressExtension = 1 << 6,
		static MachineCheckException = 1 << 7,
		static Cx8 = 1 << 8, // CMPXCHG8
		static Apic = 1 << 9,
		static SysEnter = 1 << 11,
		static MemoryTypeRange = 1 << 12,
		static PageGlobal = 1 << 13,
		static MachineCheckArchitecture = 1 << 14,
		static CMov = 1 << 15,
		static PageAttributeTable = 1 << 16,
		static PageSizeExtension36 = 1 << 17,
		static ProcessorSerial = 1 << 18,
		static CacheFlush = 1 << 19,
		static DebugStore = 1 << 21,
		static Acpi = 1 << 22,
		static Mmx = 1 << 23,
		static FxSave = 1 << 24,
		static Sse = 1 << 25,
		static Sse2 = 1 << 26,
		static SelfSnoop = 1 << 27,
		static HyperThreading = 1 << 28,
		static ThermalMonitor = 1 << 29,
		static Ia64 = 1 << 30,
		static PendingBreak = 1 << 31,

		static Sse3 = 1 << (32 + 0),
		static PclMulQdq = 1 << (32 + 1), // what
		static DebugStore64 = 1 << (32 + 2),
		static Monitor = 1 << (32 + 3),
		static CplDebugStore = 1 << (32 + 4),
		static Vmx = 1 << (32 + 5),
		static SaferMode = 1 << (32 + 6),
		static EnhancedSpeedStep = 1 << (32 + 7),
		static ThermalMonitor2 = 1 << (32 + 8),
		static Ssse3 = 1 << (32 + 9),
		static L1ContextId = 1 << (32 + 10),
		static Fma = 1 << (32 + 12),
		static Cx16 = 1 << (32 + 13), // CMPXCHG16B
		static Xtpr = 1 << (32 + 14), // I have no idea what this is
		static PerformanceMonitor = 1 << (32 + 15),
		static ProcessContextId = 1 << (32 + 17),
		static DirectCache = 1 << (32 + 18),
		static Sse41 = 1 << (32 + 19),
		static Sse42 = 1 << (32 + 20),
		static X2Apic = 1 << (32 + 21),
		static MovBe = 1 << (32 + 22),
		static PopulationCount = 1 << (32 + 23),
		static TscDeadline = 1 << (32 + 24),
		static AesNi = 1 << (32 + 25),
		static XSave = 1 << (32 + 26),
		static OsXSave = 1 << (32 + 27),
		static Avx = 1 << (32 + 28),
		static HalfPrecision = 1 << (32 + 29),
		static HwRandom = 1 << (32 + 30)
	}
);

#[derive(Copy, FromPrimitive)]
pub enum Exception {
	DivisionByZero = 0,
	Debug = 1,
	Nmi = 2,
	Breakpoint = 3,
	Overflow = 4,
	Bounds = 5,
	InvalidOpcode = 6,
	NotAvailable = 7,
	DoubleFault = 8,
	CoprocessorSegment = 9,
	Tss = 10,
	NotPresent = 11,
	StackSegment = 12,
	GeneralProtection = 13,
	PageFault = 14,
	Fpu = 16,
	Alignment = 17,
	MachineCheck = 18,
	Simd = 19,
	Virtualization = 20,
	Security = 30
}

#[derive(Copy)]
pub enum Msr {
	ApicBase = 0x1B
}

#[inline(always)]
pub fn cpuid(function: u32) -> (u32, u32, u32, u32) {
	unsafe {
		let (eax, ebx, ecx, edx): (u32, u32, u32, u32);
		asm!("cpuid" : "={eax}"(eax), "={ebx}"(ebx), "={ecx}"(ecx), "={edx}"(edx) : "{eax}"(function));
		(eax, ebx, ecx, edx)
	}
}

#[inline(always)]
pub fn supports() -> Features {
	let (_, _, feature_ecx, feature_edx) = cpuid(1);
	Features {
		bits: ((feature_ecx as u64) << 32) | (feature_edx as u64)
	}
}

#[inline(always)]
pub unsafe fn read_msr(msr: Msr) -> u64 {
	let (r1, r2): (u32, u32);
	asm!("rdmsr" : "={eax}"(r1), "={edx}"(r2) : "{ecx}"(msr as u32) :: "intel");
	r1 as u64 | ((r2 as u64) << 32)
}

#[derive(Copy)]
#[repr(C, packed)]
pub struct SegmentSelector {
	data: u16
}

impl SegmentSelector {
	#[inline(always)]
	pub fn new(index: u16, rpl: u8) -> SegmentSelector {
		SegmentSelector {
			data: index << 3 | rpl as u16
		}
	}

	pub fn bits(&self) -> u16 {
		self.data
	}
}

#[inline(always)]
pub unsafe fn set_tr(selector: SegmentSelector) {
	asm!("ltr $0" :: "r"(selector.bits()) :: "volatile", "intel");
}

#[inline(always)]
pub unsafe fn set_ss(selector: SegmentSelector) {
	asm!("mov ss, $0" :: "r"(selector.bits()) :: "volatile", "intel");
}

#[inline(always)]
pub unsafe fn set_ds(selector: SegmentSelector) {
	asm!("mov ds, $0" :: "r"(selector.bits()) :: "volatile", "intel");
}

#[inline(always)]
pub unsafe fn set_es(selector: SegmentSelector) {
	asm!("mov es, $0" :: "r"(selector.bits()) :: "volatile", "intel");
}

#[inline(always)]
pub unsafe fn set_gs(selector: SegmentSelector) {
	asm!("mov gs, $0" :: "r"(selector.bits()) :: "volatile", "intel");
}

#[inline(always)]
pub unsafe fn set_fs(selector: SegmentSelector) {
	asm!("mov fs, $0" :: "r"(selector.bits()) :: "volatile", "intel");
}

#[inline(always)]
pub unsafe fn enable_interrupts() {
	asm!("sti" :::: "volatile", "intel");
}

#[inline(always)]
pub unsafe fn disable_interrupts() {
	asm!("cli" :::: "volatile", "intel");
}

#[inline(always)]
pub unsafe fn halt() {
	asm!("hlt" :::: "volatile", "intel");
}

#[inline(always)]
pub unsafe fn out8(port: u16, value: u8) {
	asm!("out $0, $1" :: "{dx}"(port), "{al}"(value) :: "volatile", "intel");
}

#[inline(always)]
pub unsafe fn out16(port: u16, value: u16) {
	asm!("out $0, $1" :: "{dx}"(port), "{ax}"(value) :: "volatile", "intel");
}

#[inline(always)]
pub unsafe fn out32(port: u16, value: u32) {
	asm!("out $0, $1" :: "{dx}"(port), "{eax}"(value) :: "volatile", "intel");
}

#[inline(always)]
pub unsafe fn in8(port: u16) -> u8 { // unsafe since devices change state upon being read
	let r: u8;
	asm!("in $0, $1" : "={al}"(r) : "{dx}"(port) :: "intel");
	r
}

#[inline(always)]
pub unsafe fn in16(port: u16) -> u16 {
	let r: u16;
	asm!("in $0, $1" : "={ax}"(r) : "{dx}"(port) :: "intel");
	r
}

#[inline(always)]
pub unsafe fn in32(port: u16) -> u32 {
	let r: u32;
	asm!("in $0, $1" : "={eax}"(r) : "{dx}"(port) :: "intel");
	r
}
<|endoftext|>


<commit_before>use gfx;
use image;
use image::{
    DynamicImage,
    GenericImage,
    RgbaImage,
};
use texture_lib::ImageSize;

/// Represents a texture.
#[derive(Copy)]
pub struct Texture {
    /// A handle to the Gfx texture.
    pub handle: gfx::TextureHandle<gfx::GlResources>,
}

impl Texture {
    /// Creates a texture from path.
    pub fn from_path<D: gfx::Device>(
        device: &mut D,
        path: &Path
    ) -> Result<Texture, String> {
        let img = match image::open(path) {
            Ok(img) => img,
            Err(e)  => return Err(format!("Could not load '{:?}': {:?}",
                path.filename_str().unwrap(), e)),
        };

        let img = match img {
            DynamicImage::ImageRgba8(img) => img,
            x => x.to_rgba()
        };

        let (width, height) = img.dimensions();
        let texture_info = gfx::tex::TextureInfo {
            width: width as u16,
            height: height as u16,
            depth: 1,
            levels: 1,
            kind: gfx::tex::TextureKind::Texture2D,
            format: gfx::tex::RGBA8,
        };
        let image_info = texture_info.to_image_info();
        let texture = device.create_texture(texture_info).ok().unwrap();
        device.update_texture(&texture, &image_info,
            img.as_slice())
        .ok().unwrap();

        Ok(Texture {
            handle: texture
        })
    }

    /// Creates a texture from image.
    pub fn from_image<D: gfx::Device>(
        device: &mut D,
        image: &RgbaImage
    ) -> Texture {
        let (width, height) = image.dimensions();
        let texture_info = gfx::tex::TextureInfo {
            width: width as u16,
            height: height as u16,
            depth: 1,
            levels: 1,
            kind: gfx::tex::TextureKind::Texture2D,
            format: gfx::tex::RGBA8,
        };
        let image_info = texture_info.to_image_info();
        let texture = device.create_texture(texture_info).ok().unwrap();
        device.update_texture(&texture, &image_info,
            image.as_slice())
        .ok().unwrap();

        Texture {
            handle: texture
        }
    }

    /// Creates a texture from RGBA image.
    pub fn from_rgba8<D: gfx::Device>(
        device: &mut D,
        image: &RgbaImage
    ) -> Texture {
        let (width, height) = img.dimensions();

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

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

        Texture {
            handle: tex,
        }
    }

    /// Creates texture from memory alpha.
    pub fn from_memory_alpha<D: gfx::Device>(
        device: &mut D,
        buffer: &[u8],
        width: u32,
        height: u32,
    ) -> Texture {
        use std::cmp::max;

        let width = max(width, 1);
        let height = max(height, 1);

        let mut pixels = Vec::new();
        for alpha in buffer.iter() {
            pixels.push(255);
            pixels.push(255);
            pixels.push(255);
            pixels.push(*alpha);
        }

        let texture_info = gfx::tex::TextureInfo {
            width: width as u16,
            height: height as u16,
            depth: 1,
            levels: 1,
            kind: gfx::tex::TextureKind::Texture2D,
            format: gfx::tex::RGBA8,
        };

        let image_info = texture_info.to_image_info();
        let texture = device.create_texture(texture_info).ok().unwrap();
        device.update_texture(&texture, &image_info,
            &pixels)
            .ok().unwrap();
        Texture {
            handle: texture
        }
    }

    /// Updates the texture with an image.
    pub fn update<D: gfx::Device>(&mut self, device: &mut D, image: &RgbaImage) {
        device.update_texture(&self.handle,
            &self.handle.get_info().to_image_info(),
            image.as_slice()
        ).ok().unwrap();
    }

    /// Gets the size of the texture.
    #[inline(always)]
    pub fn get_size(&self) -> (u32, u32) {
        let info = self.handle.get_info();
        (info.width as u32, info.height as u32)
    }

    /// Gets the width of the texture.
    #[inline(always)]
    pub fn get_width(&self) -> u32 {
        let (w, _) = self.get_size();
        w
    }

    /// Gets the height of the texture.
    #[inline(always)]
    pub fn get_height(&self) -> u32 {
        let (_, h) = self.get_size();
        h
    }
}

impl ImageSize for Texture {
    #[inline(always)]
    fn get_size(&self) -> (u32, u32) {
        let info = self.handle.get_info();
        (info.width as u32, info.height as u32)
    }
}

<commit_msg>Change `from_rgba8` to `from_image_with_mipmap`<commit_after>use gfx;
use image;
use image::{
    DynamicImage,
    GenericImage,
    RgbaImage,
};
use texture_lib::ImageSize;

/// Represents a texture.
#[derive(Copy)]
pub struct Texture {
    /// A handle to the Gfx texture.
    pub handle: gfx::TextureHandle<gfx::GlResources>,
}

impl Texture {
    /// Creates a texture from path.
    pub fn from_path<D: gfx::Device>(
        device: &mut D,
        path: &Path
    ) -> Result<Texture, String> {
        let img = match image::open(path) {
            Ok(img) => img,
            Err(e)  => return Err(format!("Could not load '{:?}': {:?}",
                path.filename_str().unwrap(), e)),
        };

        let img = match img {
            DynamicImage::ImageRgba8(img) => img,
            x => x.to_rgba()
        };

        let (width, height) = img.dimensions();
        let texture_info = gfx::tex::TextureInfo {
            width: width as u16,
            height: height as u16,
            depth: 1,
            levels: 1,
            kind: gfx::tex::TextureKind::Texture2D,
            format: gfx::tex::RGBA8,
        };
        let image_info = texture_info.to_image_info();
        let texture = device.create_texture(texture_info).ok().unwrap();
        device.update_texture(&texture, &image_info,
            img.as_slice())
        .ok().unwrap();

        Ok(Texture {
            handle: texture
        })
    }

    /// Creates a texture from image.
    pub fn from_image<D: gfx::Device>(
        device: &mut D,
        image: &RgbaImage
    ) -> Texture {
        let (width, height) = image.dimensions();
        let texture_info = gfx::tex::TextureInfo {
            width: width as u16,
            height: height as u16,
            depth: 1,
            levels: 1,
            kind: gfx::tex::TextureKind::Texture2D,
            format: gfx::tex::RGBA8,
        };
        let image_info = texture_info.to_image_info();
        let texture = device.create_texture(texture_info).ok().unwrap();
        device.update_texture(&texture, &image_info,
            image.as_slice())
        .ok().unwrap();

        Texture {
            handle: texture
        }
    }

    /// Creates a texture from image and generates mipmap.
    pub fn from_image_with_mipmap<D: gfx::Device>(
        device: &mut D,
        image: &RgbaImage
    ) -> Texture {
        let texture = Texture::from_image(device, image);
        device.generate_mipmap(&texture.handle);

        texture
    }

    /// Creates texture from memory alpha.
    pub fn from_memory_alpha<D: gfx::Device>(
        device: &mut D,
        buffer: &[u8],
        width: u32,
        height: u32,
    ) -> Texture {
        use std::cmp::max;

        let width = max(width, 1);
        let height = max(height, 1);

        let mut pixels = Vec::new();
        for alpha in buffer.iter() {
            pixels.push(255);
            pixels.push(255);
            pixels.push(255);
            pixels.push(*alpha);
        }

        let texture_info = gfx::tex::TextureInfo {
            width: width as u16,
            height: height as u16,
            depth: 1,
            levels: 1,
            kind: gfx::tex::TextureKind::Texture2D,
            format: gfx::tex::RGBA8,
        };

        let image_info = texture_info.to_image_info();
        let texture = device.create_texture(texture_info).ok().unwrap();
        device.update_texture(&texture, &image_info,
            &pixels)
            .ok().unwrap();
        Texture {
            handle: texture
        }
    }

    /// Updates the texture with an image.
    pub fn update<D: gfx::Device>(&mut self, device: &mut D, image: &RgbaImage) {
        device.update_texture(&self.handle,
            &self.handle.get_info().to_image_info(),
            image.as_slice()
        ).ok().unwrap();
    }

    /// Gets the size of the texture.
    #[inline(always)]
    pub fn get_size(&self) -> (u32, u32) {
        let info = self.handle.get_info();
        (info.width as u32, info.height as u32)
    }

    /// Gets the width of the texture.
    #[inline(always)]
    pub fn get_width(&self) -> u32 {
        let (w, _) = self.get_size();
        w
    }

    /// Gets the height of the texture.
    #[inline(always)]
    pub fn get_height(&self) -> u32 {
        let (_, h) = self.get_size();
        h
    }
}

impl ImageSize for Texture {
    #[inline(always)]
    fn get_size(&self) -> (u32, u32) {
        let info = self.handle.get_info();
        (info.width as u32, info.height as u32)
    }
}

<|endoftext|>


<commit_before>use gl;
use gl::types::GLuint;
use libc::c_void;

use image;
use image::GenericImage;

use graphics::ImageSize;

/// Wraps OpenGL texture data.
/// The texture gets deleted when running out of scope.
///
/// In order to create a texture the function `GenTextures` must be loaded.
/// This is done automatically by the window back-ends in Piston.
pub struct Texture {
    id: GLuint,
    width: u32,
    height: u32,
}

impl Texture {
    /// Creates a new texture.
    #[inline(always)]
    pub fn new(id: GLuint, width: u32, height: u32) -> Texture {
        Texture {
            id: id,
            width: width,
            height: height,
        }
    }

    /// Gets the OpenGL id of the texture.
    #[inline(always)]
    pub fn get_id(&self) -> GLuint {
        self.id
    }

    /// Loads image from memory, the format is 8-bit greyscale.
    pub fn from_memory_alpha(buf: &[u8], width: u32, height: u32) -> Result<Texture, String> {
        let mut pixels = Vec::new();
        for alpha in buf.iter() {
            pixels.push(255);
            pixels.push(255);
            pixels.push(255);
            pixels.push(*alpha);
        }

        let mut id: GLuint = 0;
        unsafe {
            gl::GenTextures(1, &mut id);
            gl::BindTexture(gl::TEXTURE_2D, id);
            gl::TexParameteri(
                gl::TEXTURE_2D,
                gl::TEXTURE_MIN_FILTER,
                gl::LINEAR as i32
            );
            gl::TexParameteri(
                gl::TEXTURE_2D,
                gl::TEXTURE_MAG_FILTER,
                gl::LINEAR as i32
            );
            gl::TexImage2D(
                gl::TEXTURE_2D,
                0,
                gl::RGBA as i32,
                width as i32,
                height as i32,
                0,
                gl::RGBA,
                gl::UNSIGNED_BYTE,
                pixels.as_ptr() as *const c_void
            );
        }

        Ok(Texture::new(id, width, height))
    }

    /// Loads image by relative file name to the asset root.
    pub fn from_path(path: &Path) -> Result<Texture, String> {
        let img = match image::open(path) {
            Ok(img) => img,
            Err(e)  => return Err(format!("Could not load '{}': {}",
                path.filename_str().unwrap(), e)),
        };

        match img.color() {
            image::RGBA(8) => {},
            c => fail!("Unsupported color type {} in png", c),
        };

        let (width, height) = img.dimensions();

        let mut id: GLuint = 0;
        unsafe {
            gl::GenTextures(1, &mut id);
            gl::BindTexture(gl::TEXTURE_2D, id);
            gl::TexParameteri(
                gl::TEXTURE_2D,
                gl::TEXTURE_MIN_FILTER,
                gl::LINEAR as i32
            );
            gl::TexParameteri(
                gl::TEXTURE_2D,
                gl::TEXTURE_MAG_FILTER,
                gl::LINEAR as i32
            );
            gl::TexImage2D(
                gl::TEXTURE_2D,
                0,
                gl::RGBA as i32,
                width as i32,
                height as i32,
                0,
                gl::RGBA,
                gl::UNSIGNED_BYTE,
                img.raw_pixels().as_ptr() as *const c_void
            );
        }

        Ok(Texture::new(id, width, height))
    }

    /// Updates image with a new one.
    pub fn update(&mut self, img: &image::ImageBuf<image::Rgba<u8>>, width: u32, height: u32) {
        gl::BindTexture(gl::TEXTURE_2D, self.id);
        unsafe {
            gl::TexImage2D(
                gl::TEXTURE_2D,
                0,
                gl::RGBA as i32,
                width as i32,
                height as i32,
                0,
                gl::RGBA,
                gl::UNSIGNED_BYTE,
                img.pixelbuf().as_ptr() as *const c_void
            );
        }
    }
}

impl Drop for Texture {
    fn drop(&mut self) {
        unsafe {
            gl::DeleteTextures(1, [self.id].as_ptr());
        }
    }
}

impl ImageSize for Texture {
    fn get_size(&self) -> (u32, u32) {
        (self.width, self.height)
    }
}
<commit_msg>Added from_image<commit_after>use gl;
use gl::types::GLuint;
use libc::c_void;

use image;
use image::GenericImage;

use graphics::ImageSize;

/// Wraps OpenGL texture data.
/// The texture gets deleted when running out of scope.
///
/// In order to create a texture the function `GenTextures` must be loaded.
/// This is done automatically by the window back-ends in Piston.
pub struct Texture {
    id: GLuint,
    width: u32,
    height: u32,
}

impl Texture {
    /// Creates a new texture.
    #[inline(always)]
    pub fn new(id: GLuint, width: u32, height: u32) -> Texture {
        Texture {
            id: id,
            width: width,
            height: height,
        }
    }

    /// Gets the OpenGL id of the texture.
    #[inline(always)]
    pub fn get_id(&self) -> GLuint {
        self.id
    }

    /// Loads image from memory, the format is 8-bit greyscale.
    pub fn from_memory_alpha(buf: &[u8], width: u32, height: u32) -> Result<Texture, String> {
        let mut pixels = Vec::new();
        for alpha in buf.iter() {
            pixels.push(255);
            pixels.push(255);
            pixels.push(255);
            pixels.push(*alpha);
        }

        let mut id: GLuint = 0;
        unsafe {
            gl::GenTextures(1, &mut id);
            gl::BindTexture(gl::TEXTURE_2D, id);
            gl::TexParameteri(
                gl::TEXTURE_2D,
                gl::TEXTURE_MIN_FILTER,
                gl::LINEAR as i32
            );
            gl::TexParameteri(
                gl::TEXTURE_2D,
                gl::TEXTURE_MAG_FILTER,
                gl::LINEAR as i32
            );
            gl::TexImage2D(
                gl::TEXTURE_2D,
                0,
                gl::RGBA as i32,
                width as i32,
                height as i32,
                0,
                gl::RGBA,
                gl::UNSIGNED_BYTE,
                pixels.as_ptr() as *const c_void
            );
        }

        Ok(Texture::new(id, width, height))
    }

    /// Loads image by relative file name to the asset root.
    pub fn from_path(path: &Path) -> Result<Texture, String> {
        let img = match image::open(path) {
            Ok(img) => img,
            Err(e)  => return Err(format!("Could not load '{}': {}",
                path.filename_str().unwrap(), e)),
        };

        match img.color() {
            image::RGBA(8) => {},
            c => fail!("Unsupported color type {} in png", c),
        };

        let (width, height) = img.dimensions();

        let mut id: GLuint = 0;
        unsafe {
            gl::GenTextures(1, &mut id);
            gl::BindTexture(gl::TEXTURE_2D, id);
            gl::TexParameteri(
                gl::TEXTURE_2D,
                gl::TEXTURE_MIN_FILTER,
                gl::LINEAR as i32
            );
            gl::TexParameteri(
                gl::TEXTURE_2D,
                gl::TEXTURE_MAG_FILTER,
                gl::LINEAR as i32
            );
            gl::TexImage2D(
                gl::TEXTURE_2D,
                0,
                gl::RGBA as i32,
                width as i32,
                height as i32,
                0,
                gl::RGBA,
                gl::UNSIGNED_BYTE,
                img.raw_pixels().as_ptr() as *const c_void
            );
        }

        Ok(Texture::new(id, width, height))
    }

    /// Creates a texture from image.
    pub fn from_image(img: &image::ImageBuf<image::Rgba<u8>>) -> Texture {
        let (width, height) = img.dimensions();

        let mut id: GLuint = 0;
        unsafe {
            gl::GenTextures(1, &mut id);
            gl::BindTexture(gl::TEXTURE_2D, id);
            gl::TexParameteri(
                gl::TEXTURE_2D,
                gl::TEXTURE_MIN_FILTER,
                gl::LINEAR as i32
            );
            gl::TexParameteri(
                gl::TEXTURE_2D,
                gl::TEXTURE_MAG_FILTER,
                gl::LINEAR as i32
            );
            gl::TexImage2D(
                gl::TEXTURE_2D,
                0,
                gl::RGBA as i32,
                width as i32,
                height as i32,
                0,
                gl::RGBA,
                gl::UNSIGNED_BYTE,
                img.pixelbuf().as_ptr() as *const c_void
            );
        }

        Texture::new(id, width, height)
    }

    /// Updates image with a new one.
    pub fn update(&mut self, img: &image::ImageBuf<image::Rgba<u8>>) {
        let (width, height) = img.dimensions();

        gl::BindTexture(gl::TEXTURE_2D, self.id);
        unsafe {
            gl::TexImage2D(
                gl::TEXTURE_2D,
                0,
                gl::RGBA as i32,
                width as i32,
                height as i32,
                0,
                gl::RGBA,
                gl::UNSIGNED_BYTE,
                img.pixelbuf().as_ptr() as *const c_void
            );
        }
    }
}

impl Drop for Texture {
    fn drop(&mut self) {
        unsafe {
            gl::DeleteTextures(1, [self.id].as_ptr());
        }
    }
}

impl ImageSize for Texture {
    fn get_size(&self) -> (u32, u32) {
        (self.width, self.height)
    }
}
<|endoftext|>


<commit_before>use dailyschedule::{Handler, Schedule};
use std::cell::{Cell, RefCell};
use std::collections::BTreeMap;
use std::path;
use std::rc::Rc;
use super::config;
use super::serial;
use time::{Duration, Timespec, at_utc, at};
use zoneinfo::ZoneInfo;
use ticker::Ticker;
use std::sync::mpsc::{channel, Sender};
use std::thread;

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

struct Switch {
    serial: serial::SerialClient,
    alias: String,
    last_on: Cell<Timespec>,
    state: Cell<Context>,
    valid_events: RefCell<BTreeMap<Timespec, Context>>,
    /// when "hot" perform actual relay operations
    hot: Cell<bool>,
}

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

impl Switch {
    fn set_switch_state(&self, state: Context) {
        self.state.set(state);
        self.dispatch_context();
    }

    fn dispatch_context(&self) {
        if self.hot.get() {
            match self.state.get() {
                Context::Off => self.serial.switch_off(&self.alias[..]),
                Context::On => self.serial.switch_on(&self.alias[..]),
            }
        }
    }

    fn make_hot(&self) {
        self.hot.set(true);
        self.dispatch_context();
    }
}

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

    /// Perform action only when the timestamp is considered valid;
    /// Remove the current or prior timestamps from the expected timestamps.
    fn kick(&self, ts: &Timespec, context: &Context) {
        if self.valid_events.borrow().contains_key(ts) {
            println!("XXX: {} {:?} {}", at(*ts).asctime(), context, self.alias); // XXX
            self.set_switch_state(*context);

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

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

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

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

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

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

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

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

        tracker.load_schedule(config);

        tracker
    }

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

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

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

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

        self.schedule.kick_event(timestamp);

        if self.initial {
            self.initial = false;
            // configure the switch to actually set the relay (otherwise the initial kicks will
            // quickly toggle switches unintendedly
            for (_, ref mut switch) in self.switches.iter_mut() {
                switch.make_hot();
            }
        }
    }
}

#[derive(Copy, Clone)]
enum Message {
    Tick,
    Teardown,
}

pub struct Tracker {
    tx: Sender<(Message, Option<Timespec>)>,
    join: thread::JoinHandle<()>,
}

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

        let joiner = thread::spawn(move || {
            let config = config::Config::new(&configfile).unwrap(); // XXX
            let mut tracker = TrackerInner::new(&config, &zoneinfo);
            let ticker = Ticker::spawn(&config.device.ntp_server,
                                       Duration::seconds(10),
                                       Duration::days(1),
                                       Message::Tick);

            tx.send(ticker.get_sender()).ok().expect("BUG: tracker thread unable to communicate with spawner");

            for (event, timestamp) in ticker.recv_iter() {
                match event {
                    Message::Tick => {
                        if let Some(timestamp) = timestamp {
                            println!("tick: {}", at(timestamp).asctime());
                            tracker.process_tick(timestamp);
                        }
                    },
                    Message::Teardown => {
                        break;
                    },
                }
            }
            ticker.stop_ticker();
        });

        let sender = rx.recv().ok().expect("BUG: tracker thread unable to bootstrap");

        Tracker {
            tx: sender,
            join: joiner
        }
    }

    pub fn teardown(self) {
        (&self).tx.send((Message::Teardown, None)).ok().expect("BUG: not able to shutdown tracker");
        self.join();
    }

    pub fn join(self) {
        self.join.join().ok().expect("BUG: not able to join tracker");
    }
}
<commit_msg>tracker: add some missing serial stuff<commit_after>use dailyschedule::{Handler, Schedule};
use std::cell::{Cell, RefCell};
use std::collections::BTreeMap;
use std::path;
use std::rc::Rc;
use super::config;
use super::serial;
use time::{Duration, Timespec, at_utc, at};
use zoneinfo::ZoneInfo;
use ticker::Ticker;
use std::sync::mpsc::{channel, Sender};
use std::thread;

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

struct Switch {
    serial: serial::SerialClient,
    alias: String,
    last_on: Cell<Timespec>,
    state: Cell<Context>,
    valid_events: RefCell<BTreeMap<Timespec, Context>>,
    /// when "hot" perform actual relay operations
    hot: Cell<bool>,
}

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

impl Switch {
    fn set_switch_state(&self, state: Context) {
        self.state.set(state);
        self.dispatch_context();
    }

    fn dispatch_context(&self) {
        if self.hot.get() {
            match self.state.get() {
                Context::Off => self.serial.switch_off(&self.alias[..]),
                Context::On => self.serial.switch_on(&self.alias[..]),
            }
        }
    }

    fn make_hot(&self) {
        self.hot.set(true);
        self.dispatch_context();
    }
}

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

    /// Perform action only when the timestamp is considered valid;
    /// Remove the current or prior timestamps from the expected timestamps.
    fn kick(&self, ts: &Timespec, context: &Context) {
        if self.valid_events.borrow().contains_key(ts) {
            println!("XXX: {} {:?} {}", at(*ts).asctime(), context, self.alias); // XXX
            self.set_switch_state(*context);

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

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

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

impl TrackerInner {
    fn load_schedule(&mut self, config: &config::Config) {
        self.switches.clear();
        match config.device.serial_device {
            None => self.serial.connect_stub(),
            Some(ref dev) => self.serial.connect_device(&dev[..]).unwrap() // XXX
        }
        for circle in config.circles.iter() {
            self.serial.register_circle(&circle.alias, circle.mac);
            let switch = Rc::new(Switch::new(circle.alias.clone(), self.serial.clone()));
            match circle.default {
                config::CircleSetting::On => switch.set_switch_state(Context::On),
                config::CircleSetting::Off => switch.set_switch_state(Context::Off),
                config::CircleSetting::Schedule => {
                    for toggle in circle.toggles.iter() {
                        let start = toggle.start.into_dailyevent(&config.device);
                        let end = toggle.end.into_dailyevent(&config.device);

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

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

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

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

        tracker.load_schedule(config);

        tracker
    }

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

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

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

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

        self.schedule.kick_event(timestamp);

        if self.initial {
            self.initial = false;
            // configure the switch to actually set the relay (otherwise the initial kicks will
            // quickly toggle switches unintendedly
            for (_, ref mut switch) in self.switches.iter_mut() {
                switch.make_hot();
            }
        }
    }
}

#[derive(Copy, Clone)]
enum Message {
    Tick,
    Teardown,
}

pub struct Tracker {
    tx: Sender<(Message, Option<Timespec>)>,
    join: thread::JoinHandle<()>,
}

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

        let joiner = thread::spawn(move || {
            let config = config::Config::new(&configfile).unwrap(); // XXX
            let mut tracker = TrackerInner::new(&config, &zoneinfo);
            let ticker = Ticker::spawn(&config.device.ntp_server,
                                       Duration::seconds(10),
                                       Duration::days(1),
                                       Message::Tick);

            tx.send(ticker.get_sender()).ok().expect("BUG: tracker thread unable to communicate with spawner");

            for (event, timestamp) in ticker.recv_iter() {
                match event {
                    Message::Tick => {
                        if let Some(timestamp) = timestamp {
                            println!("tick: {}", at(timestamp).asctime());
                            tracker.process_tick(timestamp);
                        }
                    },
                    Message::Teardown => {
                        break;
                    },
                }
            }
            ticker.stop_ticker();
        });

        let sender = rx.recv().ok().expect("BUG: tracker thread unable to bootstrap");

        Tracker {
            tx: sender,
            join: joiner
        }
    }

    pub fn teardown(self) {
        (&self).tx.send((Message::Teardown, None)).ok().expect("BUG: not able to shutdown tracker");
        self.join();
    }

    pub fn join(self) {
        self.join.join().ok().expect("BUG: not able to join tracker");
    }
}
<|endoftext|>


<commit_before>#![feature(core, collections, custom_attribute)]

#[cfg(test)]
extern crate quickcheck;
#[cfg(test)]
#[macro_use]
#[no_link]
extern crate quickcheck_macros;
#[cfg(test)]
extern crate rand;

use std::iter::range_inclusive;
use std::cmp::min;

use Base32Type::{RFC4648Base32, CrockfordBase32, UnpaddedRFC4648Base32};

use std::ascii::AsciiExt;

#[derive(Copy)]
pub enum Base32Type {
    // Not sure if it's better to just have an additional `padding: bool`
    // argument to the encode function
    RFC4648Base32, CrockfordBase32, UnpaddedRFC4648Base32
}

const RFC4648_ALPHABET: &'static [u8]   = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
const CROCKFORD_ALPHABET: &'static [u8] = b"0123456789ABCDEFGHJKMNPQRSTVWXYZ";

pub fn encode(base32_type: Base32Type, data: &[u8]) -> String {
    let alphabet = match base32_type {
        RFC4648Base32 | UnpaddedRFC4648Base32 => RFC4648_ALPHABET,
        CrockfordBase32 => CROCKFORD_ALPHABET
    };
    let mut ret = Vec::with_capacity((data.len()+3)/4*5);

    for chunk in data.chunks(5) {
        let buf = {
            let mut buf = [0u8; 5];
            buf.clone_from_slice(chunk);
            buf
        };
        ret.push(alphabet[((buf[0] & 0xF8) >> 3) as usize]);
        ret.push(alphabet[(((buf[0] & 0x07) << 2) | ((buf[1] & 0xC0) >> 6)) as usize]);
        ret.push(alphabet[((buf[1] & 0x3E) >> 1) as usize]);
        ret.push(alphabet[(((buf[1] & 0x01) << 4) | ((buf[2] & 0xF0) >> 4)) as usize]);
        ret.push(alphabet[(((buf[2] & 0x0F) << 1) | (buf[3] >> 7)) as usize]);
        ret.push(alphabet[((buf[3] & 0x7C) >> 2) as usize]);
        ret.push(alphabet[(((buf[3] & 0x03) << 3) | ((buf[4] & 0xE0) >> 5)) as usize]);
        ret.push(alphabet[(buf[4] & 0x1F) as usize]);
    }

    if data.len() % 5 != 0 {
        let len = ret.len();
        let num_extra = 8-(data.len()%5*8+4)/5;
        match base32_type {
            UnpaddedRFC4648Base32 | CrockfordBase32 => {
                ret.truncate(len-num_extra);
            }
            RFC4648Base32 => {
                for i in range_inclusive(1, num_extra) {
                    ret[len-i] = b'=';
                }
            }
        }
    }

    String::from_utf8(ret).unwrap()
}

const RFC4648_INV_ALPHABET: [u8; 43] = [-1, -1, 26, 27, 28, 29, 30, 31, -1, -1, -1, -1, -1, 0, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25];

const CROCKFORD_INV_ALPHABET: [u8; 43] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15, 16, 17, 1, 18, 19, 1, 20, 21, 0, 22, 23, 24, 25, 26, -1, 27, 28, 29, 30, 31];

pub fn decode(base32_type: Base32Type, data: &str) -> Option<Vec<u8>> {
    if !data.is_ascii() {
        return None;
    }
    let data = data.as_bytes();
    let alphabet = match base32_type {
        RFC4648Base32 | UnpaddedRFC4648Base32 => RFC4648_INV_ALPHABET,
        CrockfordBase32 => CROCKFORD_INV_ALPHABET
    };
    let mut unpadded_data_length = data.len();
    for i in range_inclusive(1, min(6, data.len())) {
        if data[data.len() - i] != b'=' {
            break;
        }
        unpadded_data_length -= 1;
    }
    let output_length = unpadded_data_length*5/8;
    let mut ret = Vec::with_capacity((output_length+4)/5*5);
    for chunk in data.chunks(8) {
        let buf = {
            let mut buf = [0u8; 8];
            for (i, &c) in chunk.iter().enumerate() {
                match alphabet.get(c.to_ascii_uppercase().wrapping_sub(b'0') as usize) {
                    Some(&-1) | None => return None,
                    Some(&value) => buf[i] = value,
                };
            }
            buf
        };
        ret.push((buf[0] << 3) | (buf[1] >> 2));
        ret.push((buf[1] << 6) | (buf[2] << 1) | (buf[3] >> 4));
        ret.push((buf[3] << 4) | (buf[4] >> 1));
        ret.push((buf[4] << 7) | (buf[5] << 2) | (buf[6] >> 3));
        ret.push((buf[6] << 5) | buf[7]);
    }
    ret.truncate(output_length);
    Some(ret)
}

#[cfg(test)]
mod test {
    extern crate test;
    use super::{encode, decode};
    use super::Base32Type::{CrockfordBase32, RFC4648Base32, UnpaddedRFC4648Base32};
    use quickcheck;
    use std;
    use std::ascii::AsciiExt;
    use rand::distributions::{IndependentSample, Range};

    #[derive(Clone)]
    struct B32 {
        c: u8
    }

    impl quickcheck::Arbitrary for B32 {
        fn arbitrary<G: quickcheck::Gen>(g: &mut G) -> B32 {
            let alphabet = b"0123456789ABCDEFGHJKMNPQRSTVWXYZ";
            B32 {
                c: alphabet[Range::new(0, alphabet.len()).ind_sample(g)]
            }
        }
    }

    impl std::fmt::Display for B32 {
        fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
            self.c.fmt(f)
        }
    }

    #[test]
    fn masks_crockford() {
        assert_eq!(encode(CrockfordBase32, &[0xF8, 0x3E, 0x0F, 0x83, 0xE0]), "Z0Z0Z0Z0");
        assert_eq!(encode(CrockfordBase32, &[0x07, 0xC1, 0xF0, 0x7C, 0x1F]), "0Z0Z0Z0Z");
        assert_eq!(decode(CrockfordBase32, "Z0Z0Z0Z0").unwrap(), [0xF8, 0x3E, 0x0F, 0x83, 0xE0]);
        assert_eq!(decode(CrockfordBase32, "0Z0Z0Z0Z").unwrap(), [0x07, 0xC1, 0xF0, 0x7C, 0x1F]);
    }

    #[test]
    fn masks_rfc4648() {
        assert_eq!(encode(RFC4648Base32, &[0xF8, 0x3E, 0x7F, 0x83, 0xE7]), "7A7H7A7H");
        assert_eq!(encode(RFC4648Base32, &[0x77, 0xC1, 0xF7, 0x7C, 0x1F]), "O7A7O7A7");
        assert_eq!(decode(RFC4648Base32, "7A7H7A7H").unwrap(), [0xF8, 0x3E, 0x7F, 0x83, 0xE7]);
        assert_eq!(decode(RFC4648Base32, "O7A7O7A7").unwrap(), [0x77, 0xC1, 0xF7, 0x7C, 0x1F]);
        assert_eq!(encode(RFC4648Base32, &[0xF8, 0x3E, 0x7F, 0x83]), "7A7H7AY=");
    }

    #[test]
    fn masks_unpadded_rfc4648() {
        assert_eq!(encode(UnpaddedRFC4648Base32, &[0xF8, 0x3E, 0x7F, 0x83, 0xE7]), "7A7H7A7H");
        assert_eq!(encode(UnpaddedRFC4648Base32, &[0x77, 0xC1, 0xF7, 0x7C, 0x1F]), "O7A7O7A7");
        assert_eq!(decode(UnpaddedRFC4648Base32, "7A7H7A7H").unwrap(), [0xF8, 0x3E, 0x7F, 0x83, 0xE7]);
        assert_eq!(decode(UnpaddedRFC4648Base32, "O7A7O7A7").unwrap(), [0x77, 0xC1, 0xF7, 0x7C, 0x1F]);
        assert_eq!(encode(UnpaddedRFC4648Base32, &[0xF8, 0x3E, 0x7F, 0x83]), "7A7H7AY");
    }

    #[test]
    fn padding() {
        let num_padding = [0, 6, 4, 3, 1];
        for i in 1..6 {
            println!("Checking padding for length == {}", i);
            let encoded = encode(RFC4648Base32, (0..(i as u8)).collect::<Vec<u8>>().as_slice());
            assert_eq!(encoded.len(), 8);
            for j in 0..(num_padding[i % 5]) {
                println!("Making sure index {} is padding", encoded.len()-j-1);
                assert_eq!(encoded.as_bytes()[encoded.len()-j-1], b'=');
            }
            for j in 0..(8 - num_padding[i % 5]) {
                println!("Making sure index {} is not padding", j);
                assert!(encoded.as_bytes()[j] != b'=');
            }
        }
    }

    #[quickcheck]
    fn invertible_crockford(data: Vec<u8>) -> bool {
        decode(CrockfordBase32, encode(CrockfordBase32, data.as_slice()).as_slice()).unwrap() == data
    }

    #[quickcheck]
    fn invertible_rfc4648(data: Vec<u8>) -> bool {
        decode(RFC4648Base32, encode(RFC4648Base32, data.as_slice()).as_slice()).unwrap() == data
    }
    #[quickcheck]
    fn invertible_unpadded_rfc4648(data: Vec<u8>) -> bool {
        decode(UnpaddedRFC4648Base32, encode(UnpaddedRFC4648Base32, data.as_slice()).as_slice()).unwrap() == data
    }

    #[quickcheck]
    fn lower_case(data: Vec<B32>) -> bool {
        let data: String = data.iter().map(|e| e.c as char).collect();
        decode(CrockfordBase32, data.as_slice()) == decode(CrockfordBase32, data.as_slice().to_ascii_lowercase().as_slice())
    }

    #[test]
    #[allow(non_snake_case)]
    fn iIlL1_oO0() {
        assert_eq!(decode(CrockfordBase32, "IiLlOo"), decode(CrockfordBase32, "111100"));
    }

    #[test]
    fn invalid_chars_crockford() {
        assert_eq!(decode(CrockfordBase32, ","), None)
    }

    #[test]
    fn invalid_chars_rfc4648() {
        assert_eq!(decode(RFC4648Base32, ","), None)
    }

    #[test]
    fn invalid_chars_unpadded_rfc4648() {
        assert_eq!(decode(UnpaddedRFC4648Base32, ","), None)
    }

    #[bench]
    fn bench_encode(b: &mut test::Bencher) {
        let data = [0, 0, 0, 0, 0];
        b.iter(|| encode(CrockfordBase32, data.as_slice()));
        b.bytes = data.len() as u64;
    }

    #[bench]
    fn bench_decode(b: &mut test::Bencher) {
        let data = "00000000";
        b.iter(|| decode(CrockfordBase32, data));
        b.bytes = data.len() as u64;
    }
}
<commit_msg>fix warnings<commit_after>#![feature(core, collections, custom_attribute)]
#![cfg_attr(test, feature(test, convert))]

#[cfg(test)]
extern crate quickcheck;
#[cfg(test)]
#[macro_use]
#[no_link]
extern crate quickcheck_macros;
#[cfg(test)]
extern crate rand;

use std::iter::range_inclusive;
use std::cmp::min;

use Base32Type::{RFC4648Base32, CrockfordBase32, UnpaddedRFC4648Base32};

use std::ascii::AsciiExt;

#[derive(Copy)]
pub enum Base32Type {
    // Not sure if it's better to just have an additional `padding: bool`
    // argument to the encode function
    RFC4648Base32, CrockfordBase32, UnpaddedRFC4648Base32
}

const RFC4648_ALPHABET: &'static [u8]   = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
const CROCKFORD_ALPHABET: &'static [u8] = b"0123456789ABCDEFGHJKMNPQRSTVWXYZ";

pub fn encode(base32_type: Base32Type, data: &[u8]) -> String {
    let alphabet = match base32_type {
        RFC4648Base32 | UnpaddedRFC4648Base32 => RFC4648_ALPHABET,
        CrockfordBase32 => CROCKFORD_ALPHABET
    };
    let mut ret = Vec::with_capacity((data.len()+3)/4*5);

    for chunk in data.chunks(5) {
        let buf = {
            let mut buf = [0u8; 5];
            buf.clone_from_slice(chunk);
            buf
        };
        ret.push(alphabet[((buf[0] & 0xF8) >> 3) as usize]);
        ret.push(alphabet[(((buf[0] & 0x07) << 2) | ((buf[1] & 0xC0) >> 6)) as usize]);
        ret.push(alphabet[((buf[1] & 0x3E) >> 1) as usize]);
        ret.push(alphabet[(((buf[1] & 0x01) << 4) | ((buf[2] & 0xF0) >> 4)) as usize]);
        ret.push(alphabet[(((buf[2] & 0x0F) << 1) | (buf[3] >> 7)) as usize]);
        ret.push(alphabet[((buf[3] & 0x7C) >> 2) as usize]);
        ret.push(alphabet[(((buf[3] & 0x03) << 3) | ((buf[4] & 0xE0) >> 5)) as usize]);
        ret.push(alphabet[(buf[4] & 0x1F) as usize]);
    }

    if data.len() % 5 != 0 {
        let len = ret.len();
        let num_extra = 8-(data.len()%5*8+4)/5;
        match base32_type {
            UnpaddedRFC4648Base32 | CrockfordBase32 => {
                ret.truncate(len-num_extra);
            }
            RFC4648Base32 => {
                for i in range_inclusive(1, num_extra) {
                    ret[len-i] = b'=';
                }
            }
        }
    }

    String::from_utf8(ret).unwrap()
}

const RFC4648_INV_ALPHABET: [u8; 43] = [-1, -1, 26, 27, 28, 29, 30, 31, -1, -1, -1, -1, -1, 0, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25];

const CROCKFORD_INV_ALPHABET: [u8; 43] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15, 16, 17, 1, 18, 19, 1, 20, 21, 0, 22, 23, 24, 25, 26, -1, 27, 28, 29, 30, 31];

pub fn decode(base32_type: Base32Type, data: &str) -> Option<Vec<u8>> {
    if !data.is_ascii() {
        return None;
    }
    let data = data.as_bytes();
    let alphabet = match base32_type {
        RFC4648Base32 | UnpaddedRFC4648Base32 => RFC4648_INV_ALPHABET,
        CrockfordBase32 => CROCKFORD_INV_ALPHABET
    };
    let mut unpadded_data_length = data.len();
    for i in range_inclusive(1, min(6, data.len())) {
        if data[data.len() - i] != b'=' {
            break;
        }
        unpadded_data_length -= 1;
    }
    let output_length = unpadded_data_length*5/8;
    let mut ret = Vec::with_capacity((output_length+4)/5*5);
    for chunk in data.chunks(8) {
        let buf = {
            let mut buf = [0u8; 8];
            for (i, &c) in chunk.iter().enumerate() {
                match alphabet.get(c.to_ascii_uppercase().wrapping_sub(b'0') as usize) {
                    Some(&-1) | None => return None,
                    Some(&value) => buf[i] = value,
                };
            }
            buf
        };
        ret.push((buf[0] << 3) | (buf[1] >> 2));
        ret.push((buf[1] << 6) | (buf[2] << 1) | (buf[3] >> 4));
        ret.push((buf[3] << 4) | (buf[4] >> 1));
        ret.push((buf[4] << 7) | (buf[5] << 2) | (buf[6] >> 3));
        ret.push((buf[6] << 5) | buf[7]);
    }
    ret.truncate(output_length);
    Some(ret)
}

#[cfg(test)]
#[allow(dead_code, unused_attributes)]
mod test {
    extern crate test;
    use super::{encode, decode};
    use super::Base32Type::{CrockfordBase32, RFC4648Base32, UnpaddedRFC4648Base32};
    use quickcheck;
    use std;
    use std::ascii::AsciiExt;
    use rand::distributions::{IndependentSample, Range};

    #[derive(Clone)]
    struct B32 {
        c: u8
    }

    impl quickcheck::Arbitrary for B32 {
        fn arbitrary<G: quickcheck::Gen>(g: &mut G) -> B32 {
            let alphabet = b"0123456789ABCDEFGHJKMNPQRSTVWXYZ";
            B32 {
                c: alphabet[Range::new(0, alphabet.len()).ind_sample(g)]
            }
        }
    }

    impl std::fmt::Display for B32 {
        fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
            self.c.fmt(f)
        }
    }

    #[test]
    fn masks_crockford() {
        assert_eq!(encode(CrockfordBase32, &[0xF8, 0x3E, 0x0F, 0x83, 0xE0]), "Z0Z0Z0Z0");
        assert_eq!(encode(CrockfordBase32, &[0x07, 0xC1, 0xF0, 0x7C, 0x1F]), "0Z0Z0Z0Z");
        assert_eq!(decode(CrockfordBase32, "Z0Z0Z0Z0").unwrap(), [0xF8, 0x3E, 0x0F, 0x83, 0xE0]);
        assert_eq!(decode(CrockfordBase32, "0Z0Z0Z0Z").unwrap(), [0x07, 0xC1, 0xF0, 0x7C, 0x1F]);
    }

    #[test]
    fn masks_rfc4648() {
        assert_eq!(encode(RFC4648Base32, &[0xF8, 0x3E, 0x7F, 0x83, 0xE7]), "7A7H7A7H");
        assert_eq!(encode(RFC4648Base32, &[0x77, 0xC1, 0xF7, 0x7C, 0x1F]), "O7A7O7A7");
        assert_eq!(decode(RFC4648Base32, "7A7H7A7H").unwrap(), [0xF8, 0x3E, 0x7F, 0x83, 0xE7]);
        assert_eq!(decode(RFC4648Base32, "O7A7O7A7").unwrap(), [0x77, 0xC1, 0xF7, 0x7C, 0x1F]);
        assert_eq!(encode(RFC4648Base32, &[0xF8, 0x3E, 0x7F, 0x83]), "7A7H7AY=");
    }

    #[test]
    fn masks_unpadded_rfc4648() {
        assert_eq!(encode(UnpaddedRFC4648Base32, &[0xF8, 0x3E, 0x7F, 0x83, 0xE7]), "7A7H7A7H");
        assert_eq!(encode(UnpaddedRFC4648Base32, &[0x77, 0xC1, 0xF7, 0x7C, 0x1F]), "O7A7O7A7");
        assert_eq!(decode(UnpaddedRFC4648Base32, "7A7H7A7H").unwrap(), [0xF8, 0x3E, 0x7F, 0x83, 0xE7]);
        assert_eq!(decode(UnpaddedRFC4648Base32, "O7A7O7A7").unwrap(), [0x77, 0xC1, 0xF7, 0x7C, 0x1F]);
        assert_eq!(encode(UnpaddedRFC4648Base32, &[0xF8, 0x3E, 0x7F, 0x83]), "7A7H7AY");
    }

    #[test]
    fn padding() {
        let num_padding = [0, 6, 4, 3, 1];
        for i in 1..6 {
            let encoded = encode(RFC4648Base32, (0..(i as u8)).collect::<Vec<u8>>().as_ref());
            assert_eq!(encoded.len(), 8);
            for j in 0..(num_padding[i % 5]) {
                assert_eq!(encoded.as_bytes()[encoded.len()-j-1], b'=');
            }
            for j in 0..(8 - num_padding[i % 5]) {
                assert!(encoded.as_bytes()[j] != b'=');
            }
        }
    }

    #[quickcheck]
    fn invertible_crockford(data: Vec<u8>) -> bool {
        decode(CrockfordBase32, encode(CrockfordBase32, data.as_ref()).as_ref()).unwrap() == data
    }

    #[quickcheck]
    fn invertible_rfc4648(data: Vec<u8>) -> bool {
        decode(RFC4648Base32, encode(RFC4648Base32, data.as_ref()).as_ref()).unwrap() == data
    }
    #[quickcheck]
    fn invertible_unpadded_rfc4648(data: Vec<u8>) -> bool {
        decode(UnpaddedRFC4648Base32, encode(UnpaddedRFC4648Base32, data.as_ref()).as_ref()).unwrap() == data
    }

    #[quickcheck]
    fn lower_case(data: Vec<B32>) -> bool {
        let data: String = data.iter().map(|e| e.c as char).collect();
        decode(CrockfordBase32, data.as_ref()) == decode(CrockfordBase32, data.to_ascii_lowercase().as_ref())
    }

    #[test]
    #[allow(non_snake_case)]
    fn iIlL1_oO0() {
        assert_eq!(decode(CrockfordBase32, "IiLlOo"), decode(CrockfordBase32, "111100"));
    }

    #[test]
    fn invalid_chars_crockford() {
        assert_eq!(decode(CrockfordBase32, ","), None)
    }

    #[test]
    fn invalid_chars_rfc4648() {
        assert_eq!(decode(RFC4648Base32, ","), None)
    }

    #[test]
    fn invalid_chars_unpadded_rfc4648() {
        assert_eq!(decode(UnpaddedRFC4648Base32, ","), None)
    }

    #[bench]
    fn bench_encode(b: &mut test::Bencher) {
        let data = [0, 0, 0, 0, 0];
        b.iter(|| encode(CrockfordBase32, data.as_ref()));
        b.bytes = data.len() as u64;
    }

    #[bench]
    fn bench_decode(b: &mut test::Bencher) {
        let data = "00000000";
        b.iter(|| decode(CrockfordBase32, data));
        b.bytes = data.len() as u64;
    }
}
<|endoftext|>


<commit_before>use std::{
    fmt, time,
    sync::Arc,
};

use rustc_hash::FxHashMap;
use ra_db::{
    SourceRootId, FileId, CrateGraph, SourceDatabase, SourceRoot,
    salsa::{Database, SweepStrategy},
};
use ra_syntax::SourceFile;
use ra_prof::profile;
use relative_path::RelativePathBuf;
use rayon::prelude::*;

use crate::{
    db::RootDatabase,
    symbol_index::{SymbolIndex, SymbolsDatabase},
    status::syntax_tree_stats,
};

#[derive(Default)]
pub struct AnalysisChange {
    new_roots: Vec<(SourceRootId, bool)>,
    roots_changed: FxHashMap<SourceRootId, RootChange>,
    files_changed: Vec<(FileId, Arc<String>)>,
    libraries_added: Vec<LibraryData>,
    crate_graph: Option<CrateGraph>,
}

impl fmt::Debug for AnalysisChange {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        let mut d = fmt.debug_struct("AnalysisChange");
        if !self.new_roots.is_empty() {
            d.field("new_roots", &self.new_roots);
        }
        if !self.roots_changed.is_empty() {
            d.field("roots_changed", &self.roots_changed);
        }
        if !self.files_changed.is_empty() {
            d.field("files_changed", &self.files_changed.len());
        }
        if !self.libraries_added.is_empty() {
            d.field("libraries_added", &self.libraries_added.len());
        }
        if !self.crate_graph.is_some() {
            d.field("crate_graph", &self.crate_graph);
        }
        d.finish()
    }
}

impl AnalysisChange {
    pub fn new() -> AnalysisChange {
        AnalysisChange::default()
    }

    pub fn add_root(&mut self, root_id: SourceRootId, is_local: bool) {
        self.new_roots.push((root_id, is_local));
    }

    pub fn add_file(
        &mut self,
        root_id: SourceRootId,
        file_id: FileId,
        path: RelativePathBuf,
        text: Arc<String>,
    ) {
        let file = AddFile { file_id, path, text };
        self.roots_changed.entry(root_id).or_default().added.push(file);
    }

    pub fn change_file(&mut self, file_id: FileId, new_text: Arc<String>) {
        self.files_changed.push((file_id, new_text))
    }

    pub fn remove_file(&mut self, root_id: SourceRootId, file_id: FileId, path: RelativePathBuf) {
        let file = RemoveFile { file_id, path };
        self.roots_changed.entry(root_id).or_default().removed.push(file);
    }

    pub fn add_library(&mut self, data: LibraryData) {
        self.libraries_added.push(data)
    }

    pub fn set_crate_graph(&mut self, graph: CrateGraph) {
        self.crate_graph = Some(graph);
    }
}

#[derive(Debug)]
struct AddFile {
    file_id: FileId,
    path: RelativePathBuf,
    text: Arc<String>,
}

#[derive(Debug)]
struct RemoveFile {
    file_id: FileId,
    path: RelativePathBuf,
}

#[derive(Default)]
struct RootChange {
    added: Vec<AddFile>,
    removed: Vec<RemoveFile>,
}

impl fmt::Debug for RootChange {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt.debug_struct("AnalysisChange")
            .field("added", &self.added.len())
            .field("removed", &self.removed.len())
            .finish()
    }
}

pub struct LibraryData {
    root_id: SourceRootId,
    root_change: RootChange,
    symbol_index: SymbolIndex,
}

impl fmt::Debug for LibraryData {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("LibraryData")
            .field("root_id", &self.root_id)
            .field("root_change", &self.root_change)
            .field("n_symbols", &self.symbol_index.len())
            .finish()
    }
}

impl LibraryData {
    pub fn prepare(
        root_id: SourceRootId,
        files: Vec<(FileId, RelativePathBuf, Arc<String>)>,
    ) -> LibraryData {
        let symbol_index = SymbolIndex::for_files(files.par_iter().map(|(file_id, _, text)| {
            let file = SourceFile::parse(text);
            (*file_id, file)
        }));
        let mut root_change = RootChange::default();
        root_change.added = files
            .into_iter()
            .map(|(file_id, path, text)| AddFile { file_id, path, text })
            .collect();
        LibraryData { root_id, root_change, symbol_index }
    }
}

const GC_COOLDOWN: time::Duration = time::Duration::from_millis(100);

impl RootDatabase {
    pub(crate) fn apply_change(&mut self, change: AnalysisChange) {
        let _p = profile("RootDatabase::apply_change");
        log::info!("apply_change {:?}", change);
        if !change.new_roots.is_empty() {
            let mut local_roots = Vec::clone(&self.local_roots());
            for (root_id, is_local) in change.new_roots {
                self.set_source_root(root_id, Default::default());
                if is_local {
                    local_roots.push(root_id);
                }
            }
            self.set_local_roots(Arc::new(local_roots));
        }

        for (root_id, root_change) in change.roots_changed {
            self.apply_root_change(root_id, root_change);
        }
        for (file_id, text) in change.files_changed {
            self.set_file_text(file_id, text)
        }
        if !change.libraries_added.is_empty() {
            let mut libraries = Vec::clone(&self.library_roots());
            for library in change.libraries_added {
                libraries.push(library.root_id);
                self.set_source_root(library.root_id, Default::default());
                self.set_constant_library_symbols(library.root_id, Arc::new(library.symbol_index));
                self.apply_root_change(library.root_id, library.root_change);
            }
            self.set_library_roots(Arc::new(libraries));
        }
        if let Some(crate_graph) = change.crate_graph {
            self.set_crate_graph(Arc::new(crate_graph))
        }
        self.collect_after_change()
    }

    fn apply_root_change(&mut self, root_id: SourceRootId, root_change: RootChange) {
        let mut source_root = SourceRoot::clone(&self.source_root(root_id));
        for add_file in root_change.added {
            self.set_file_text(add_file.file_id, add_file.text);
            self.set_file_relative_path(add_file.file_id, add_file.path.clone());
            self.set_file_source_root(add_file.file_id, root_id);
            source_root.files.insert(add_file.path, add_file.file_id);
        }
        for remove_file in root_change.removed {
            self.set_file_text(remove_file.file_id, Default::default());
            source_root.files.remove(&remove_file.path);
        }
        self.set_source_root(root_id, Arc::new(source_root));
    }

    pub(crate) fn maybe_collect_garbage(&mut self) {
        if self.last_gc_check.elapsed() > GC_COOLDOWN {
            self.last_gc_check = time::Instant::now();
            let retained_trees = syntax_tree_stats(self).retained;
            if retained_trees > 100 {
                log::info!("automatic garbadge collection, {} retained trees", retained_trees);
                self.collect_garbage();
            }
        }
    }

    pub(crate) fn collect_garbage(&mut self) {
        self.last_gc = time::Instant::now();

        let sweep = SweepStrategy::default().discard_values().sweep_all_revisions();

        self.query(ra_db::ParseQuery).sweep(sweep);

        self.query(hir::db::HirParseQuery).sweep(sweep);
        self.query(hir::db::AstIdMapQuery).sweep(sweep);
        self.query(hir::db::AstIdToNodeQuery).sweep(sweep);

        self.query(hir::db::RawItemsWithSourceMapQuery).sweep(sweep);
        self.query(hir::db::BodyWithSourceMapQuery).sweep(sweep);
    }

    pub(crate) fn collect_after_change(&mut self) {
        let sweep = SweepStrategy::default().discard_everything().sweep_all_revisions();
        self.query(hir::db::AstIdToNodeQuery).sweep(sweep)
    }
}
<commit_msg>revert eagarly clean astd maps<commit_after>use std::{
    fmt, time,
    sync::Arc,
};

use rustc_hash::FxHashMap;
use ra_db::{
    SourceRootId, FileId, CrateGraph, SourceDatabase, SourceRoot,
    salsa::{Database, SweepStrategy},
};
use ra_syntax::SourceFile;
use ra_prof::profile;
use relative_path::RelativePathBuf;
use rayon::prelude::*;

use crate::{
    db::RootDatabase,
    symbol_index::{SymbolIndex, SymbolsDatabase},
    status::syntax_tree_stats,
};

#[derive(Default)]
pub struct AnalysisChange {
    new_roots: Vec<(SourceRootId, bool)>,
    roots_changed: FxHashMap<SourceRootId, RootChange>,
    files_changed: Vec<(FileId, Arc<String>)>,
    libraries_added: Vec<LibraryData>,
    crate_graph: Option<CrateGraph>,
}

impl fmt::Debug for AnalysisChange {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        let mut d = fmt.debug_struct("AnalysisChange");
        if !self.new_roots.is_empty() {
            d.field("new_roots", &self.new_roots);
        }
        if !self.roots_changed.is_empty() {
            d.field("roots_changed", &self.roots_changed);
        }
        if !self.files_changed.is_empty() {
            d.field("files_changed", &self.files_changed.len());
        }
        if !self.libraries_added.is_empty() {
            d.field("libraries_added", &self.libraries_added.len());
        }
        if !self.crate_graph.is_some() {
            d.field("crate_graph", &self.crate_graph);
        }
        d.finish()
    }
}

impl AnalysisChange {
    pub fn new() -> AnalysisChange {
        AnalysisChange::default()
    }

    pub fn add_root(&mut self, root_id: SourceRootId, is_local: bool) {
        self.new_roots.push((root_id, is_local));
    }

    pub fn add_file(
        &mut self,
        root_id: SourceRootId,
        file_id: FileId,
        path: RelativePathBuf,
        text: Arc<String>,
    ) {
        let file = AddFile { file_id, path, text };
        self.roots_changed.entry(root_id).or_default().added.push(file);
    }

    pub fn change_file(&mut self, file_id: FileId, new_text: Arc<String>) {
        self.files_changed.push((file_id, new_text))
    }

    pub fn remove_file(&mut self, root_id: SourceRootId, file_id: FileId, path: RelativePathBuf) {
        let file = RemoveFile { file_id, path };
        self.roots_changed.entry(root_id).or_default().removed.push(file);
    }

    pub fn add_library(&mut self, data: LibraryData) {
        self.libraries_added.push(data)
    }

    pub fn set_crate_graph(&mut self, graph: CrateGraph) {
        self.crate_graph = Some(graph);
    }
}

#[derive(Debug)]
struct AddFile {
    file_id: FileId,
    path: RelativePathBuf,
    text: Arc<String>,
}

#[derive(Debug)]
struct RemoveFile {
    file_id: FileId,
    path: RelativePathBuf,
}

#[derive(Default)]
struct RootChange {
    added: Vec<AddFile>,
    removed: Vec<RemoveFile>,
}

impl fmt::Debug for RootChange {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt.debug_struct("AnalysisChange")
            .field("added", &self.added.len())
            .field("removed", &self.removed.len())
            .finish()
    }
}

pub struct LibraryData {
    root_id: SourceRootId,
    root_change: RootChange,
    symbol_index: SymbolIndex,
}

impl fmt::Debug for LibraryData {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("LibraryData")
            .field("root_id", &self.root_id)
            .field("root_change", &self.root_change)
            .field("n_symbols", &self.symbol_index.len())
            .finish()
    }
}

impl LibraryData {
    pub fn prepare(
        root_id: SourceRootId,
        files: Vec<(FileId, RelativePathBuf, Arc<String>)>,
    ) -> LibraryData {
        let symbol_index = SymbolIndex::for_files(files.par_iter().map(|(file_id, _, text)| {
            let file = SourceFile::parse(text);
            (*file_id, file)
        }));
        let mut root_change = RootChange::default();
        root_change.added = files
            .into_iter()
            .map(|(file_id, path, text)| AddFile { file_id, path, text })
            .collect();
        LibraryData { root_id, root_change, symbol_index }
    }
}

const GC_COOLDOWN: time::Duration = time::Duration::from_millis(100);

impl RootDatabase {
    pub(crate) fn apply_change(&mut self, change: AnalysisChange) {
        let _p = profile("RootDatabase::apply_change");
        log::info!("apply_change {:?}", change);
        if !change.new_roots.is_empty() {
            let mut local_roots = Vec::clone(&self.local_roots());
            for (root_id, is_local) in change.new_roots {
                self.set_source_root(root_id, Default::default());
                if is_local {
                    local_roots.push(root_id);
                }
            }
            self.set_local_roots(Arc::new(local_roots));
        }

        for (root_id, root_change) in change.roots_changed {
            self.apply_root_change(root_id, root_change);
        }
        for (file_id, text) in change.files_changed {
            self.set_file_text(file_id, text)
        }
        if !change.libraries_added.is_empty() {
            let mut libraries = Vec::clone(&self.library_roots());
            for library in change.libraries_added {
                libraries.push(library.root_id);
                self.set_source_root(library.root_id, Default::default());
                self.set_constant_library_symbols(library.root_id, Arc::new(library.symbol_index));
                self.apply_root_change(library.root_id, library.root_change);
            }
            self.set_library_roots(Arc::new(libraries));
        }
        if let Some(crate_graph) = change.crate_graph {
            self.set_crate_graph(Arc::new(crate_graph))
        }
    }

    fn apply_root_change(&mut self, root_id: SourceRootId, root_change: RootChange) {
        let mut source_root = SourceRoot::clone(&self.source_root(root_id));
        for add_file in root_change.added {
            self.set_file_text(add_file.file_id, add_file.text);
            self.set_file_relative_path(add_file.file_id, add_file.path.clone());
            self.set_file_source_root(add_file.file_id, root_id);
            source_root.files.insert(add_file.path, add_file.file_id);
        }
        for remove_file in root_change.removed {
            self.set_file_text(remove_file.file_id, Default::default());
            source_root.files.remove(&remove_file.path);
        }
        self.set_source_root(root_id, Arc::new(source_root));
    }

    pub(crate) fn maybe_collect_garbage(&mut self) {
        if self.last_gc_check.elapsed() > GC_COOLDOWN {
            self.last_gc_check = time::Instant::now();
            let retained_trees = syntax_tree_stats(self).retained;
            if retained_trees > 100 {
                log::info!("automatic garbadge collection, {} retained trees", retained_trees);
                self.collect_garbage();
            }
        }
    }

    pub(crate) fn collect_garbage(&mut self) {
        let _p = profile("RootDatabase::collect_garbage");
        self.last_gc = time::Instant::now();

        let sweep = SweepStrategy::default().discard_values().sweep_all_revisions();

        self.query(ra_db::ParseQuery).sweep(sweep);

        self.query(hir::db::HirParseQuery).sweep(sweep);
        self.query(hir::db::AstIdMapQuery).sweep(sweep);
        self.query(hir::db::AstIdToNodeQuery).sweep(sweep);

        self.query(hir::db::RawItemsWithSourceMapQuery).sweep(sweep);
        self.query(hir::db::BodyWithSourceMapQuery).sweep(sweep);
    }
}
<|endoftext|>