<commit_before>use backend::Backend;
use provider::error;
use provider::Provider;
use provider::Output;
use libc::uint32_t;

pub struct File<'a> {
    name: &'static str,
    backend: &'a Backend,
    provider: &'a Provider,
}

impl<'a> File<'a> {
    pub fn new(n: &'static str, b: &'a Backend, p: &'a Provider) -> File<'a> {
        File {
            name: n,
            backend: b,
            provider: p,
        }
    }

    pub fn mode(&self) -> Result<u32, error::Error> {
        self.backend
            .handle(self.provider.file.mode(self.name))
            .and_then(Output::to_u32)
    }

    pub fn size(&self) -> Result<i64, error::Error> {
        self.backend
            .handle(self.provider.file.size(self.name))
            .and_then(Output::to_i64)
    }

    pub fn is_file(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_file(self.name))
            .and_then(Output::to_bool)
    }

    pub fn exist(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.exist(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_directory(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_directory(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_block_device(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_block_device(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_character_device(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_character_device(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_pipe(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_pipe(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_socket(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_socket(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_symlink(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_symlink(self.name))
            .and_then(Output::to_bool)
    }

    pub fn contents(&self) -> Result<String, error::Error> {
        self.backend
            .handle(self.provider.file.contents(self.name))
            .and_then(Output::to_string)
    }

    pub fn owner(&self) -> Result<String, error::Error> {
        self.backend
            .handle(self.provider.file.owner(self.name))
            .and_then(Output::to_string)
    }

    pub fn group(&self) -> Result<String, error::Error> {
        self.backend
            .handle(self.provider.file.group(self.name))
            .and_then(Output::to_string)
    }

    pub fn linked_to(&self) -> Result<String, error::Error> {
        self.backend
            .handle(self.provider.file.linked_to(self.name))
            .and_then(Output::to_string)
    }

    pub fn is_readable(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_readable(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_readable_by_owner(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_readable_by_owner(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_readable_by_group(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_readable_by_group(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_readable_by_others(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_readable_by_others(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_readable_by_user(&self, user: &'static str) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_readable_by_user(self.name, user))
            .and_then(Output::to_bool)
    }

    pub fn is_writable(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_writable(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_writable_by_owner(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_writable_by_owner(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_writable_by_group(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_writable_by_group(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_writable_by_others(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_writable_by_others(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_writable_by_user(&self, user: &'static str) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_writable_by_user(self.name, user))
            .and_then(Output::to_bool)
    }

    pub fn md5sum(&self) -> Result<String, error::Error> {
        self.backend
            .handle(self.provider.file.md5sum(self.name))
            .and_then(Output::to_string)
    }

    pub fn sha256sum(&self) -> Result<String, error::Error> {
        self.backend
            .handle(self.provider.file.sha256sum(self.name))
            .and_then(Output::to_string)
    }
}

// Wrapper functions for FFI

#[no_mangle]
pub extern "C" fn resource_file_free(ptr: *mut File) {
    if ptr.is_null() {
        return;
    }
    unsafe {
        Box::from_raw(ptr);
    }
}

#[no_mangle]
pub extern "C" fn resource_file_mode(ptr: *const File) -> uint32_t {
    let f = unsafe {
        assert!(!ptr.is_null());
        &*ptr
    };

    f.mode().unwrap()
}

#[no_mangle]
pub extern "C" fn resource_file_is_file(ptr: *const File) -> uint32_t {
    let f = unsafe {
        assert!(!ptr.is_null());
        &*ptr
    };

    if f.is_file().unwrap() { 1 } else { 0 }
}

#[no_mangle]
pub extern "C" fn resource_file_exist(ptr: *const File) -> uint32_t {
    let f = unsafe {
        assert!(!ptr.is_null());
        &*ptr
    };

    if f.exist().unwrap() { 1 } else { 0 }
}

#[no_mangle]
pub extern "C" fn resource_file_is_directory(ptr: *const File) -> uint32_t {
    let f = unsafe {
        assert!(!ptr.is_null());
        &*ptr
    };

    if f.is_directory().unwrap() { 1 } else { 0 }
}

#[no_mangle]
pub extern "C" fn resource_file_is_block_device(ptr: *const File) -> uint32_t {
    let f = unsafe {
        assert!(!ptr.is_null());
        &*ptr
    };

    if f.is_block_device().unwrap() { 1 } else { 0 }
}
<commit_msg>Add wrapper function for file.is_character_device()<commit_after>use backend::Backend;
use provider::error;
use provider::Provider;
use provider::Output;
use libc::uint32_t;

pub struct File<'a> {
    name: &'static str,
    backend: &'a Backend,
    provider: &'a Provider,
}

impl<'a> File<'a> {
    pub fn new(n: &'static str, b: &'a Backend, p: &'a Provider) -> File<'a> {
        File {
            name: n,
            backend: b,
            provider: p,
        }
    }

    pub fn mode(&self) -> Result<u32, error::Error> {
        self.backend
            .handle(self.provider.file.mode(self.name))
            .and_then(Output::to_u32)
    }

    pub fn size(&self) -> Result<i64, error::Error> {
        self.backend
            .handle(self.provider.file.size(self.name))
            .and_then(Output::to_i64)
    }

    pub fn is_file(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_file(self.name))
            .and_then(Output::to_bool)
    }

    pub fn exist(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.exist(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_directory(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_directory(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_block_device(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_block_device(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_character_device(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_character_device(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_pipe(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_pipe(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_socket(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_socket(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_symlink(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_symlink(self.name))
            .and_then(Output::to_bool)
    }

    pub fn contents(&self) -> Result<String, error::Error> {
        self.backend
            .handle(self.provider.file.contents(self.name))
            .and_then(Output::to_string)
    }

    pub fn owner(&self) -> Result<String, error::Error> {
        self.backend
            .handle(self.provider.file.owner(self.name))
            .and_then(Output::to_string)
    }

    pub fn group(&self) -> Result<String, error::Error> {
        self.backend
            .handle(self.provider.file.group(self.name))
            .and_then(Output::to_string)
    }

    pub fn linked_to(&self) -> Result<String, error::Error> {
        self.backend
            .handle(self.provider.file.linked_to(self.name))
            .and_then(Output::to_string)
    }

    pub fn is_readable(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_readable(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_readable_by_owner(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_readable_by_owner(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_readable_by_group(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_readable_by_group(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_readable_by_others(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_readable_by_others(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_readable_by_user(&self, user: &'static str) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_readable_by_user(self.name, user))
            .and_then(Output::to_bool)
    }

    pub fn is_writable(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_writable(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_writable_by_owner(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_writable_by_owner(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_writable_by_group(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_writable_by_group(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_writable_by_others(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_writable_by_others(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_writable_by_user(&self, user: &'static str) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_writable_by_user(self.name, user))
            .and_then(Output::to_bool)
    }

    pub fn md5sum(&self) -> Result<String, error::Error> {
        self.backend
            .handle(self.provider.file.md5sum(self.name))
            .and_then(Output::to_string)
    }

    pub fn sha256sum(&self) -> Result<String, error::Error> {
        self.backend
            .handle(self.provider.file.sha256sum(self.name))
            .and_then(Output::to_string)
    }
}

// Wrapper functions for FFI

#[no_mangle]
pub extern "C" fn resource_file_free(ptr: *mut File) {
    if ptr.is_null() {
        return;
    }
    unsafe {
        Box::from_raw(ptr);
    }
}

#[no_mangle]
pub extern "C" fn resource_file_mode(ptr: *const File) -> uint32_t {
    let f = unsafe {
        assert!(!ptr.is_null());
        &*ptr
    };

    f.mode().unwrap()
}

#[no_mangle]
pub extern "C" fn resource_file_is_file(ptr: *const File) -> uint32_t {
    let f = unsafe {
        assert!(!ptr.is_null());
        &*ptr
    };

    if f.is_file().unwrap() { 1 } else { 0 }
}

#[no_mangle]
pub extern "C" fn resource_file_exist(ptr: *const File) -> uint32_t {
    let f = unsafe {
        assert!(!ptr.is_null());
        &*ptr
    };

    if f.exist().unwrap() { 1 } else { 0 }
}

#[no_mangle]
pub extern "C" fn resource_file_is_directory(ptr: *const File) -> uint32_t {
    let f = unsafe {
        assert!(!ptr.is_null());
        &*ptr
    };

    if f.is_directory().unwrap() { 1 } else { 0 }
}

#[no_mangle]
pub extern "C" fn resource_file_is_block_device(ptr: *const File) -> uint32_t {
    let f = unsafe {
        assert!(!ptr.is_null());
        &*ptr
    };

    if f.is_block_device().unwrap() { 1 } else { 0 }
}

#[no_mangle]
pub extern "C" fn resource_file_is_character_device(ptr: *const File) -> uint32_t {
    let f = unsafe {
        assert!(!ptr.is_null());
        &*ptr
    };

    if f.is_character_device().unwrap() {
        1
    } else {
        0
    }
}
<|endoftext|>


<commit_before>//! Extended attribute support for Darwin and Linux systems.
#![allow(trivial_casts)]  // for ARM
extern crate libc;

use std::io;
use std::path::Path;

pub const ENABLED: bool = cfg!(feature="git") && cfg!(any(target_os="macos", target_os="linux"));

pub trait FileAttributes {
    fn attributes(&self) -> io::Result<Vec<Attribute>>;
    fn symlink_attributes(&self) -> io::Result<Vec<Attribute>>;
}

#[cfg(any(target_os = "macos", target_os = "linux"))]
impl FileAttributes for Path {
    fn attributes(&self) -> io::Result<Vec<Attribute>> {
        list_attrs(&lister::Lister::new(FollowSymlinks::Yes), self)
    }

    fn symlink_attributes(&self) -> io::Result<Vec<Attribute>> {
        list_attrs(&lister::Lister::new(FollowSymlinks::No), self)
    }
}

#[cfg(not(any(target_os = "macos", target_os = "linux")))]
impl FileAttributes for Path {
    fn attributes(&self) -> io::Result<Vec<Attribute>> {
        Ok(vec![])
    }

    fn symlink_attributes(&self) -> io::Result<Vec<Attribute>> {
        Ok(vec![])
    }
}

/// Attributes which can be passed to `Attribute::list_with_flags`
#[cfg(any(target_os = "macos", target_os = "linux"))]
#[derive(Copy, Clone)]
pub enum FollowSymlinks {
    Yes,
    No
}

/// Extended attribute
#[derive(Debug, Clone)]
pub struct Attribute {
    pub name: String,
    pub size: usize,
}

#[cfg(any(target_os = "macos", target_os = "linux"))]
pub fn list_attrs(lister: &lister::Lister, path: &Path) -> io::Result<Vec<Attribute>> {
    use std::ffi::CString;

    let c_path = match path.to_str().and_then(|s| { CString::new(s).ok() }) {
        Some(cstring) => cstring,
        None => return Err(io::Error::new(io::ErrorKind::Other, "Error: path somehow contained a NUL?")),
    };

    let mut names = Vec::new();
    let bufsize = lister.listxattr_first(&c_path);

    if bufsize < 0 {
        return Err(io::Error::last_os_error());
    }
    else if bufsize > 0 {
        let mut buf = vec![0u8; bufsize as usize];
        let err = lister.listxattr_second(&c_path, &mut buf, bufsize);

        if err < 0 {
            return Err(io::Error::last_os_error());
        }

        if err > 0 {
            // End indicies of the attribute names
            // the buffer contains 0-terminates c-strings
            let idx = buf.iter().enumerate().filter_map(|(i, v)|
                if *v == 0 { Some(i) } else { None }
            );
            let mut start = 0;

            for end in idx {
                let c_end = end + 1; // end of the c-string (including 0)
                let size = lister.getxattr(&c_path, &buf[start..c_end]);

                if size > 0 {
                    names.push(Attribute {
                        name: lister.translate_attribute_name(&buf[start..end]),
                        size: size as usize
                    });
                }

                start = c_end;
            }

        }

    }
    Ok(names)
}

#[cfg(target_os = "macos")]
mod lister {
    use std::ffi::CString;
    use libc::{c_int, size_t, ssize_t, c_char, c_void, uint32_t};
    use super::FollowSymlinks;
    use std::ptr;

    extern "C" {
        fn listxattr(
            path: *const c_char, namebuf: *mut c_char,
            size: size_t, options: c_int
        ) -> ssize_t;

        fn getxattr(
            path: *const c_char, name: *const c_char,
            value: *mut c_void, size: size_t, position: uint32_t,
            options: c_int
        ) -> ssize_t;
    }

    pub struct Lister {
        c_flags: c_int,
    }

    impl Lister {
        pub fn new(do_follow: FollowSymlinks) -> Lister {
            let c_flags: c_int = match do_follow {
                FollowSymlinks::Yes => 0x0001,
                FollowSymlinks::No  => 0x0000,
            };

            Lister { c_flags: c_flags }
        }

        pub fn translate_attribute_name(&self, input: &[u8]) -> String {
            use std::str::from_utf8_unchecked;

            unsafe {
                from_utf8_unchecked(input).into()
            }
        }

        pub fn listxattr_first(&self, c_path: &CString) -> ssize_t {
            unsafe {
                listxattr(c_path.as_ptr(), ptr::null_mut(), 0, self.c_flags)
            }
        }

        pub fn listxattr_second(&self, c_path: &CString, buf: &mut Vec<u8>, bufsize: ssize_t) -> ssize_t {
            unsafe {
                listxattr(
                    c_path.as_ptr(),
                    buf.as_mut_ptr() as *mut c_char,
                    bufsize as size_t, self.c_flags
                )
            }
        }

        pub fn getxattr(&self, c_path: &CString, buf: &[u8]) -> ssize_t {
            unsafe {
                getxattr(
                    c_path.as_ptr(),
                    buf.as_ptr() as *const c_char,
                    ptr::null_mut(), 0, 0, self.c_flags
                )
            }
        }
    }
}

#[cfg(target_os = "linux")]
mod lister {
    use std::ffi::CString;
    use libc::{size_t, ssize_t, c_char, c_void};
    use super::FollowSymlinks;
    use std::ptr;

    extern "C" {
        fn listxattr(
            path: *const c_char, list: *mut c_char, size: size_t
        ) -> ssize_t;

        fn llistxattr(
            path: *const c_char, list: *mut c_char, size: size_t
        ) -> ssize_t;

        fn getxattr(
            path: *const c_char, name: *const c_char,
            value: *mut c_void, size: size_t
        ) -> ssize_t;

        fn lgetxattr(
            path: *const c_char, name: *const c_char,
            value: *mut c_void, size: size_t
        ) -> ssize_t;
    }

    pub struct Lister {
        follow_symlinks: FollowSymlinks,
    }

    impl Lister {
        pub fn new(follow_symlinks: FollowSymlinks) -> Lister {
            Lister { follow_symlinks }
        }

        pub fn translate_attribute_name(&self, input: &[u8]) -> String {
            String::from_utf8_lossy(input).into_owned()
        }

        pub fn listxattr_first(&self, c_path: &CString) -> ssize_t {
            let listxattr = match self.follow_symlinks {
                FollowSymlinks::Yes => listxattr,
                FollowSymlinks::No  => llistxattr,
            };

            unsafe {
                listxattr(c_path.as_ptr() as *const _, ptr::null_mut(), 0)
            }
        }

        pub fn listxattr_second(&self, c_path: &CString, buf: &mut Vec<u8>, bufsize: ssize_t) -> ssize_t {
            let listxattr = match self.follow_symlinks {
                FollowSymlinks::Yes => listxattr,
                FollowSymlinks::No  => llistxattr,
            };

            unsafe {
                listxattr(
                    c_path.as_ptr() as *const _,
                    buf.as_mut_ptr() as *mut c_char,
                    bufsize as size_t
                )
            }
        }

        pub fn getxattr(&self, c_path: &CString, buf: &[u8]) -> ssize_t {
            let getxattr = match self.follow_symlinks {
                FollowSymlinks::Yes => getxattr,
                FollowSymlinks::No  => lgetxattr,
            };

            unsafe {
                getxattr(
                    c_path.as_ptr() as *const _,
                    buf.as_ptr() as *const c_char,
                    ptr::null_mut(), 0
                )
            }
        }
    }
}
<commit_msg>refactor: use shorthand fields<commit_after>//! Extended attribute support for Darwin and Linux systems.
#![allow(trivial_casts)]  // for ARM
extern crate libc;

use std::io;
use std::path::Path;

pub const ENABLED: bool = cfg!(feature="git") && cfg!(any(target_os="macos", target_os="linux"));

pub trait FileAttributes {
    fn attributes(&self) -> io::Result<Vec<Attribute>>;
    fn symlink_attributes(&self) -> io::Result<Vec<Attribute>>;
}

#[cfg(any(target_os = "macos", target_os = "linux"))]
impl FileAttributes for Path {
    fn attributes(&self) -> io::Result<Vec<Attribute>> {
        list_attrs(&lister::Lister::new(FollowSymlinks::Yes), self)
    }

    fn symlink_attributes(&self) -> io::Result<Vec<Attribute>> {
        list_attrs(&lister::Lister::new(FollowSymlinks::No), self)
    }
}

#[cfg(not(any(target_os = "macos", target_os = "linux")))]
impl FileAttributes for Path {
    fn attributes(&self) -> io::Result<Vec<Attribute>> {
        Ok(vec![])
    }

    fn symlink_attributes(&self) -> io::Result<Vec<Attribute>> {
        Ok(vec![])
    }
}

/// Attributes which can be passed to `Attribute::list_with_flags`
#[cfg(any(target_os = "macos", target_os = "linux"))]
#[derive(Copy, Clone)]
pub enum FollowSymlinks {
    Yes,
    No
}

/// Extended attribute
#[derive(Debug, Clone)]
pub struct Attribute {
    pub name: String,
    pub size: usize,
}

#[cfg(any(target_os = "macos", target_os = "linux"))]
pub fn list_attrs(lister: &lister::Lister, path: &Path) -> io::Result<Vec<Attribute>> {
    use std::ffi::CString;

    let c_path = match path.to_str().and_then(|s| { CString::new(s).ok() }) {
        Some(cstring) => cstring,
        None => return Err(io::Error::new(io::ErrorKind::Other, "Error: path somehow contained a NUL?")),
    };

    let mut names = Vec::new();
    let bufsize = lister.listxattr_first(&c_path);

    if bufsize < 0 {
        return Err(io::Error::last_os_error());
    }
    else if bufsize > 0 {
        let mut buf = vec![0u8; bufsize as usize];
        let err = lister.listxattr_second(&c_path, &mut buf, bufsize);

        if err < 0 {
            return Err(io::Error::last_os_error());
        }

        if err > 0 {
            // End indicies of the attribute names
            // the buffer contains 0-terminates c-strings
            let idx = buf.iter().enumerate().filter_map(|(i, v)|
                if *v == 0 { Some(i) } else { None }
            );
            let mut start = 0;

            for end in idx {
                let c_end = end + 1; // end of the c-string (including 0)
                let size = lister.getxattr(&c_path, &buf[start..c_end]);

                if size > 0 {
                    names.push(Attribute {
                        name: lister.translate_attribute_name(&buf[start..end]),
                        size: size as usize
                    });
                }

                start = c_end;
            }

        }

    }
    Ok(names)
}

#[cfg(target_os = "macos")]
mod lister {
    use std::ffi::CString;
    use libc::{c_int, size_t, ssize_t, c_char, c_void, uint32_t};
    use super::FollowSymlinks;
    use std::ptr;

    extern "C" {
        fn listxattr(
            path: *const c_char, namebuf: *mut c_char,
            size: size_t, options: c_int
        ) -> ssize_t;

        fn getxattr(
            path: *const c_char, name: *const c_char,
            value: *mut c_void, size: size_t, position: uint32_t,
            options: c_int
        ) -> ssize_t;
    }

    pub struct Lister {
        c_flags: c_int,
    }

    impl Lister {
        pub fn new(do_follow: FollowSymlinks) -> Lister {
            let c_flags: c_int = match do_follow {
                FollowSymlinks::Yes => 0x0001,
                FollowSymlinks::No  => 0x0000,
            };

            Lister { c_flags }
        }

        pub fn translate_attribute_name(&self, input: &[u8]) -> String {
            use std::str::from_utf8_unchecked;

            unsafe {
                from_utf8_unchecked(input).into()
            }
        }

        pub fn listxattr_first(&self, c_path: &CString) -> ssize_t {
            unsafe {
                listxattr(c_path.as_ptr(), ptr::null_mut(), 0, self.c_flags)
            }
        }

        pub fn listxattr_second(&self, c_path: &CString, buf: &mut Vec<u8>, bufsize: ssize_t) -> ssize_t {
            unsafe {
                listxattr(
                    c_path.as_ptr(),
                    buf.as_mut_ptr() as *mut c_char,
                    bufsize as size_t, self.c_flags
                )
            }
        }

        pub fn getxattr(&self, c_path: &CString, buf: &[u8]) -> ssize_t {
            unsafe {
                getxattr(
                    c_path.as_ptr(),
                    buf.as_ptr() as *const c_char,
                    ptr::null_mut(), 0, 0, self.c_flags
                )
            }
        }
    }
}

#[cfg(target_os = "linux")]
mod lister {
    use std::ffi::CString;
    use libc::{size_t, ssize_t, c_char, c_void};
    use super::FollowSymlinks;
    use std::ptr;

    extern "C" {
        fn listxattr(
            path: *const c_char, list: *mut c_char, size: size_t
        ) -> ssize_t;

        fn llistxattr(
            path: *const c_char, list: *mut c_char, size: size_t
        ) -> ssize_t;

        fn getxattr(
            path: *const c_char, name: *const c_char,
            value: *mut c_void, size: size_t
        ) -> ssize_t;

        fn lgetxattr(
            path: *const c_char, name: *const c_char,
            value: *mut c_void, size: size_t
        ) -> ssize_t;
    }

    pub struct Lister {
        follow_symlinks: FollowSymlinks,
    }

    impl Lister {
        pub fn new(follow_symlinks: FollowSymlinks) -> Lister {
            Lister { follow_symlinks }
        }

        pub fn translate_attribute_name(&self, input: &[u8]) -> String {
            String::from_utf8_lossy(input).into_owned()
        }

        pub fn listxattr_first(&self, c_path: &CString) -> ssize_t {
            let listxattr = match self.follow_symlinks {
                FollowSymlinks::Yes => listxattr,
                FollowSymlinks::No  => llistxattr,
            };

            unsafe {
                listxattr(c_path.as_ptr() as *const _, ptr::null_mut(), 0)
            }
        }

        pub fn listxattr_second(&self, c_path: &CString, buf: &mut Vec<u8>, bufsize: ssize_t) -> ssize_t {
            let listxattr = match self.follow_symlinks {
                FollowSymlinks::Yes => listxattr,
                FollowSymlinks::No  => llistxattr,
            };

            unsafe {
                listxattr(
                    c_path.as_ptr() as *const _,
                    buf.as_mut_ptr() as *mut c_char,
                    bufsize as size_t
                )
            }
        }

        pub fn getxattr(&self, c_path: &CString, buf: &[u8]) -> ssize_t {
            let getxattr = match self.follow_symlinks {
                FollowSymlinks::Yes => getxattr,
                FollowSymlinks::No  => lgetxattr,
            };

            unsafe {
                getxattr(
                    c_path.as_ptr() as *const _,
                    buf.as_ptr() as *const c_char,
                    ptr::null_mut(), 0
                )
            }
        }
    }
}
<|endoftext|>


<commit_before>// Copyright (c) <2015> <lummax>
// Licensed under MIT (http://opensource.org/licenses/MIT)

use std::collections::{HashSet, VecMap};
use std::num::Int;

use constants::{BLOCK_SIZE, LINE_SIZE, NUM_LINES_PER_BLOCK};
use gc_object::GCObjectRef;

pub struct BlockInfo {
    line_counter: VecMap<u8>,
    object_map: HashSet<GCObjectRef>,
    new_objects: HashSet<GCObjectRef>,
    allocated: bool,
    hole_count: u8,
    evacuation_candidate: bool,
}

impl BlockInfo {
    pub fn new() -> BlockInfo {
        let mut line_counter = VecMap::with_capacity(NUM_LINES_PER_BLOCK);
        for index in (0..NUM_LINES_PER_BLOCK) {
            line_counter.insert(index, 0);
        }
        return BlockInfo {
            line_counter: line_counter,
            object_map: HashSet::new(),
            new_objects: HashSet::new(),
            allocated: false,
            hole_count: 0,
            evacuation_candidate: false,
        };
    }

    pub fn set_allocated(&mut self) {
        self.allocated = true;
    }

    pub fn set_gc_object(&mut self, object: GCObjectRef) {
        debug_assert!(self.is_in_block(object),
            "set_gc_object() on invalid block: {:p} (allocated={})",
            self, self.allocated);
        self.object_map.insert(object);
    }

    pub fn unset_gc_object(&mut self, object: GCObjectRef) {
        debug_assert!(self.is_in_block(object),
            "unset_gc_object() on invalid block: {:p} (allocated={})",
            self, self.allocated);
        self.object_map.remove(&object);
    }

    pub fn is_gc_object(&self, object: GCObjectRef) -> bool {
        if self.is_in_block(object) {
            return self.object_map.contains(&object);
        }
        return false;
    }

    pub fn get_object_map(&mut self) -> HashSet<GCObjectRef> {
        return self.object_map.clone();
    }

    pub fn clear_object_map(&mut self) {
        self.object_map.clear();
    }

    pub fn set_new_object(&mut self, object: GCObjectRef) {
        debug_assert!(self.is_in_block(object),
            "set_new_object() on invalid block: {:p} (allocated={})",
            self, self.allocated);
        self.new_objects.insert(object);
    }

    pub fn get_new_objects(&mut self) -> HashSet<GCObjectRef> {
        return self.new_objects.clone();
    }

    pub fn remove_new_objects_from_map(&mut self) {
        let new_objects = self.new_objects.drain().collect();
        let difference = self.object_map.difference(&new_objects)
                                        .map(|o| *o).collect();
        self.object_map = difference;
    }

    pub fn set_evacuation_candidate(&mut self, hole_count: u8) {
        debug!("Set block {:p} to evacuation_candidate={} ({} holes)",
               &self, self.hole_count >= hole_count, self.hole_count);
        self.evacuation_candidate = self.hole_count >= hole_count;
    }

    pub fn is_evacuation_candidate(&self) -> bool{
        return self.evacuation_candidate;
    }

    pub fn increment_lines(&mut self, object: GCObjectRef) {
        self.update_line_nums(object, true);
    }

    pub fn decrement_lines(&mut self, object: GCObjectRef) {
        self.update_line_nums(object, false);
    }

    pub fn count_holes_and_marked_lines(&self) -> (u8, u8) {
        return (self.hole_count,
                self.line_counter.values().filter(|&e| *e != 0).count() as u8);
    }

    pub fn count_holes_and_available_lines(&self) -> (u8, u8) {
        return (self.hole_count,
                self.line_counter.values().filter(|&e| *e == 0).count() as u8);
    }

    pub fn clear_line_counts(&mut self) {
        for index in (0..NUM_LINES_PER_BLOCK) {
            self.line_counter.insert(index, 0);
        }
    }

    pub fn reset(&mut self) {
        self.clear_line_counts();
        self.clear_object_map();
        self.allocated = false;
        self.hole_count = 0;
        self.evacuation_candidate = false;
    }

    pub fn is_empty(&self) -> bool {
        return self.line_counter.values().all(|v| *v == 0);
    }

    pub fn offset(&mut self, offset: usize) -> GCObjectRef {
        let self_ptr = self as *mut BlockInfo;
        let object = unsafe { (self_ptr as *mut u8).offset(offset as isize) };
        return object as GCObjectRef;
    }

    pub fn scan_block(&self, last_high_offset: u16) -> Option<(u16, u16)> {
        let last_high_index = last_high_offset as usize / LINE_SIZE;
        debug!("Scanning block {:p} for a hole with last_high_offset {}",
               self, last_high_index);
        let mut low_index = NUM_LINES_PER_BLOCK - 1;
        for index in ((last_high_index + 1)..NUM_LINES_PER_BLOCK) {
            if self.line_counter.get(&index).map_or(true, |c| *c == 0) {
                // +1 to skip the next line in case an object straddles lines
                low_index = index + 1;
                break;
            }
        }
        let mut high_index = NUM_LINES_PER_BLOCK;
        for index in (low_index..NUM_LINES_PER_BLOCK) {
            if self.line_counter.get(&index).map_or(false, |c| *c != 0) {
                high_index = index;
                break;
            }
        }
        if low_index == high_index && high_index != (NUM_LINES_PER_BLOCK - 1) {
            debug!("Rescan: Found single line hole? in block {:p}", self);
            return self.scan_block((high_index * LINE_SIZE - 1) as u16);
        } else if low_index < (NUM_LINES_PER_BLOCK - 1) {
            debug!("Found low index {} and high index {} in block {:p}",
                   low_index, high_index, self);
            return Some(((low_index * LINE_SIZE) as u16,
                         (high_index * LINE_SIZE - 1) as u16));
        }
        debug!("Found no hole in block {:p}", self);
        return None;
    }

    pub fn count_holes(&mut self) {
        let holes = self.line_counter.values()
            .fold((0, false), |(holes, in_hole), &elem|
                  match (in_hole, elem) {
                    (false, 0) => (holes + 1, true),
                    (_, _) => (holes, false),
                  }).0;
        self.hole_count = holes;
    }
}

impl BlockInfo{
    fn is_in_block(&self, object: GCObjectRef) -> bool {
        // This works because we get zeroed memory from the OS, so
        // self.allocated will be false if this block is not initialized and
        // this method gets only called for objects within the ImmixSpace.
        // After the first initialization the field is properly managed.
        if self.allocated {
            let self_ptr = self as *const BlockInfo as *const u8;
            let self_bound = unsafe{ self_ptr.offset(BLOCK_SIZE as isize)};
            return self_ptr < (object as *const u8)
                && (object as *const u8) <  self_bound;
        }
        return false;
    }

    fn object_to_line_num(object: GCObjectRef) -> usize {
        return (object as usize % BLOCK_SIZE) / LINE_SIZE;
    }

    fn update_line_nums(&mut self, object: GCObjectRef, increment: bool) {
        // This calculates how many lines are affected starting from a
        // LINE_SIZE aligned address. So it might not mark enough lines. But
        // that does not matter as we always skip a line in scan_block()
        let line_num = BlockInfo::object_to_line_num(object);
        let object_size = unsafe{ (*object).object_size() };
        for line in line_num..(line_num + (object_size / LINE_SIZE) + 1) {
            match increment {
                true => {
                    if self.line_counter.contains_key(&line) {
                        if let Some(val) = self.line_counter.get_mut(&line) {
                            *val += 1;
                        }
                    } else { self.line_counter.insert(line, 1); }
                    debug!("Incremented line count for line {} to {}", line,
                           self.line_counter.get(&line).unwrap());
                },
                false => {
                    if self.line_counter.contains_key(&line) {
                        if let Some(val) = self.line_counter.get_mut(&line) {
                            *val = Int::saturating_sub(*val, 1);
                        }
                    } else { self.line_counter.insert(line, 0); }
                    debug!("Decremented line count for line {} to {}", line,
                           self.line_counter.get(&line).unwrap());
                }
            }
        }
    }
}
<commit_msg>Add rustdoc for block_info.rs<commit_after>// Copyright (c) <2015> <lummax>
// Licensed under MIT (http://opensource.org/licenses/MIT)

use std::collections::{HashSet, VecMap};
use std::num::Int;

use constants::{BLOCK_SIZE, LINE_SIZE, NUM_LINES_PER_BLOCK};
use gc_object::GCObjectRef;

/// A `BlockInfo` contains management information for the immix garbage
/// collectors.
///
/// It is the first bytes in a chunk of memory of size `BLOCK_SIZE` and must
/// not exeed `LINE_SIZE` bytes in size.
pub struct BlockInfo {
    /// A counter of live objects for every line in this block.
    line_counter: VecMap<u8>,

    /// A set of addresses that are valid objects. Needed for the conservative
    /// part.
    object_map: HashSet<GCObjectRef>,

    /// Objects in this block that were never touched by the garbage
    /// collector.
    new_objects: HashSet<GCObjectRef>,

    /// If this block is actually in use.
    allocated: bool,

    /// How many holen are in this block.
    hole_count: u8,

    /// If this block is a candidate for opportunistic evacuation.
    evacuation_candidate: bool,
}

impl BlockInfo {
    /// Create a new `BlockInfo`.
    pub fn new() -> BlockInfo {
        let mut line_counter = VecMap::with_capacity(NUM_LINES_PER_BLOCK);
        for index in (0..NUM_LINES_PER_BLOCK) {
            line_counter.insert(index, 0);
        }
        return BlockInfo {
            line_counter: line_counter,
            object_map: HashSet::new(),
            new_objects: HashSet::new(),
            allocated: false,
            hole_count: 0,
            evacuation_candidate: false,
        };
    }

    /// Set this block as allocated (actually in use).
    pub fn set_allocated(&mut self) {
        self.allocated = true;
    }

    /// Set an address in this block as a valid object.
    pub fn set_gc_object(&mut self, object: GCObjectRef) {
        debug_assert!(self.is_in_block(object),
            "set_gc_object() on invalid block: {:p} (allocated={})",
            self, self.allocated);
        self.object_map.insert(object);
    }

    /// Unset an address in this block as a valid object.
    pub fn unset_gc_object(&mut self, object: GCObjectRef) {
        debug_assert!(self.is_in_block(object),
            "unset_gc_object() on invalid block: {:p} (allocated={})",
            self, self.allocated);
        self.object_map.remove(&object);
    }

    /// Return if an address in this block is a valid object.
    pub fn is_gc_object(&self, object: GCObjectRef) -> bool {
        if self.is_in_block(object) {
            return self.object_map.contains(&object);
        }
        return false;
    }

    /// Get a copy of the object map.
    pub fn get_object_map(&mut self) -> HashSet<GCObjectRef> {
        return self.object_map.clone();
    }

    /// Clear the object map.
    pub fn clear_object_map(&mut self) {
        self.object_map.clear();
    }

    /// Set an object in this block as new (not the `GCHeader.new` bit).
    pub fn set_new_object(&mut self, object: GCObjectRef) {
        debug_assert!(self.is_in_block(object),
            "set_new_object() on invalid block: {:p} (allocated={})",
            self, self.allocated);
        self.new_objects.insert(object);
    }

    /// Get the new objects in this block.
    pub fn get_new_objects(&mut self) -> HashSet<GCObjectRef> {
        return self.new_objects.clone();
    }

    /// Remove all the new objects from the object map and clear the new
    /// objects set.
    pub fn remove_new_objects_from_map(&mut self) {
        let new_objects = self.new_objects.drain().collect();
        let difference = self.object_map.difference(&new_objects)
                                        .map(|o| *o).collect();
        self.object_map = difference;
    }

    /// Set as an evacuation candidate if this block has at least `hole_count`
    /// holes.
    pub fn set_evacuation_candidate(&mut self, hole_count: u8) {
        debug!("Set block {:p} to evacuation_candidate={} ({} holes)",
               &self, self.hole_count >= hole_count, self.hole_count);
        self.evacuation_candidate = self.hole_count >= hole_count;
    }

    /// Return if this is an evacuation candidate.
    pub fn is_evacuation_candidate(&self) -> bool{
        return self.evacuation_candidate;
    }

    /// Increment the lines on which the object is allocated.
    pub fn increment_lines(&mut self, object: GCObjectRef) {
        self.update_line_nums(object, true);
    }

    /// Decrement the lines on which the object is allocated.
    pub fn decrement_lines(&mut self, object: GCObjectRef) {
        self.update_line_nums(object, false);
    }

    /// Return the number of holes and marked lines in this block.
    ///
    /// A marked line is a line with a count of at least one.
    ///
    /// _Note_: You must call count_holes() bevorhand to set the number of
    /// holes.
    pub fn count_holes_and_marked_lines(&self) -> (u8, u8) {
        return (self.hole_count,
                self.line_counter.values().filter(|&e| *e != 0).count() as u8);
    }

    /// Return the number of holes and available lines in this block.
    ///
    /// An available line is a line with a count of zero.
    ///
    /// _Note_: You must call count_holes() bevorhand to set the number of
    /// holes.
    pub fn count_holes_and_available_lines(&self) -> (u8, u8) {
        return (self.hole_count,
                self.line_counter.values().filter(|&e| *e == 0).count() as u8);
    }

    /// Clear the line counter map.
    pub fn clear_line_counts(&mut self) {
        for index in (0..NUM_LINES_PER_BLOCK) {
            self.line_counter.insert(index, 0);
        }
    }

    /// Reset all member field for this block.
    pub fn reset(&mut self) {
        self.clear_line_counts();
        self.clear_object_map();
        self.allocated = false;
        self.hole_count = 0;
        self.evacuation_candidate = false;
    }

    /// Return true if no line is marked (every line has a count of zero).
    pub fn is_empty(&self) -> bool {
        return self.line_counter.values().all(|v| *v == 0);
    }

    /// Get a pointer to an address `offset` bytes into this block.
    pub fn offset(&mut self, offset: usize) -> GCObjectRef {
        let self_ptr = self as *mut BlockInfo;
        let object = unsafe { (self_ptr as *mut u8).offset(offset as isize) };
        return object as GCObjectRef;
    }

    /// Scan the block for a hole to allocate into.
    ///
    /// The scan will start at `last_high_offset` bytes into the block and
    /// return a tuple of `low_offset`, `high_offset` as the lowest and
    /// highest usable offsets for a hole.
    ///
    /// `None` is returned if no hole was found.
    pub fn scan_block(&self, last_high_offset: u16) -> Option<(u16, u16)> {
        let last_high_index = last_high_offset as usize / LINE_SIZE;
        debug!("Scanning block {:p} for a hole with last_high_offset {}",
               self, last_high_index);
        let mut low_index = NUM_LINES_PER_BLOCK - 1;
        for index in ((last_high_index + 1)..NUM_LINES_PER_BLOCK) {
            if self.line_counter.get(&index).map_or(true, |c| *c == 0) {
                // +1 to skip the next line in case an object straddles lines
                low_index = index + 1;
                break;
            }
        }
        let mut high_index = NUM_LINES_PER_BLOCK;
        for index in (low_index..NUM_LINES_PER_BLOCK) {
            if self.line_counter.get(&index).map_or(false, |c| *c != 0) {
                high_index = index;
                break;
            }
        }
        if low_index == high_index && high_index != (NUM_LINES_PER_BLOCK - 1) {
            debug!("Rescan: Found single line hole? in block {:p}", self);
            return self.scan_block((high_index * LINE_SIZE - 1) as u16);
        } else if low_index < (NUM_LINES_PER_BLOCK - 1) {
            debug!("Found low index {} and high index {} in block {:p}",
                   low_index, high_index, self);
            return Some(((low_index * LINE_SIZE) as u16,
                         (high_index * LINE_SIZE - 1) as u16));
        }
        debug!("Found no hole in block {:p}", self);
        return None;
    }

    /// Count the holes in this block.
    ///
    /// Holes are lines with no objects allocated.
    pub fn count_holes(&mut self) {
        let holes = self.line_counter.values()
            .fold((0, false), |(holes, in_hole), &elem|
                  match (in_hole, elem) {
                    (false, 0) => (holes + 1, true),
                    (_, _) => (holes, false),
                  }).0;
        self.hole_count = holes;
    }
}

impl BlockInfo{
    /// Returns true if this block is allocated and the address is within the
    /// bounds of this block.
    fn is_in_block(&self, object: GCObjectRef) -> bool {
        // This works because we get zeroed memory from the OS, so
        // self.allocated will be false if this block is not initialized and
        // this method gets only called for objects within the ImmixSpace.
        // After the first initialization the field is properly managed.
        if self.allocated {
            let self_ptr = self as *const BlockInfo as *const u8;
            let self_bound = unsafe{ self_ptr.offset(BLOCK_SIZE as isize)};
            return self_ptr < (object as *const u8)
                && (object as *const u8) <  self_bound;
        }
        return false;
    }

    /// Convert an address on this block into a line number.
    fn object_to_line_num(object: GCObjectRef) -> usize {
        return (object as usize % BLOCK_SIZE) / LINE_SIZE;
    }

    /// Update the line counter for the given object.
    ///
    /// Increment if `increment`, otherwise do a saturating substraction.
    fn update_line_nums(&mut self, object: GCObjectRef, increment: bool) {
        // This calculates how many lines are affected starting from a
        // LINE_SIZE aligned address. So it might not mark enough lines. But
        // that does not matter as we always skip a line in scan_block()
        let line_num = BlockInfo::object_to_line_num(object);
        let object_size = unsafe{ (*object).object_size() };
        for line in line_num..(line_num + (object_size / LINE_SIZE) + 1) {
            match increment {
                true => {
                    if self.line_counter.contains_key(&line) {
                        if let Some(val) = self.line_counter.get_mut(&line) {
                            *val += 1;
                        }
                    } else { self.line_counter.insert(line, 1); }
                    debug!("Incremented line count for line {} to {}", line,
                           self.line_counter.get(&line).unwrap());
                },
                false => {
                    if self.line_counter.contains_key(&line) {
                        if let Some(val) = self.line_counter.get_mut(&line) {
                            *val = Int::saturating_sub(*val, 1);
                        }
                    } else { self.line_counter.insert(line, 0); }
                    debug!("Decremented line count for line {} to {}", line,
                           self.line_counter.get(&line).unwrap());
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>extern mod harfbuzz;

use font::Font;
use font_cache::FontCache;

use au = gfx::geometry;
use au::Au;
use geom::point::Point2D;
use glyph::{GlyphStore, GlyphIndex, GlyphData};
use libc::types::common::c99::int32_t;
use libc::{c_uint, c_int, c_void, c_char};
use ptr::{null, to_unsafe_ptr, offset};
use std::arc;
use util::*;

use harfbuzz::{HB_MEMORY_MODE_READONLY,
                  HB_DIRECTION_LTR};
use harfbuzz::{hb_blob_t, hb_face_t, hb_font_t, hb_font_funcs_t, hb_buffer_t,
                  hb_codepoint_t, hb_bool_t, hb_glyph_position_t,
		  hb_glyph_info_t, hb_var_int_t, hb_position_t};
use harfbuzz::bindgen::{hb_blob_create, hb_blob_destroy,
                           hb_face_create, hb_face_destroy,
                           hb_font_create, hb_font_destroy,
                           hb_buffer_create, hb_buffer_destroy,
                           hb_buffer_add_utf8, hb_shape,
                           hb_buffer_get_glyph_infos,
                           hb_buffer_get_glyph_positions,
                           hb_font_set_ppem, hb_font_set_scale,
                           hb_buffer_set_direction,
                           hb_font_funcs_create, hb_font_funcs_destroy,
                           hb_font_set_funcs,
                           hb_font_funcs_set_glyph_h_advance_func,
                           hb_font_funcs_set_glyph_func,
                           hb_font_funcs_set_glyph_h_kerning_func};

fn float_to_fixed_hb(f: float) -> i32 {
    util::float_to_fixed(16, f)
}

fn fixed_to_float_hb(i: hb_position_t) -> float {
    util::fixed_to_float(16, i)
}

fn fixed_to_rounded_int_hb(f: hb_position_t) -> int {
    util::fixed_to_rounded_int(16, f)
}

pub struct HarfbuzzShaper {
    font: @Font
}

pub impl HarfbuzzShaper {
    static pub fn new(font: @Font) -> HarfbuzzShaper {
        HarfbuzzShaper { font: font }
    }

    /**
    Calculate the layout metrics associated with a some given text
    when rendered in a specific font.
    */
    pub fn shape_text(text: &str, glyphs: &GlyphStore) {
        debug!("shaping text '%s'", text);

        // TODO(Issue #94): harfbuzz fonts and faces should be cached on the
        // Shaper object, which is owned by the Font instance.
        // TODO(Issue #92): font tables should be stored in Font object and cached per-task
        let face_blob: *hb_blob_t = vec::as_imm_buf(*(self.font).fontbuf, |buf: *u8, len: uint| {
            hb_blob_create(buf as *c_char,
                           len as c_uint,
                           HB_MEMORY_MODE_READONLY,
                           null(),
                           null())
        });

        let hb_face: *hb_face_t = hb_face_create(face_blob, 0 as c_uint);
        let hb_font: *hb_font_t = hb_font_create(hb_face);

        // Set points-per-em. if zero, performs no hinting in that direction.
        let pt_size = self.font.style.pt_size;
        hb_font_set_ppem(hb_font, pt_size as c_uint, pt_size as c_uint);
        // Set scaling. Note that this takes 16.16 fixed point.
        hb_font_set_scale(hb_font, float_to_fixed_hb(pt_size) as c_int, float_to_fixed_hb(pt_size) as c_int);

        let funcs: *hb_font_funcs_t = hb_font_funcs_create();
        hb_font_funcs_set_glyph_func(funcs, glyph_func, null(), null());
        hb_font_funcs_set_glyph_h_advance_func(funcs, glyph_h_advance_func, null(), null());

        unsafe {
            let font_data: *c_void = core::ptr::addr_of(self.font) as *c_void;
            hb_font_set_funcs(hb_font, funcs, font_data, null());
        };

        let hb_buffer: *hb_buffer_t = hb_buffer_create();
        hb_buffer_set_direction(hb_buffer, HB_DIRECTION_LTR);

        // Using as_buf because it never does a copy - we don't need the trailing null
        str::as_buf(text, |ctext: *u8, _l: uint| {
            hb_buffer_add_utf8(hb_buffer,
                               ctext as *c_char,
                               text.len() as c_int,
                               0 as c_uint,
                               text.len() as c_int);
        });

        hb_shape(hb_font, hb_buffer, null(), 0 as c_uint);

        let info_buf_len = 0 as c_uint;
        let info_buf = hb_buffer_get_glyph_infos(hb_buffer, to_unsafe_ptr(&info_buf_len));
        assert info_buf.is_not_null();
        let pos_buf_len = 0 as c_uint;
        let pos_buf = hb_buffer_get_glyph_positions(hb_buffer, to_unsafe_ptr(&pos_buf_len));
        assert pos_buf.is_not_null();

        assert info_buf_len == pos_buf_len;

        for uint::range(0u, info_buf_len as uint) |i| { unsafe {
            let hb_info: hb_glyph_info_t = *offset(info_buf, i);
            let hb_pos: hb_glyph_position_t = *offset(pos_buf, i);
            let codepoint = hb_info.codepoint as GlyphIndex;
            let advance: Au = au::from_frac_px(fixed_to_float_hb(hb_pos.x_advance));
            let offset = match (hb_pos.x_offset, hb_pos.y_offset) {
                (0, 0) => None,
                (x, y) => Some(Point2D(au::from_frac_px(fixed_to_float_hb(x)),
                                       au::from_frac_px(fixed_to_float_hb(y))))
            };
            // TODO: convert pos.y_advance into offset adjustment
            // TODO: handle multiple glyphs per char, ligatures, etc.
            // See Issue #
            debug!("glyph %?: index %?, advance %?, offset %?",
                   i, codepoint, advance, offset);

            let data = GlyphData(codepoint, advance, offset, false, false, false);
            glyphs.add_glyph_for_index(i, &data);
        } /* unsafe */ }

        hb_buffer_destroy(hb_buffer);
        hb_font_funcs_destroy(funcs);
        hb_font_destroy(hb_font);
        hb_face_destroy(hb_face);
        hb_blob_destroy(face_blob);
    }
}

extern fn glyph_func(_font: *hb_font_t,
                     font_data: *c_void,
                     unicode: hb_codepoint_t,
                     _variant_selector: hb_codepoint_t,
                     glyph: *mut hb_codepoint_t,
                     _user_data: *c_void) -> hb_bool_t unsafe {

    let font: *Font = font_data as *Font;
    assert font.is_not_null();
    return match (*font).glyph_index(unicode as char) {
        Some(g) => { *glyph = g as hb_codepoint_t; true },
        None => false
    } as hb_bool_t;
}

extern fn glyph_h_advance_func(_font: *hb_font_t,
                               font_data: *c_void,
                               glyph: hb_codepoint_t,
                               _user_data: *c_void) -> hb_position_t unsafe {
    let font: *Font = font_data as *Font;
    assert font.is_not_null();

    let advance = (*font).glyph_h_advance(glyph as GlyphIndex);
    float_to_fixed_hb(advance)
}
<commit_msg>Reuse harfbuzz font, face objects on subsequent shaper calls. Move static functions into HarfbuzzShaper.<commit_after>extern mod harfbuzz;

use font::Font;
use font_cache::FontCache;

use au = gfx::geometry;
use au::Au;
use geom::point::Point2D;
use glyph::{GlyphStore, GlyphIndex, GlyphData};
use libc::types::common::c99::int32_t;
use libc::{c_uint, c_int, c_void, c_char};
use ptr::{null, to_unsafe_ptr, offset};
use std::arc;
use util::*;

use harfbuzz::{HB_MEMORY_MODE_READONLY,
                  HB_DIRECTION_LTR};
use harfbuzz::{hb_blob_t, hb_face_t, hb_font_t, hb_font_funcs_t, hb_buffer_t,
                  hb_codepoint_t, hb_bool_t, hb_glyph_position_t,
		  hb_glyph_info_t, hb_var_int_t, hb_position_t};
use harfbuzz::bindgen::{hb_blob_create, hb_blob_destroy,
                           hb_face_create, hb_face_destroy,
                           hb_font_create, hb_font_destroy,
                           hb_buffer_create, hb_buffer_destroy,
                           hb_buffer_add_utf8, hb_shape,
                           hb_buffer_get_glyph_infos,
                           hb_buffer_get_glyph_positions,
                           hb_font_set_ppem, hb_font_set_scale,
                           hb_buffer_set_direction,
                           hb_font_funcs_create, hb_font_funcs_destroy,
                           hb_font_set_funcs,
                           hb_font_funcs_set_glyph_h_advance_func,
                           hb_font_funcs_set_glyph_func,
                           hb_font_funcs_set_glyph_h_kerning_func};

pub struct HarfbuzzShaper {
    priv font: @Font,
    priv hb_blob: *hb_blob_t,
    priv hb_face: *hb_face_t,
    priv hb_font: *hb_font_t,
    priv hb_funcs: *hb_font_funcs_t,

    drop {
        assert self.hb_blob.is_not_null();
        hb_blob_destroy(self.hb_blob);

        assert self.hb_face.is_not_null();
        hb_face_destroy(self.hb_face);

        assert self.hb_font.is_not_null();
        hb_font_destroy(self.hb_font);

        assert self.hb_funcs.is_not_null();
        hb_font_funcs_destroy(self.hb_funcs);
    }
}

pub impl HarfbuzzShaper {
    static pub fn new(font: @Font) -> HarfbuzzShaper {
        // TODO(Issue #92): font tables should be stored in Font object and cached per-task
        let hb_blob: *hb_blob_t = vec::as_imm_buf(*(font).buf(), |buf: *u8, len: uint| {
            hb_blob_create(buf as *c_char,
                           len as c_uint,
                           HB_MEMORY_MODE_READONLY,
                           null(),
                           null())
        });

        let hb_face: *hb_face_t = hb_face_create(hb_blob, 0 as c_uint);
        let hb_font: *hb_font_t = hb_font_create(hb_face);
        // Set points-per-em. if zero, performs no hinting in that direction.
        let pt_size = font.style.pt_size;
        hb_font_set_ppem(hb_font, pt_size as c_uint, pt_size as c_uint);
        // Set scaling. Note that this takes 16.16 fixed point.
        hb_font_set_scale(hb_font,
                          HarfbuzzShaper::float_to_fixed(pt_size) as c_int,
                          HarfbuzzShaper::float_to_fixed(pt_size) as c_int);

        // configure static function callbacks.
        // NB. This funcs structure could be reused globally, as it never changes.
        let hb_funcs: *hb_font_funcs_t = hb_font_funcs_create();
        hb_font_funcs_set_glyph_func(hb_funcs, glyph_func, null(), null());
        hb_font_funcs_set_glyph_h_advance_func(hb_funcs, glyph_h_advance_func, null(), null());
        unsafe {
            let font_data: *c_void = core::ptr::addr_of(font) as *c_void;
            hb_font_set_funcs(hb_font, hb_funcs, font_data, null());
        };

        HarfbuzzShaper {
            font: font,
            hb_blob: hb_blob,
            hb_face: hb_face,
            hb_font: hb_font,
            hb_funcs: hb_funcs,
        }
    }

    /**
    Calculate the layout metrics associated with a some given text
    when rendered in a specific font.
    */
    pub fn shape_text(text: &str, glyphs: &GlyphStore) {
        debug!("shaping text '%s'", text);

        // TODO(Issue #94): harfbuzz fonts and faces should be cached on the
        // Shaper object, which is owned by the Font instance.

        let hb_buffer: *hb_buffer_t = hb_buffer_create();
        hb_buffer_set_direction(hb_buffer, HB_DIRECTION_LTR);

        // Using as_buf because it never does a copy - we don't need the trailing null
        str::as_buf(text, |ctext: *u8, _l: uint| {
            hb_buffer_add_utf8(hb_buffer,
                               ctext as *c_char,
                               text.len() as c_int,
                               0 as c_uint,
                               text.len() as c_int);
        });

        hb_shape(self.hb_font, hb_buffer, null(), 0 as c_uint);

        let info_buf_len = 0 as c_uint;
        let info_buf = hb_buffer_get_glyph_infos(hb_buffer, to_unsafe_ptr(&info_buf_len));
        assert info_buf.is_not_null();
        let pos_buf_len = 0 as c_uint;
        let pos_buf = hb_buffer_get_glyph_positions(hb_buffer, to_unsafe_ptr(&pos_buf_len));
        assert pos_buf.is_not_null();

        assert info_buf_len == pos_buf_len;

        for uint::range(0u, info_buf_len as uint) |i| { unsafe {
            let hb_info: hb_glyph_info_t = *offset(info_buf, i);
            let hb_pos: hb_glyph_position_t = *offset(pos_buf, i);
            let codepoint = hb_info.codepoint as GlyphIndex;
            let advance: Au = au::from_frac_px(HarfbuzzShaper::fixed_to_float(hb_pos.x_advance));
            let offset = match (hb_pos.x_offset, hb_pos.y_offset) {
                (0, 0) => None,
                (x, y) => Some(Point2D(au::from_frac_px(HarfbuzzShaper::fixed_to_float(x)),
                                       au::from_frac_px(HarfbuzzShaper::fixed_to_float(y))))
            };
            // TODO: convert pos.y_advance into offset adjustment
            // TODO: handle multiple glyphs per char, ligatures, etc.
            // NB. this debug statement is commented out, as it must be checked for every shaped char.
            //debug!("glyph %?: index %?, advance %?, offset %?", i, codepoint, advance, offset);

            let data = GlyphData(codepoint, advance, offset, false, false, false);
            glyphs.add_glyph_for_index(i, &data);
        } /* unsafe */ }

        hb_buffer_destroy(hb_buffer);
    }

    static priv fn float_to_fixed(f: float) -> i32 {
        util::float_to_fixed(16, f)
    }

    static priv fn fixed_to_float(i: hb_position_t) -> float {
        util::fixed_to_float(16, i)
    }

    static priv fn fixed_to_rounded_int(f: hb_position_t) -> int {
        util::fixed_to_rounded_int(16, f)
    }
}

/// Callbacks from Harfbuzz when font map and glyph advance lookup needed.
extern fn glyph_func(_font: *hb_font_t,
                     font_data: *c_void,
                     unicode: hb_codepoint_t,
                     _variant_selector: hb_codepoint_t,
                     glyph: *mut hb_codepoint_t,
                     _user_data: *c_void) -> hb_bool_t unsafe {
    let font: *Font = font_data as *Font;
    assert font.is_not_null();
    return match (*font).glyph_index(unicode as char) {
        Some(g) => { *glyph = g as hb_codepoint_t; true },
        None => false
    } as hb_bool_t;
}

extern fn glyph_h_advance_func(_font: *hb_font_t,
                               font_data: *c_void,
                               glyph: hb_codepoint_t,
                               _user_data: *c_void) -> hb_position_t unsafe {
    let font: *Font = font_data as *Font;
    assert font.is_not_null();

    let advance = (*font).glyph_h_advance(glyph as GlyphIndex);
    HarfbuzzShaper::float_to_fixed(advance)
}
<|endoftext|>


<commit_before>use screen::*;
use context::Context;
use piston_window::{self, PistonWindow};

pub struct Battle {
    main_menu: bool,
}

impl Battle {
    pub fn new(_window: &PistonWindow, _context: &mut Context) -> Self {
        Battle { main_menu: false }
    }
}

impl Screen for Battle {
    fn on_input(&mut self, input: &piston_window::Input, _: &PistonWindow, _: &mut Context) -> InputResults {
        use piston_window::{Input, Button, Key};

        let result = Vec::new();

        match input {
            &Input::Press(button) => {
                match button {
                    Button::Keyboard(Key::Escape) => {
                        self.main_menu = true;
                    }
                    _ => {}
                }
            }
            &Input::Release(button) => {
                match button {
                    Button::Keyboard(Key::Escape) => {
                        self.main_menu = false;
                    }
                    _ => {}
                }
            }
            _ => {}
        }

        result
    }

    fn on_draw(&mut self, _args: &piston_window::RenderArgs, window: &PistonWindow) {
        window.draw_2d(|_context, graphics| {
            piston_window::clear([0.0, 0.0, 0.0, 1.0], graphics);
        });
    }

    fn on_update(&mut self, _args: &piston_window::UpdateArgs) {}
}
<commit_msg>Battle: main menu result<commit_after>use screen::*;
use context::Context;
use piston_window::{self, PistonWindow};

pub struct Battle {
    main_menu: bool,
}

impl Battle {
    pub fn new(_window: &PistonWindow, _context: &mut Context) -> Self {
        Battle { main_menu: false }
    }
}

impl Screen for Battle {
    fn on_input(&mut self, input: &piston_window::Input, window: &PistonWindow, context: &mut Context) -> InputResults {
        use piston_window::{Input, Button, Key};

        let mut result = Vec::new();

        match input {
            &Input::Press(button) => {
                match button {
                    Button::Keyboard(Key::Escape) => {
                        result.push(InputResult::PushScreen(Box::new(MainMenu::new(&window, context))));
                    }
                    _ => {}
                }
            }
            &Input::Release(button) => {
                match button {
                    Button::Keyboard(Key::Escape) => {
                        self.main_menu = false;
                    }
                    _ => {}
                }
            }
            _ => {}
        }

        result
    }

    fn on_draw(&mut self, _args: &piston_window::RenderArgs, window: &PistonWindow) {
        window.draw_2d(|_context, graphics| {
            piston_window::clear([0.0, 0.0, 0.0, 1.0], graphics);
        });
    }

    fn on_update(&mut self, _args: &piston_window::UpdateArgs) {}
}
<|endoftext|>


<commit_before>use crate::index::diff::Error;
use crate::{Change, CrateVersion};
use bstr::BStr;
use git_repository as git;
use std::collections::BTreeSet;
use std::ops::Range;

#[derive(Default)]
pub(crate) struct Delegate {
    changes: Vec<Change>,
    delete_version_ids: BTreeSet<u64>,
    err: Option<Error>,
}

impl Delegate {
    pub fn handle(
        &mut self,
        change: git::object::tree::diff::Change<'_, '_, '_>,
    ) -> Result<git::object::tree::diff::Action, Error> {
        use git::bstr::ByteSlice;
        use git::object::tree::diff::change::Event::*;
        use git::objs::tree::EntryMode::*;
        fn entry_data(
            entry: git::objs::tree::EntryMode,
            id: git::Id<'_>,
        ) -> Result<Option<git::Object<'_>>, Error> {
            matches!(entry, Blob | BlobExecutable)
                .then(|| id.object())
                .transpose()
                .map_err(Into::into)
        }
        if change.location.contains(&b'.') {
            return Ok(Default::default());
        }
        match change.event {
            Addition { entry_mode, id } => {
                if let Some(obj) = entry_data(entry_mode, id)? {
                    for line in obj.data.lines() {
                        let version = version_from_json_line(line, change.location)?;
                        self.changes.push(if version.yanked {
                            Change::Yanked(version)
                        } else {
                            Change::Added(version)
                        });
                    }
                }
            }
            Deletion { entry_mode, .. } => {
                if entry_mode.is_no_tree() {
                    self.changes.push(Change::Deleted {
                        name: change.location.to_string(),
                    });
                }
            }
            Modification { .. } => {
                if let Some(diff) = change.event.diff().transpose()? {
                    let location = change.location;

                    let input = diff.line_tokens();
                    let mut err = None;
                    git::diff::blob::diff(
                        diff.algo,
                        &input,
                        |before: Range<u32>, after: Range<u32>| {
                            if err.is_some() {
                                return;
                            }
                            let mut lines_before = input.before
                                [before.start as usize..before.end as usize]
                                .iter()
                                .map(|&line| input.interner[line].as_bstr())
                                .peekable();
                            let mut lines_after = input.after
                                [after.start as usize..after.end as usize]
                                .iter()
                                .map(|&line| input.interner[line].as_bstr())
                                .peekable();
                            let mut remember = |version: CrateVersion| {
                                self.changes.push(if version.yanked {
                                    Change::Yanked(version)
                                } else {
                                    Change::Added(version)
                                });
                            };
                            loop {
                                match (lines_before.peek().is_some(), lines_after.peek().is_some())
                                {
                                    (true, false) => {
                                        for removed in lines_before {
                                            match version_from_json_line(removed, location) {
                                                Ok(version) => {
                                                    self.delete_version_ids.insert(version.id());
                                                }
                                                Err(e) => {
                                                    err = Some(e);
                                                    break;
                                                }
                                            }
                                        }
                                        break;
                                    }
                                    (false, true) => {
                                        for inserted in lines_after {
                                            match version_from_json_line(inserted, location) {
                                                Ok(version) => remember(version),
                                                Err(e) => {
                                                    err = Some(e);
                                                    break;
                                                }
                                            }
                                        }
                                        break;
                                    }
                                    (true, true) => {
                                        for (removed, inserted) in
                                            lines_before.by_ref().zip(lines_after.by_ref())
                                        {
                                            match version_from_json_line(inserted, location)
                                                .and_then(|inserted| {
                                                    version_from_json_line(removed, location)
                                                        .map(|removed| (removed, inserted))
                                                }) {
                                                Ok((removed_version, inserted_version)) => {
                                                    if removed_version.yanked
                                                        != inserted_version.yanked
                                                    {
                                                        remember(inserted_version);
                                                    }
                                                }
                                                Err(e) => {
                                                    err = Some(e);
                                                    break;
                                                }
                                            }
                                        }
                                    }
                                    (false, false) => break,
                                }
                            }
                        },
                    );
                    if let Some(err) = err {
                        return Err(err);
                    }
                }
            }
        }
        Ok(Default::default())
    }

    pub fn into_result(mut self) -> Result<Vec<Change>, Error> {
        match self.err {
            Some(err) => Err(err),
            None => {
                if !self.delete_version_ids.is_empty() {
                    let deleted_version_ids = &self.delete_version_ids;
                    self.changes.retain(|change| match change {
                        Change::Added(v) | Change::Yanked(v) => {
                            !deleted_version_ids.contains(&v.id())
                        }
                        Change::Deleted { .. } => true,
                    })
                }
                Ok(self.changes)
            }
        }
    }
}

fn version_from_json_line(line: &[u8], file_name: &BStr) -> Result<CrateVersion, Error> {
    serde_json::from_slice(line).map_err(|err| Error::VersionDecode {
        source: err,
        file_name: file_name.into(),
        line: line.into(),
    })
}
<commit_msg>refactor (#26)<commit_after>use crate::index::diff::Error;
use crate::{Change, CrateVersion};
use bstr::BStr;
use git_repository as git;
use std::collections::BTreeSet;
use std::ops::Range;

#[derive(Default)]
pub(crate) struct Delegate {
    changes: Vec<Change>,
    delete_version_ids: BTreeSet<u64>,
    err: Option<Error>,
}

impl Delegate {
    pub fn handle(
        &mut self,
        change: git::object::tree::diff::Change<'_, '_, '_>,
    ) -> Result<git::object::tree::diff::Action, Error> {
        use git::bstr::ByteSlice;
        use git::object::tree::diff::change::Event::*;
        use git::objs::tree::EntryMode::*;
        fn entry_data(
            entry: git::objs::tree::EntryMode,
            id: git::Id<'_>,
        ) -> Result<Option<git::Object<'_>>, Error> {
            matches!(entry, Blob | BlobExecutable)
                .then(|| id.object())
                .transpose()
                .map_err(Into::into)
        }
        if change.location.contains(&b'.') {
            return Ok(Default::default());
        }
        match change.event {
            Addition { entry_mode, id } => {
                if let Some(obj) = entry_data(entry_mode, id)? {
                    for line in obj.data.lines() {
                        let version = version_from_json_line(line, change.location)?;
                        self.changes.push(if version.yanked {
                            Change::Yanked(version)
                        } else {
                            Change::Added(version)
                        });
                    }
                }
            }
            Deletion { entry_mode, .. } => {
                if entry_mode.is_no_tree() {
                    self.changes.push(Change::Deleted {
                        name: change.location.to_string(),
                    });
                }
            }
            Modification { .. } => {
                if let Some(diff) = change.event.diff().transpose()? {
                    let location = change.location;

                    let input = diff.line_tokens();
                    let mut err = None;
                    git::diff::blob::diff(
                        diff.algo,
                        &input,
                        |before: Range<u32>, after: Range<u32>| {
                            if err.is_some() {
                                return;
                            }
                            let mut lines_before = input.before
                                [before.start as usize..before.end as usize]
                                .iter()
                                .map(|&line| input.interner[line].as_bstr())
                                .peekable();
                            let mut lines_after = input.after
                                [after.start as usize..after.end as usize]
                                .iter()
                                .map(|&line| input.interner[line].as_bstr())
                                .peekable();
                            let mut remember = |version: CrateVersion| {
                                self.changes.push(if version.yanked {
                                    Change::Yanked(version)
                                } else {
                                    Change::Added(version)
                                });
                            };
                            'outer: loop {
                                match (lines_before.peek().is_some(), lines_after.peek().is_some())
                                {
                                    (true, false) => {
                                        for removed in lines_before {
                                            match version_from_json_line(removed, location) {
                                                Ok(version) => {
                                                    self.delete_version_ids.insert(version.id());
                                                }
                                                Err(e) => {
                                                    err = Some(e);
                                                    break;
                                                }
                                            }
                                        }
                                        break 'outer;
                                    }
                                    (false, true) => {
                                        for inserted in lines_after {
                                            match version_from_json_line(inserted, location) {
                                                Ok(version) => remember(version),
                                                Err(e) => {
                                                    err = Some(e);
                                                    break;
                                                }
                                            }
                                        }
                                        break 'outer;
                                    }
                                    (true, true) => {
                                        for (removed, inserted) in
                                            lines_before.by_ref().zip(lines_after.by_ref())
                                        {
                                            match version_from_json_line(inserted, location)
                                                .and_then(|inserted| {
                                                    version_from_json_line(removed, location)
                                                        .map(|removed| (removed, inserted))
                                                }) {
                                                Ok((removed_version, inserted_version)) => {
                                                    if removed_version.yanked
                                                        != inserted_version.yanked
                                                    {
                                                        remember(inserted_version);
                                                    }
                                                }
                                                Err(e) => {
                                                    err = Some(e);
                                                    break;
                                                }
                                            }
                                        }
                                    }
                                    (false, false) => break 'outer,
                                }
                            }
                        },
                    );
                    if let Some(err) = err {
                        return Err(err);
                    }
                }
            }
        }
        Ok(Default::default())
    }

    pub fn into_result(mut self) -> Result<Vec<Change>, Error> {
        match self.err {
            Some(err) => Err(err),
            None => {
                if !self.delete_version_ids.is_empty() {
                    let deleted_version_ids = &self.delete_version_ids;
                    self.changes.retain(|change| match change {
                        Change::Added(v) | Change::Yanked(v) => {
                            !deleted_version_ids.contains(&v.id())
                        }
                        Change::Deleted { .. } => true,
                    })
                }
                Ok(self.changes)
            }
        }
    }
}

fn version_from_json_line(line: &[u8], file_name: &BStr) -> Result<CrateVersion, Error> {
    serde_json::from_slice(line).map_err(|err| Error::VersionDecode {
        source: err,
        file_name: file_name.into(),
        line: line.into(),
    })
}
<|endoftext|>