<commit_before>use std::sync::{ Once, ONCE_INIT };
use std::{ mem, ptr };
use rand::{ Rng, OsRng };

const GARBAGE_VALUE: u8 = 0xd0;
const CANARY_SIZE: usize = 16;
static ALLOC_INIT: Once = ONCE_INIT;
static mut PAGE_SIZE: usize = 0;
static mut PAGE_MASK: usize = 0;
static mut CANARY: [u8; CANARY_SIZE] = [0; CANARY_SIZE];

// -- alloc init --

unsafe fn alloc_init() {
    #[cfg(unix)] {
        PAGE_SIZE = ::libc::sysconf(::libc::_SC_PAGESIZE) as usize;
    }

    #[cfg(windows)] {
        let mut si = mem::uninitialized();
        ::kernel32::GetSystemInfo(&mut si);
        PAGE_SIZE = si.dwPageSize as usize;
    }

    if PAGE_SIZE < CANARY_SIZE || PAGE_SIZE < mem::size_of::<usize>() {
        panic!("page size too small");
    }

    PAGE_MASK = PAGE_SIZE - 1;

    OsRng::new().unwrap().fill_bytes(&mut CANARY);
}

// -- aligned alloc / aligned free --

#[cfg(unix)]
unsafe fn alloc_aligned(size: usize) -> Option<*mut u8> {
    let mut memptr = mem::uninitialized();
    match ::libc::posix_memalign(&mut memptr, PAGE_SIZE, size) {
        0 => Some(memptr as *mut u8),
        ::libc::EINVAL => panic!("EINVAL: invalid alignmen. {}", PAGE_SIZE),
        ::libc::ENOMEM => None,
        _ => unreachable!()
    }
}

#[cfg(windows)]
unsafe fn alloc_aligned(size: usize) -> Option<*mut u8> {
    let memptr = ::kernel32::VirtualAlloc(
        ptr::null_mut(),
        size as ::winapi::SIZE_T,
        ::winapi::MEM_COMMIT | ::winapi::MEM_RESERVE,
        ::winapi::PAGE_READWRITE
    );
    if memptr.is_null() {
        None
    } else {
        Some(memptr)
    }
}

#[cfg(unix)]
unsafe fn free_aligned(memptr: *mut u8) {
    ::libc::free(memptr as *mut ::libc::c_void);
}

#[cfg(windows)]
unsafe fn free_aligned(memptr: *mut u8) {
    ::kernel32::VirtualFree(memptr as ::winapi::LPVOID, 0, ::winapi::MEM_RELEASE);
}

// -- malloc / free --

#[inline]
unsafe fn page_round(size: usize) -> usize {
    (size + PAGE_MASK) & (!PAGE_MASK)
}

unsafe fn unprotected_ptr_from_user_ptr(memptr: *const u8) -> *mut u8 {
    let canary_ptr = memptr.offset(-(mem::size_of_val(&CANARY) as isize));
    let unprotected_ptr_u = canary_ptr as usize & !PAGE_MASK;
    if unprotected_ptr_u <= PAGE_SIZE * 2 {
        panic!("user address {} too small", memptr as usize);
    }
    unprotected_ptr_u as *mut u8
}

unsafe fn _malloc(size: usize) -> Option<*mut u8> {
    ALLOC_INIT.call_once(|| alloc_init());

    if size >= ::std::usize::MAX - PAGE_SIZE * 4 {
        return None;
    }

    // aligned alloc ptr
    let size_with_canary = mem::size_of_val(&CANARY) + size;
    let unprotected_size = page_round(size_with_canary);
    let total_size = PAGE_SIZE + PAGE_SIZE + unprotected_size + PAGE_SIZE;
    let base_ptr = match alloc_aligned(total_size) {
        Some(memptr) => memptr,
        None => return None
    };

    // canary offset
    let unprotected_ptr = base_ptr.offset(PAGE_SIZE as isize * 2);
    _mprotect(base_ptr.offset(PAGE_SIZE as isize), PAGE_SIZE, Prot::NoAccess);
    ptr::copy(
        CANARY.as_ptr(),
        unprotected_ptr.offset(unprotected_size as isize) as *mut u8,
        mem::size_of_val(&CANARY)
    );

    // mprotect ptr
    _mprotect(unprotected_ptr.offset(unprotected_size as isize), PAGE_SIZE, Prot::NoAccess);
    ::mlock(unprotected_ptr, unprotected_size);
    let canary_ptr = unprotected_ptr
        .offset(page_round(size_with_canary) as isize)
        .offset(-(size_with_canary as isize));
    let user_ptr = canary_ptr.offset(mem::size_of_val(&CANARY) as isize);
    ptr::copy(CANARY.as_ptr(), canary_ptr as *mut u8, mem::size_of_val(&CANARY));
    ptr::write(base_ptr as *mut usize, unprotected_size);
    _mprotect(base_ptr, PAGE_SIZE, Prot::ReadOnly);

    debug_assert_eq!(unprotected_ptr_from_user_ptr(user_ptr), unprotected_ptr);

    Some(user_ptr)
}

/// Secure malloc.
pub unsafe fn malloc<T>(size: usize) -> Option<*mut T> {
    _malloc(size)
        .map(|memptr| {
            ::memset(memptr, GARBAGE_VALUE as i32, size);
            memptr as *mut T
        })
}

/// Alloc array.
///
/// ```
/// use std::{ slice, mem };
/// use memsec::{ allocarray, free, memzero, memset, memcmp };
///
/// let memptr: *mut u8 = unsafe { allocarray(8).unwrap() };
/// let array = unsafe { slice::from_raw_parts_mut(memptr, 8) };
/// assert_eq!(array, [0xd0; 8]);
/// unsafe { memzero(memptr, 8) };
/// assert_eq!(array, [0; 8]);
/// array[0] = 1;
/// assert_eq!(unsafe { memcmp(memptr, [1, 0, 0, 0, 0, 0, 0, 0].as_ptr(), 8) }, 0);
/// unsafe { free(memptr) };
/// ```
pub unsafe fn allocarray<T>(count: usize) -> Option<*mut T> {
    let size = mem::size_of::<T>();
    if count > mem::size_of::<usize>() && size >= ::std::usize::MAX / count {
        None
    } else {
        malloc(count * size)
    }
}

/// Secure free.
pub unsafe fn free<T>(memptr: *mut T) {
    if memptr.is_null() { return () };
    let memptr = memptr as *mut u8;

    // get unprotected ptr
    let canary_ptr = memptr.offset(-(mem::size_of_val(&CANARY) as isize));
    let unprotected_ptr = unprotected_ptr_from_user_ptr(memptr);
    let base_ptr = unprotected_ptr.offset(-(PAGE_SIZE as isize * 2));
    let unprotected_size = ptr::read(base_ptr as *const usize);
    let total_size = PAGE_SIZE + PAGE_SIZE + unprotected_size + PAGE_SIZE;
    _mprotect(base_ptr, total_size, Prot::ReadWrite);

    // check
    debug_assert_eq!(::memcmp(canary_ptr as *const u8, CANARY.as_ptr(), mem::size_of_val(&CANARY)), 0);
    debug_assert_eq!(::memcmp(
        unprotected_ptr.offset(unprotected_size as isize) as *const u8,
        CANARY.as_ptr(),
        mem::size_of_val(&CANARY)
    ), 0);

    // free
    ::munlock(unprotected_ptr, unprotected_size);
    free_aligned(base_ptr);
}

// -- mprotect --

/// Prot enum.
#[derive(Debug, Clone, PartialEq)]
pub enum Prot {
    #[cfg(unix)] NoAccess = ::libc::PROT_NONE as isize,
    #[cfg(unix)] ReadOnly = ::libc::PROT_READ as isize,
    #[cfg(unix)] WriteOnly = ::libc::PROT_WRITE as isize,
    #[cfg(unix)] ReadWrite = (::libc::PROT_READ | ::libc::PROT_WRITE) as isize,
    #[cfg(unix)] Execute = ::libc::PROT_EXEC as isize,
    #[cfg(unix)] ReadExec = (::libc::PROT_READ | ::libc::PROT_EXEC) as isize,
    #[cfg(unix)] WriteExec = (::libc::PROT_WRITE | ::libc::PROT_EXEC) as isize,
    #[cfg(unix)] ReadWriteExec = (::libc::PROT_READ | ::libc::PROT_WRITE | ::libc::PROT_EXEC) as isize,

    #[cfg(windows)] NoAccess = ::winapi::PAGE_NOACCESS as isize,
    #[cfg(windows)] ReadOnly = ::winapi::PAGE_READONLY as isize,
    #[cfg(windows)] ReadWrite = ::winapi::PAGE_READWRITE as isize,
    #[cfg(windows)] WriteCopy = ::winapi::PAGE_WRITECOPY as isize,
    #[cfg(windows)] Execute = ::winapi::PAGE_EXECUTE as isize,
    #[cfg(windows)] ReadExec = ::winapi::PAGE_EXECUTE_READ as isize,
    #[cfg(windows)] ReadWriteExec = ::winapi::PAGE_EXECUTE_READWRITE as isize,
    #[cfg(windows)] WriteCopyExec = ::winapi::PAGE_EXECUTE_WRITECOPY as isize,
    #[cfg(windows)] Guard = ::winapi::PAGE_GUARD as isize,
    #[cfg(windows)] NoCache = ::winapi::PAGE_NOCACHE as isize,
    #[cfg(windows)] WriteCombine = ::winapi::PAGE_WRITECOMBINE as isize,
    #[cfg(windows)] RevertToFileMap = ::winapi::PAGE_REVERT_TO_FILE_MAP as isize,
    #[cfg(windows)] TargetsNoUpdate = ::winapi::PAGE_TARGETS_NO_UPDATE as isize,

    // ::winapi::PAGE_TARGETS_INVALID == ::winapi::PAGE_TARGETS_NO_UPDATE
    // #[cfg(windows)] TargetsInvalid = ::winapi::PAGE_TARGETS_INVALID as isize,
}

/// Unix mprotect.
#[cfg(unix)]
unsafe fn _mprotect<T>(ptr: *mut T, len: usize, prot: Prot) -> bool {
    ::libc::mprotect(ptr as *mut ::libc::c_void, len, prot as ::libc::c_int) == 0
}

/// Windows VirtualProtect.
#[cfg(windows)]
unsafe fn _mprotect<T>(ptr: *mut T, len: usize, prot: Prot) -> bool {
    let mut old = std::mem::uninitialized();
    ::kernel32::VirtualProtect(
        ptr as ::winapi::LPVOID,
        len as ::winapi::SIZE_T,
        prot as ::winapi::DWORD,
        &mut old as ::winapi::PDWORD
    ) != 0
}

/// Secure mprotect.
pub unsafe fn mprotect<T>(memptr: *mut T, prot: Prot) -> bool {
    let memptr = memptr as *mut u8;
    let unprotected_ptr = unprotected_ptr_from_user_ptr(memptr);
    let base_ptr = unprotected_ptr.offset(-(PAGE_SIZE as isize * 2));
    let unprotected_size = ptr::read(base_ptr as *const usize);
    _mprotect(unprotected_ptr, unprotected_size, prot)
}

// -- test --

#[cfg(all(unix, test, not(any(target_os = "macos", target_os = "ios"))))]
#[should_panic]
#[test]
fn mprotect_test() {
    use nix::sys::signal;

    ALLOC_INIT.call_once(|| unsafe { alloc_init() });

    extern fn sigsegv(_: i32) { panic!() }
    let sigaction = signal::SigAction::new(
        signal::SigHandler::Handler(sigsegv),
        signal::SA_SIGINFO,
        signal::SigSet::empty(),
    );
    unsafe { signal::sigaction(signal::SIGSEGV, &sigaction).ok() };

    let x: *mut u8 = unsafe { alloc_aligned(16 * mem::size_of::<u8>()).unwrap() };
    unsafe { _mprotect(x, 16 * mem::size_of::<u8>(), Prot::NoAccess) };

    unsafe { ::memzero(x, 16 * mem::size_of::<u8>()) }; // SIGSEGV!
}

#[test]
fn alloc_free_aligned() {
    ALLOC_INIT.call_once(|| unsafe { alloc_init() });

    let x: *mut u8 = unsafe { alloc_aligned(16 * mem::size_of::<u8>()).unwrap() };
    unsafe { ::memzero(x, 16 * mem::size_of::<u8>()) };
    assert!(unsafe { _mprotect(x, 16 * mem::size_of::<u8>(), Prot::ReadOnly) });
    assert_eq!(unsafe { ::memcmp(x, [0; 16].as_ptr(), 16 * mem::size_of::<u8>()) }, 0);
    assert!(unsafe { _mprotect(x, 16 * mem::size_of::<u8>(), Prot::NoAccess) });
    assert!(unsafe { _mprotect(x, 16 * mem::size_of::<u8>(), Prot::ReadWrite) });
    unsafe { ::memzero(x, 16 * mem::size_of::<u8>()) };
    unsafe { free_aligned(x) };
}
<commit_msg>fix: _mprotect missing ::std, alloc_aligned type<commit_after>use std::sync::{ Once, ONCE_INIT };
use std::{ mem, ptr };
use rand::{ Rng, OsRng };

const GARBAGE_VALUE: u8 = 0xd0;
const CANARY_SIZE: usize = 16;
static ALLOC_INIT: Once = ONCE_INIT;
static mut PAGE_SIZE: usize = 0;
static mut PAGE_MASK: usize = 0;
static mut CANARY: [u8; CANARY_SIZE] = [0; CANARY_SIZE];

// -- alloc init --

unsafe fn alloc_init() {
    #[cfg(unix)] {
        PAGE_SIZE = ::libc::sysconf(::libc::_SC_PAGESIZE) as usize;
    }

    #[cfg(windows)] {
        let mut si = mem::uninitialized();
        ::kernel32::GetSystemInfo(&mut si);
        PAGE_SIZE = si.dwPageSize as usize;
    }

    if PAGE_SIZE < CANARY_SIZE || PAGE_SIZE < mem::size_of::<usize>() {
        panic!("page size too small");
    }

    PAGE_MASK = PAGE_SIZE - 1;

    OsRng::new().unwrap().fill_bytes(&mut CANARY);
}

// -- aligned alloc / aligned free --

#[cfg(unix)]
unsafe fn alloc_aligned(size: usize) -> Option<*mut u8> {
    let mut memptr = mem::uninitialized();
    match ::libc::posix_memalign(&mut memptr, PAGE_SIZE, size) {
        0 => Some(memptr as *mut u8),
        ::libc::EINVAL => panic!("EINVAL: invalid alignmen. {}", PAGE_SIZE),
        ::libc::ENOMEM => None,
        _ => unreachable!()
    }
}

#[cfg(windows)]
unsafe fn alloc_aligned(size: usize) -> Option<*mut u8> {
    let memptr = ::kernel32::VirtualAlloc(
        ptr::null_mut(),
        size as ::winapi::SIZE_T,
        ::winapi::MEM_COMMIT | ::winapi::MEM_RESERVE,
        ::winapi::PAGE_READWRITE
    );
    if memptr.is_null() {
        None
    } else {
        Some(memptr as *mut u8)
    }
}

#[cfg(unix)]
unsafe fn free_aligned(memptr: *mut u8) {
    ::libc::free(memptr as *mut ::libc::c_void);
}

#[cfg(windows)]
unsafe fn free_aligned(memptr: *mut u8) {
    ::kernel32::VirtualFree(memptr as ::winapi::LPVOID, 0, ::winapi::MEM_RELEASE);
}

// -- malloc / free --

#[inline]
unsafe fn page_round(size: usize) -> usize {
    (size + PAGE_MASK) & (!PAGE_MASK)
}

unsafe fn unprotected_ptr_from_user_ptr(memptr: *const u8) -> *mut u8 {
    let canary_ptr = memptr.offset(-(mem::size_of_val(&CANARY) as isize));
    let unprotected_ptr_u = canary_ptr as usize & !PAGE_MASK;
    if unprotected_ptr_u <= PAGE_SIZE * 2 {
        panic!("user address {} too small", memptr as usize);
    }
    unprotected_ptr_u as *mut u8
}

unsafe fn _malloc(size: usize) -> Option<*mut u8> {
    ALLOC_INIT.call_once(|| alloc_init());

    if size >= ::std::usize::MAX - PAGE_SIZE * 4 {
        return None;
    }

    // aligned alloc ptr
    let size_with_canary = mem::size_of_val(&CANARY) + size;
    let unprotected_size = page_round(size_with_canary);
    let total_size = PAGE_SIZE + PAGE_SIZE + unprotected_size + PAGE_SIZE;
    let base_ptr = match alloc_aligned(total_size) {
        Some(memptr) => memptr,
        None => return None
    };

    // canary offset
    let unprotected_ptr = base_ptr.offset(PAGE_SIZE as isize * 2);
    _mprotect(base_ptr.offset(PAGE_SIZE as isize), PAGE_SIZE, Prot::NoAccess);
    ptr::copy(
        CANARY.as_ptr(),
        unprotected_ptr.offset(unprotected_size as isize) as *mut u8,
        mem::size_of_val(&CANARY)
    );

    // mprotect ptr
    _mprotect(unprotected_ptr.offset(unprotected_size as isize), PAGE_SIZE, Prot::NoAccess);
    ::mlock(unprotected_ptr, unprotected_size);
    let canary_ptr = unprotected_ptr
        .offset(page_round(size_with_canary) as isize)
        .offset(-(size_with_canary as isize));
    let user_ptr = canary_ptr.offset(mem::size_of_val(&CANARY) as isize);
    ptr::copy(CANARY.as_ptr(), canary_ptr as *mut u8, mem::size_of_val(&CANARY));
    ptr::write(base_ptr as *mut usize, unprotected_size);
    _mprotect(base_ptr, PAGE_SIZE, Prot::ReadOnly);

    debug_assert_eq!(unprotected_ptr_from_user_ptr(user_ptr), unprotected_ptr);

    Some(user_ptr)
}

/// Secure malloc.
pub unsafe fn malloc<T>(size: usize) -> Option<*mut T> {
    _malloc(size)
        .map(|memptr| {
            ::memset(memptr, GARBAGE_VALUE as i32, size);
            memptr as *mut T
        })
}

/// Alloc array.
///
/// ```
/// use std::{ slice, mem };
/// use memsec::{ allocarray, free, memzero, memset, memcmp };
///
/// let memptr: *mut u8 = unsafe { allocarray(8).unwrap() };
/// let array = unsafe { slice::from_raw_parts_mut(memptr, 8) };
/// assert_eq!(array, [0xd0; 8]);
/// unsafe { memzero(memptr, 8) };
/// assert_eq!(array, [0; 8]);
/// array[0] = 1;
/// assert_eq!(unsafe { memcmp(memptr, [1, 0, 0, 0, 0, 0, 0, 0].as_ptr(), 8) }, 0);
/// unsafe { free(memptr) };
/// ```
pub unsafe fn allocarray<T>(count: usize) -> Option<*mut T> {
    let size = mem::size_of::<T>();
    if count > mem::size_of::<usize>() && size >= ::std::usize::MAX / count {
        None
    } else {
        malloc(count * size)
    }
}

/// Secure free.
pub unsafe fn free<T>(memptr: *mut T) {
    if memptr.is_null() { return () };
    let memptr = memptr as *mut u8;

    // get unprotected ptr
    let canary_ptr = memptr.offset(-(mem::size_of_val(&CANARY) as isize));
    let unprotected_ptr = unprotected_ptr_from_user_ptr(memptr);
    let base_ptr = unprotected_ptr.offset(-(PAGE_SIZE as isize * 2));
    let unprotected_size = ptr::read(base_ptr as *const usize);
    let total_size = PAGE_SIZE + PAGE_SIZE + unprotected_size + PAGE_SIZE;
    _mprotect(base_ptr, total_size, Prot::ReadWrite);

    // check
    debug_assert_eq!(::memcmp(canary_ptr as *const u8, CANARY.as_ptr(), mem::size_of_val(&CANARY)), 0);
    debug_assert_eq!(::memcmp(
        unprotected_ptr.offset(unprotected_size as isize) as *const u8,
        CANARY.as_ptr(),
        mem::size_of_val(&CANARY)
    ), 0);

    // free
    ::munlock(unprotected_ptr, unprotected_size);
    free_aligned(base_ptr);
}

// -- mprotect --

/// Prot enum.
#[derive(Debug, Clone, PartialEq)]
pub enum Prot {
    #[cfg(unix)] NoAccess = ::libc::PROT_NONE as isize,
    #[cfg(unix)] ReadOnly = ::libc::PROT_READ as isize,
    #[cfg(unix)] WriteOnly = ::libc::PROT_WRITE as isize,
    #[cfg(unix)] ReadWrite = (::libc::PROT_READ | ::libc::PROT_WRITE) as isize,
    #[cfg(unix)] Execute = ::libc::PROT_EXEC as isize,
    #[cfg(unix)] ReadExec = (::libc::PROT_READ | ::libc::PROT_EXEC) as isize,
    #[cfg(unix)] WriteExec = (::libc::PROT_WRITE | ::libc::PROT_EXEC) as isize,
    #[cfg(unix)] ReadWriteExec = (::libc::PROT_READ | ::libc::PROT_WRITE | ::libc::PROT_EXEC) as isize,

    #[cfg(windows)] NoAccess = ::winapi::PAGE_NOACCESS as isize,
    #[cfg(windows)] ReadOnly = ::winapi::PAGE_READONLY as isize,
    #[cfg(windows)] ReadWrite = ::winapi::PAGE_READWRITE as isize,
    #[cfg(windows)] WriteCopy = ::winapi::PAGE_WRITECOPY as isize,
    #[cfg(windows)] Execute = ::winapi::PAGE_EXECUTE as isize,
    #[cfg(windows)] ReadExec = ::winapi::PAGE_EXECUTE_READ as isize,
    #[cfg(windows)] ReadWriteExec = ::winapi::PAGE_EXECUTE_READWRITE as isize,
    #[cfg(windows)] WriteCopyExec = ::winapi::PAGE_EXECUTE_WRITECOPY as isize,
    #[cfg(windows)] Guard = ::winapi::PAGE_GUARD as isize,
    #[cfg(windows)] NoCache = ::winapi::PAGE_NOCACHE as isize,
    #[cfg(windows)] WriteCombine = ::winapi::PAGE_WRITECOMBINE as isize,
    #[cfg(windows)] RevertToFileMap = ::winapi::PAGE_REVERT_TO_FILE_MAP as isize,
    #[cfg(windows)] TargetsNoUpdate = ::winapi::PAGE_TARGETS_NO_UPDATE as isize,

    // ::winapi::PAGE_TARGETS_INVALID == ::winapi::PAGE_TARGETS_NO_UPDATE
    // #[cfg(windows)] TargetsInvalid = ::winapi::PAGE_TARGETS_INVALID as isize,
}

/// Unix mprotect.
#[cfg(unix)]
unsafe fn _mprotect<T>(ptr: *mut T, len: usize, prot: Prot) -> bool {
    ::libc::mprotect(ptr as *mut ::libc::c_void, len, prot as ::libc::c_int) == 0
}

/// Windows VirtualProtect.
#[cfg(windows)]
unsafe fn _mprotect<T>(ptr: *mut T, len: usize, prot: Prot) -> bool {
    let mut old = ::std::mem::uninitialized();
    ::kernel32::VirtualProtect(
        ptr as ::winapi::LPVOID,
        len as ::winapi::SIZE_T,
        prot as ::winapi::DWORD,
        &mut old as ::winapi::PDWORD
    ) != 0
}

/// Secure mprotect.
pub unsafe fn mprotect<T>(memptr: *mut T, prot: Prot) -> bool {
    let memptr = memptr as *mut u8;
    let unprotected_ptr = unprotected_ptr_from_user_ptr(memptr);
    let base_ptr = unprotected_ptr.offset(-(PAGE_SIZE as isize * 2));
    let unprotected_size = ptr::read(base_ptr as *const usize);
    _mprotect(unprotected_ptr, unprotected_size, prot)
}

// -- test --

#[cfg(all(unix, test, not(any(target_os = "macos", target_os = "ios"))))]
#[should_panic]
#[test]
fn mprotect_test() {
    use nix::sys::signal;

    ALLOC_INIT.call_once(|| unsafe { alloc_init() });

    extern fn sigsegv(_: i32) { panic!() }
    let sigaction = signal::SigAction::new(
        signal::SigHandler::Handler(sigsegv),
        signal::SA_SIGINFO,
        signal::SigSet::empty(),
    );
    unsafe { signal::sigaction(signal::SIGSEGV, &sigaction).ok() };

    let x: *mut u8 = unsafe { alloc_aligned(16 * mem::size_of::<u8>()).unwrap() };
    unsafe { _mprotect(x, 16 * mem::size_of::<u8>(), Prot::NoAccess) };

    unsafe { ::memzero(x, 16 * mem::size_of::<u8>()) }; // SIGSEGV!
}

#[test]
fn alloc_free_aligned() {
    ALLOC_INIT.call_once(|| unsafe { alloc_init() });

    let x: *mut u8 = unsafe { alloc_aligned(16 * mem::size_of::<u8>()).unwrap() };
    unsafe { ::memzero(x, 16 * mem::size_of::<u8>()) };
    assert!(unsafe { _mprotect(x, 16 * mem::size_of::<u8>(), Prot::ReadOnly) });
    assert_eq!(unsafe { ::memcmp(x, [0; 16].as_ptr(), 16 * mem::size_of::<u8>()) }, 0);
    assert!(unsafe { _mprotect(x, 16 * mem::size_of::<u8>(), Prot::NoAccess) });
    assert!(unsafe { _mprotect(x, 16 * mem::size_of::<u8>(), Prot::ReadWrite) });
    unsafe { ::memzero(x, 16 * mem::size_of::<u8>()) };
    unsafe { free_aligned(x) };
}
<|endoftext|>


<commit_before>//
extern crate termion;

//
use self::termion::event::{Event, Key, MouseEvent};
use self::termion::input::{TermRead, MouseTerminal};
use self::termion::raw::IntoRawMode;
use self::termion::terminal_size;

use std::io::{self, Write, stdin, Stdout};

//
use core::screen::Screen;
use core::codepointinfo::CodepointInfo;

use core::editor::Editor;

fn fill_screen(screen: &mut Screen, data: &[u8]) {

    screen.clear();

    let mut offset = 0;
    for c in data {

        let mut displayed_cp: char = *c as char;
        if *c as char == '\n' as char {
            displayed_cp = ' ';
        }

        let cpi = CodepointInfo {
            cp: *c as char,
            displayed_cp: displayed_cp,
            offset: offset,
        };

        let (ok, _) = screen.push(cpi);
        offset += 1;
        if ok == false {
            break;
        }
    }
}

fn draw_screen(screen: &Screen, stdout: &mut Stdout) {

    write!(stdout, "{}", termion::cursor::Goto(1, 1)).unwrap();

    for l in 0..screen.height {
        let line = &screen.line[l];

        for c in 0..line.width {
            let cpi = &line.chars[c];
            write!(stdout, "{}", cpi.displayed_cp).unwrap();
        }

        if l < screen.height - 1 {
            write!(stdout, "\r\n").unwrap();
        }
    }

    stdout.flush().unwrap();
}

fn draw_buffer(buf: &Option<&Box<::core::buffer::Buffer>>,
               mut scr: &mut Screen,
               mut stdout: &mut Stdout) {
    match *buf {
        Some(ref b) => {
            match b.byte_buffer {

                Some(ref byte_buf) => {
                    fill_screen(&mut scr, &byte_buf.data);
                    draw_screen(&scr, &mut stdout);
                }
                _ => {}
            }
        }
        _ => {}
    }
}

fn terminal_clear_current_line(mut stdout: &mut Stdout, line_width: u16) {
    for _ in 0..line_width {
        write!(stdout, " ").unwrap();
    }
}

fn terminal_cursor_to(mut stdout: &mut Stdout, x: u16, y: u16) {
    write!(stdout, "{}", termion::cursor::Goto(x, y)).unwrap();
}

fn terminal_clear_screen(stdout: &mut Stdout, clear_toggle_flag: &mut bool) {
    if *clear_toggle_flag == true {
        write!(stdout, "{}", termion::clear::All).unwrap();
        stdout.flush().unwrap();
    }
    *clear_toggle_flag = false;
}

pub fn main_loop(editor: &mut Editor) {

    let mut stdout = MouseTerminal::from(io::stdout().into_raw_mode().unwrap());

    write!(stdout, "{}{}", termion::cursor::Hide, termion::clear::All).unwrap();
    stdout.flush().unwrap();

    let mut keys: Vec<Event> = Vec::new();

    let mut quit = false;
    let mut clear_toggle_flag = true;

    //
    let display_status = true;

    // select file
    let mut bid = 2;
    let mut buf = editor.buffer_map.get(&bid);
    let mut status_line_y = 0 as u16;
    let mut status = String::new();

    while !quit {
        let (width, height) = terminal_size().unwrap();
        let mut scr = Screen::new(width as usize, height as usize);

        status_line_y += 1;
        status_line_y %= height;

        draw_buffer(&buf, &mut scr, &mut stdout);
        if display_status == true {
            display_status_line(&buf, &status, status_line_y, width, &mut stdout);
        }

        for evt in stdin().events() {

            keys.push(evt.unwrap());
            let evt = keys[keys.len() - 1].clone();

            draw_buffer(&buf, &mut scr, &mut stdout);

            // Print recieved Events...
            match evt {

                Event::Key(k) => {
                    match k {
                        // Exit.
                        Key::Ctrl('r') => {
                            keys.clear();
                        }

                        Key::Ctrl('c') => {
                            if keys.len() > 1 {
                                if let Event::Key(prev_event) = keys[keys.len() - 2] {
                                    if let Key::Ctrl(prev_char) = prev_event {
                                        if prev_char == 'x' {
                                            quit = true;
                                            break;
                                        }
                                    }
                                }
                            }
                        }

                        Key::Char(c) => {
                            if c == '\n' {
                                status = format!("'{}'", "<newline>");
                            } else {
                                status = format!("'{}'", c);
                            }
                        }
                        Key::Alt(c) => status = format!("Alt-{}", c),
                        Key::Ctrl(c) => status = format!("Ctrl-{}", c),

                        Key::F(1) => {
                            if bid > 0 {
                                bid -= 1;
                            }
                            buf = editor.buffer_map.get(&bid);
                            clear_toggle_flag = true;
                            break;
                        }

                        Key::F(2) => {
                            bid = ::std::cmp::min(bid + 1, (editor.buffer_map.len() - 1) as u64);
                            buf = editor.buffer_map.get(&bid);
                            clear_toggle_flag = true;
                            break;
                        }

                        Key::F(f) => status = format!("F{:?}", f),
                        Key::Left => status = format!("<left>"),
                        Key::Right => status = format!("<right>"),
                        Key::Up => status = format!("<up>"),
                        Key::Down => status = format!("<down>"),
                        Key::Backspace => status = format!("<backspace>"),
                        Key::Home => status = format!("<Home>"),
                        Key::End => status = format!("<End>"),
                        Key::PageUp => status = format!("<PageUp>"),
                        Key::PageDown => status = format!("<PageDown>"),
                        Key::Delete => status = format!("<Delete>"),
                        Key::Insert => status = format!("<Insert>"),
                        Key::Esc => status = format!("<Esc>"),
                        _ => status = format!("Other"),
                    }
                }

                Event::Mouse(m) => {
                    match m {
                        MouseEvent::Press(mb, x, y) => {
                            status = format!("MouseEvent::Press => MouseButton {:?} @ ({}, {})",
                                             mb,
                                             x,
                                             y);
                        }

                        MouseEvent::Release(x, y) => {
                            status = format!("MouseEvent::Release => @ ({}, {})", x, y);
                        }

                        MouseEvent::Hold(x, y) => {
                            status = format!("MouseEvent::Hold => @ ({}, {})", x, y);
                        }
                    };
                }

                Event::Unsupported(_) => {}

            }

            if display_status == true {
                display_status_line(&buf, &status, status_line_y, width, &mut stdout);
            }
            break;
        }
    }

    // quit
    // clear, restore cursor
    write!(stdout, "{}{}", termion::clear::All, termion::cursor::Show).unwrap();
    stdout.flush().unwrap();
}

fn display_status_line(buf: &Option<&Box<::core::buffer::Buffer>>,
                       status: &str,
                       line: u16,
                       width: u16,
                       mut stdout: &mut Stdout) {
    // select/clear last line
    let name = match *buf {
        Some(ref b) => b.name.as_str(),
        None => "",
    };

    let file_name = match *buf {
        Some(ref b) => {
            match b.byte_buffer {
                Some(ref bb) => bb.file_name.as_str(),
                None => "",
            }
        }
        None => "",
    };

    terminal_cursor_to(&mut stdout, 1, line);
    terminal_clear_current_line(&mut stdout, width);
    terminal_cursor_to(&mut stdout, 1, line);

    let status_str = format!("line {} buffer_name '{}', file: '{}', event '{}'",
                             line,
                             name,
                             file_name,
                             status);

    print!("{}", status_str);
    stdout.flush().unwrap();
}
<commit_msg>remove unused code<commit_after>//
extern crate termion;

//
use self::termion::event::{Event, Key, MouseEvent};
use self::termion::input::{TermRead, MouseTerminal};
use self::termion::raw::IntoRawMode;
use self::termion::terminal_size;

use std::io::{self, Write, stdin, Stdout};

//
use core::screen::Screen;
use core::codepointinfo::CodepointInfo;

use core::editor::Editor;

fn fill_screen(screen: &mut Screen, data: &[u8]) {

    screen.clear();

    let mut offset = 0;
    for c in data {

        let mut displayed_cp: char = *c as char;
        if *c as char == '\n' as char {
            displayed_cp = ' ';
        }

        let cpi = CodepointInfo {
            cp: *c as char,
            displayed_cp: displayed_cp,
            offset: offset,
        };

        let (ok, _) = screen.push(cpi);
        offset += 1;
        if ok == false {
            break;
        }
    }
}

fn draw_screen(screen: &Screen, stdout: &mut Stdout) {

    write!(stdout, "{}", termion::cursor::Goto(1, 1)).unwrap();

    for l in 0..screen.height {
        let line = &screen.line[l];

        for c in 0..line.width {
            let cpi = &line.chars[c];
            write!(stdout, "{}", cpi.displayed_cp).unwrap();
        }

        if l < screen.height - 1 {
            write!(stdout, "\r\n").unwrap();
        }
    }

    stdout.flush().unwrap();
}

fn draw_buffer(buf: &Option<&Box<::core::buffer::Buffer>>,
               mut scr: &mut Screen,
               mut stdout: &mut Stdout) {
    match *buf {
        Some(ref b) => {
            match b.byte_buffer {

                Some(ref byte_buf) => {
                    fill_screen(&mut scr, &byte_buf.data);
                    draw_screen(&scr, &mut stdout);
                }
                _ => {}
            }
        }
        _ => {}
    }
}

fn terminal_clear_current_line(mut stdout: &mut Stdout, line_width: u16) {
    for _ in 0..line_width {
        write!(stdout, " ").unwrap();
    }
}

fn terminal_cursor_to(mut stdout: &mut Stdout, x: u16, y: u16) {
    write!(stdout, "{}", termion::cursor::Goto(x, y)).unwrap();
}

pub fn main_loop(editor: &mut Editor) {

    let mut stdout = MouseTerminal::from(io::stdout().into_raw_mode().unwrap());

    write!(stdout, "{}{}", termion::cursor::Hide, termion::clear::All).unwrap();
    stdout.flush().unwrap();

    let mut keys: Vec<Event> = Vec::new();

    let mut quit = false;
    // let mut clear_toggle_flag = true;

    //
    let display_status = true;

    // select file
    let mut bid = 2;
    let mut buf = editor.buffer_map.get(&bid);
    let mut status_line_y = 0 as u16;
    let mut status = String::new();

    while !quit {
        let (width, height) = terminal_size().unwrap();
        let mut scr = Screen::new(width as usize, height as usize);

        status_line_y += 1;
        status_line_y %= height;

        draw_buffer(&buf, &mut scr, &mut stdout);
        if display_status == true {
            display_status_line(&buf, &status, status_line_y, width, &mut stdout);
        }

        for evt in stdin().events() {

            keys.push(evt.unwrap());
            let evt = keys[keys.len() - 1].clone();

            draw_buffer(&buf, &mut scr, &mut stdout);

            // Print recieved Events...
            match evt {

                Event::Key(k) => {
                    match k {
                        // Exit.
                        Key::Ctrl('r') => {
                            keys.clear();
                        }

                        Key::Ctrl('c') => {
                            if keys.len() > 1 {
                                if let Event::Key(prev_event) = keys[keys.len() - 2] {
                                    if let Key::Ctrl(prev_char) = prev_event {
                                        if prev_char == 'x' {
                                            quit = true;
                                            break;
                                        }
                                    }
                                }
                            }
                        }

                        Key::Char(c) => {
                            if c == '\n' {
                                status = format!("'{}'", "<newline>");
                            } else {
                                status = format!("'{}'", c);
                            }
                        }
                        Key::Alt(c) => status = format!("Alt-{}", c),
                        Key::Ctrl(c) => status = format!("Ctrl-{}", c),

                        Key::F(1) => {
                            if bid > 0 {
                                bid -= 1;
                            }
                            buf = editor.buffer_map.get(&bid);
                            break;
                        }

                        Key::F(2) => {
                            bid = ::std::cmp::min(bid + 1, (editor.buffer_map.len() - 1) as u64);
                            buf = editor.buffer_map.get(&bid);
                            break;
                        }

                        Key::F(f) => status = format!("F{:?}", f),
                        Key::Left => status = format!("<left>"),
                        Key::Right => status = format!("<right>"),
                        Key::Up => status = format!("<up>"),
                        Key::Down => status = format!("<down>"),
                        Key::Backspace => status = format!("<backspace>"),
                        Key::Home => status = format!("<Home>"),
                        Key::End => status = format!("<End>"),
                        Key::PageUp => status = format!("<PageUp>"),
                        Key::PageDown => status = format!("<PageDown>"),
                        Key::Delete => status = format!("<Delete>"),
                        Key::Insert => status = format!("<Insert>"),
                        Key::Esc => status = format!("<Esc>"),
                        _ => status = format!("Other"),
                    }
                }

                Event::Mouse(m) => {
                    match m {
                        MouseEvent::Press(mb, x, y) => {
                            status = format!("MouseEvent::Press => MouseButton {:?} @ ({}, {})",
                                             mb,
                                             x,
                                             y);
                        }

                        MouseEvent::Release(x, y) => {
                            status = format!("MouseEvent::Release => @ ({}, {})", x, y);
                        }

                        MouseEvent::Hold(x, y) => {
                            status = format!("MouseEvent::Hold => @ ({}, {})", x, y);
                        }
                    };
                }

                Event::Unsupported(_) => {}

            }

            if display_status == true {
                display_status_line(&buf, &status, status_line_y, width, &mut stdout);
            }
            break;
        }
    }

    // quit
    // clear, restore cursor
    write!(stdout, "{}{}", termion::clear::All, termion::cursor::Show).unwrap();
    stdout.flush().unwrap();
}

fn display_status_line(buf: &Option<&Box<::core::buffer::Buffer>>,
                       status: &str,
                       line: u16,
                       width: u16,
                       mut stdout: &mut Stdout) {
    // select/clear last line
    let name = match *buf {
        Some(ref b) => b.name.as_str(),
        None => "",
    };

    let file_name = match *buf {
        Some(ref b) => {
            match b.byte_buffer {
                Some(ref bb) => bb.file_name.as_str(),
                None => "",
            }
        }
        None => "",
    };

    terminal_cursor_to(&mut stdout, 1, line);
    terminal_clear_current_line(&mut stdout, width);
    terminal_cursor_to(&mut stdout, 1, line);

    let status_str = format!("line {} buffer_name '{}', file: '{}', event '{}'",
                             line,
                             name,
                             file_name,
                             status);

    print!("{}", status_str);
    stdout.flush().unwrap();
}
<|endoftext|>


<commit_before>use std::borrow::{Cow, ToOwned};
use std::ffi::{CStr, CString};
use std::str;
use ::utf8::PascalString;

#[derive(Hash)]
pub struct PascalStr {
    string: str
}

impl PascalStr {
    #[inline]
    pub fn as_ptr(&self) -> *const u8 {
        (&self.string).as_ptr()
    }

    #[inline]
    pub fn as_mut_ptr(&mut self) -> *mut u8 {
        &mut self.string as *mut str as *mut u8
    }

    #[inline]
    pub fn as_str(&self) -> &str {
        &self.string
    }

    #[inline]
    pub fn as_mut_str(&mut self) -> &mut str {
        &mut self.string
    }

    #[inline]
    pub fn as_bytes(&self) -> &[u8] {
        self.string.as_bytes()
    }

    pub fn as_cstr(&self) -> Result<Cow<CStr>, InteriorNullError> {
        unimplemented!()
    }

    #[inline]
    pub fn len(&self) -> usize {
        self.string.len()
    }

    #[inline]
    pub fn chars(&self) -> Chars {
        self.string.chars()
    }

    #[inline]
    pub fn bytes(&self) -> Bytes {
        self.string.bytes()
    }

    #[inline]
    pub fn lines(&self) -> Lines {
        self.string.lines()
    }
}

impl ToOwned for PascalStr {
    type Owned = PascalString;
    #[inline]
    fn to_owned(&self) -> Self::Owned {
        PascalString::from_str(self.as_str()).unwrap()
    }
}

pub type Chars<'a> = str::Chars<'a>;
pub type Bytes<'a> = str::Bytes<'a>;
pub type Lines<'a> = str::Lines<'a>;

pub struct InteriorNullError;
<commit_msg>Add PascalStr::is_empty/is_full<commit_after>use std::borrow::{Cow, ToOwned};
use std::ffi::{CStr, CString};
use std::str;
use ::utf8::PascalString;
use ::PASCAL_STRING_BUF_SIZE;

#[derive(Hash)]
pub struct PascalStr {
    string: str
}

impl PascalStr {
    #[inline]
    pub fn as_ptr(&self) -> *const u8 {
        (&self.string).as_ptr()
    }

    #[inline]
    pub fn as_mut_ptr(&mut self) -> *mut u8 {
        &mut self.string as *mut str as *mut u8
    }

    #[inline]
    pub fn as_str(&self) -> &str {
        &self.string
    }

    #[inline]
    pub fn as_mut_str(&mut self) -> &mut str {
        &mut self.string
    }

    #[inline]
    pub fn as_bytes(&self) -> &[u8] {
        self.string.as_bytes()
    }

    pub fn as_cstr(&self) -> Result<Cow<CStr>, InteriorNullError> {
        unimplemented!()
    }

    #[inline]
    pub fn len(&self) -> usize {
        self.string.len()
    }

    #[inline]
    pub fn is_empty(&self) -> bool {
        self.string.is_empty()
    }

    #[inline]
    pub fn is_full(&self) -> bool {
        self.len() == PASCAL_STRING_BUF_SIZE
    }

    #[inline]
    pub fn chars(&self) -> Chars {
        self.string.chars()
    }

    #[inline]
    pub fn bytes(&self) -> Bytes {
        self.string.bytes()
    }

    #[inline]
    pub fn lines(&self) -> Lines {
        self.string.lines()
    }
}

impl ToOwned for PascalStr {
    type Owned = PascalString;
    #[inline]
    fn to_owned(&self) -> Self::Owned {
        PascalString::from_str(self.as_str()).unwrap()
    }
}

pub type Chars<'a> = str::Chars<'a>;
pub type Bytes<'a> = str::Bytes<'a>;
pub type Lines<'a> = str::Lines<'a>;

pub struct InteriorNullError;
<|endoftext|>


<commit_before><commit_msg>No need to use long paths<commit_after><|endoftext|>


<commit_before>extern crate secret_service;

use secret_service::SecretService;

fn main() {
    let ss = SecretService::new().unwrap();
    println!("{:?}", ss.test_message());
}
<commit_msg>Script to clear collection<commit_after>extern crate secret_service;

use secret_service::SecretService;

fn main() {
    let ss = SecretService::new().unwrap();
    let collection = ss.get_default_collection().unwrap();
    let items = collection.get_all_items().unwrap();
    let items_count = items.len();
    println!("Count before: {:?}", items.len());
    if items_count > 0 {
        for item in items {
            item.delete();
        }
    }
    let items = collection.get_all_items().unwrap();
    println!("Count after: {:?}", items.len());
}
<|endoftext|>


<commit_before>
extern crate mprovision;
extern crate docopt;
#[macro_use(version)]
extern crate version;

use std::io::{self, Write};
use std::process;
use docopt::Docopt;

const USAGE: &'static str = "
mprovision
A tool that helps iOS developers to manage mobileprovision files.

Usage:
  mprovision search <text> [<directory>]
  mprovision remove <uuid> [<directory>]
  mprovision (-h | --help)
  mprovision --version

Options:
  -h --help     Show this help message.
  --version     Show version.
";

fn main() {
    let args = Docopt::new(USAGE)
        .and_then(|d| d.options_first(true)
                       .version(Some(format!("mprovision {}", version!())))
                       .parse())
        .unwrap_or_else(|e| e.exit());

    if let Some(cmd) = Command::from_args(&args) {
        let result = match cmd {
            Command::Search => search(&args),
            Command::Remove => remove(&args),
        };
        match result {
            Ok(_) => process::exit(0),
            Err(e) => {
                writeln!(&mut io::stderr(), "{}", e).unwrap();
                process::exit(1);
            }
        }
    }
}

enum Command {
    Search,
    Remove,
}

impl Command {
    fn from_args(args: &::docopt::ArgvMap) -> Option<Command> {
        if args.get_bool("search") {
            Some(Command::Search)
        } else if args.get_bool("remove") {
            Some(Command::Remove)
        } else {
            None
        }
    }
}

fn search(args: &::docopt::ArgvMap) -> Result<(), String> {
    let text = args.get_str("<text>");
    if text.is_empty() {
        return Err("<text> should not be empty.".to_owned());
    }
    let dir_name = args.get_str("<directory>");
    let dir_name = if dir_name.is_empty() { None } else { Some(dir_name) };

    let result = mprovision::with_path(dir_name, |path| {
        mprovision::search(path, text)
    });
    match result {
        Ok(info) => {
            if info.profiles.len() == 0 {
                println!("Nothing found for '{}'", text);
            } else {
                println!("Found {} of {}:\n", info.profiles.len(), info.total);
                for profile in &info.profiles {
                    println!("{}\n", profile.description());
                }
            }
            Ok(())
        },
        Err(e) => Err(e.to_string()),
    }
}

fn remove(args: &::docopt::ArgvMap) -> Result<(), String> {
    let uuid = args.get_str("<uuid>");
    if uuid.is_empty() {
        return Err("<uuid> should not be empty.".to_owned());
    }
    let dir_name = args.get_str("<directory>");
    let dir_name = if dir_name.is_empty() { None } else { Some(dir_name) };

    mprovision::with_path(dir_name, |path| mprovision::remove(path, uuid))
        .and_then(|_| Ok(println!("Profile '{}' was removed.", uuid)))
        .map_err(|e| e.to_string())
}
<commit_msg>Refactor error handling in the search command.<commit_after>
extern crate mprovision;
extern crate docopt;
#[macro_use(version)]
extern crate version;

use std::io::{self, Write};
use std::process;
use docopt::Docopt;

const USAGE: &'static str = "
mprovision
A tool that helps iOS developers to manage mobileprovision files.

Usage:
  mprovision search <text> [<directory>]
  mprovision remove <uuid> [<directory>]
  mprovision (-h | --help)
  mprovision --version

Options:
  -h --help     Show this help message.
  --version     Show version.
";

fn main() {
    let args = Docopt::new(USAGE)
        .and_then(|d| d.options_first(true)
                       .version(Some(format!("mprovision {}", version!())))
                       .parse())
        .unwrap_or_else(|e| e.exit());

    if let Some(cmd) = Command::from_args(&args) {
        let result = match cmd {
            Command::Search => search(&args),
            Command::Remove => remove(&args),
        };
        match result {
            Ok(_) => process::exit(0),
            Err(e) => {
                writeln!(&mut io::stderr(), "{}", e).unwrap();
                process::exit(1);
            }
        }
    }
}

enum Command {
    Search,
    Remove,
}

impl Command {
    fn from_args(args: &::docopt::ArgvMap) -> Option<Command> {
        if args.get_bool("search") {
            Some(Command::Search)
        } else if args.get_bool("remove") {
            Some(Command::Remove)
        } else {
            None
        }
    }
}

fn search(args: &::docopt::ArgvMap) -> Result<(), String> {
    let text = args.get_str("<text>");
    if text.is_empty() {
        return Err("<text> should not be empty.".to_owned());
    }
    let dir_name = args.get_str("<directory>");
    let dir_name = if dir_name.is_empty() { None } else { Some(dir_name) };

    mprovision::with_path(dir_name, |path| mprovision::search(path, text))
        .and_then(|info| {
            if info.profiles.len() == 0 {
                println!("Nothing found for '{}'", text);
            } else {
                println!("Found {} of {}:\n", info.profiles.len(), info.total);
                for profile in &info.profiles {
                    println!("{}\n", profile.description());
                }
            }
            Ok(())
        })
        .map_err(|e| e.to_string())
}

fn remove(args: &::docopt::ArgvMap) -> Result<(), String> {
    let uuid = args.get_str("<uuid>");
    if uuid.is_empty() {
        return Err("<uuid> should not be empty.".to_owned());
    }
    let dir_name = args.get_str("<directory>");
    let dir_name = if dir_name.is_empty() { None } else { Some(dir_name) };

    mprovision::with_path(dir_name, |path| mprovision::remove(path, uuid))
        .and_then(|_| Ok(println!("Profile '{}' was removed.", uuid)))
        .map_err(|e| e.to_string())
}
<|endoftext|>


<commit_before>#![cfg_attr(all(test, feature = "nightly"), feature(test))] // we only need test feature when testing

#[macro_use] extern crate log;

extern crate syntex_syntax;
extern crate toml;
extern crate env_logger;

extern crate racer;

#[cfg(not(test))]
use racer::core;
#[cfg(not(test))]
use racer::util;
#[cfg(not(test))]
use racer::core::Match;
#[cfg(not(test))]
use racer::util::{getline, path_exists};
#[cfg(not(test))]
use racer::nameres::{do_file_search, do_external_search, PATH_SEP};
#[cfg(not(test))]
use racer::scopes;
#[cfg(not(test))]
use std::path::Path;

#[cfg(not(test))]
fn match_with_snippet_fn(m: Match) {
    let (linenum, charnum) = scopes::point_to_coords_from_file(&m.filepath, m.point).unwrap();
    if m.matchstr == "" {
        panic!("MATCHSTR is empty - waddup?");
    }

    let snippet = racer::snippets::snippet_for_match(&m);
    println!("MATCH {};{};{};{};{};{:?};{}", m.matchstr,
                                    snippet,
                                    linenum.to_string(),
                                    charnum.to_string(),
                                    m.filepath.to_str().unwrap(),
                                    m.mtype,
                                    m.contextstr
             );
}

#[cfg(not(test))]
fn match_fn(m: Match) {
    if let Some((linenum, charnum)) = scopes::point_to_coords_from_file(&m.filepath, m.point) {
        println!("MATCH {},{},{},{},{:?},{}", m.matchstr,
                                    linenum.to_string(),
                                    charnum.to_string(),
                                    m.filepath.to_str().unwrap(),
                                    m.mtype,
                                    m.contextstr
                 );
    } else {
        error!("Could not resolve file coords for match {:?}", m);
    }
}

#[cfg(not(test))]
fn complete(match_found: &Fn(Match), args: &[String]) {
    if args.len() < 1 {
        println!("Provide more arguments!");
        print_usage();
        std::process::exit(1);
    }
    match args[1].parse::<usize>() {
        Ok(linenum) => {
            // input: linenum, colnum, fname
            if args.len() < 4 {
                println!("Provide more arguments!");
                print_usage();
                std::process::exit(1);
            }
            let charnum = args[2].parse::<usize>().unwrap();
            let fname = &args[3];
            let substitute_file = Path::new(match args.len() > 4 {
                true => &args[4],
                false => fname
            });
            let fpath = Path::new(fname);
            let src = core::load_file(&substitute_file);
            let line = &*getline(&substitute_file, linenum);
            let (start, pos) = util::expand_ident(line, charnum);
            println!("PREFIX {},{},{}", start, pos, &line[start..pos]);

            let session = core::Session::from_path(&fpath, &substitute_file);
            let point = scopes::coords_to_point(&*src, linenum, charnum);
            for m in core::complete_from_file(&*src, &fpath, point, &session) {
                match_found(m);
            }
            println!("END");
        }
        Err(_) => {
            // input: a command line string passed in
            let arg = &args[1];
            let it = arg.split("::");
            let p: Vec<&str> = it.collect();

            for m in do_file_search(p[0], &Path::new(".")) {
                if p.len() == 1 {
                    match_found(m);
                } else {
                    for m in do_external_search(&p[1..], &m.filepath, m.point, core::SearchType::StartsWith, core::Namespace::BothNamespaces, &m.session) {
                        match_found(m);
                    }
                }
            }
        }
    }
}

#[cfg(not(test))]
fn prefix(args: &[String]) {
    if args.len() < 4 {
        println!("Provide more arguments!");
        print_usage();
        std::process::exit(1);
    }
    let linenum = args[1].parse::<usize>().unwrap();
    let charnum = args[2].parse::<usize>().unwrap();
    let fname = &args[3];

    // print the start, end, and the identifier prefix being matched
    let path = Path::new(fname);
    let line = &*getline(&path, linenum);
    let (start, pos) = util::expand_ident(line, charnum);
    println!("PREFIX {},{},{}", start, pos, &line[start..pos]);
}

#[cfg(not(test))]
fn find_definition(args: &[String]) {
    if args.len() < 4 {
        println!("Provide more arguments!");
        print_usage();
        std::process::exit(1);
    }
    let linenum = args[1].parse::<usize>().unwrap();
    let charnum = args[2].parse::<usize>().unwrap();
    let fname = &args[3];
    let substitute_file = Path::new(match args.len() > 4 {
        true => &args[4],
        false => fname
    });
    let fpath = Path::new(&fname);
    let session = core::Session::from_path(&fpath, &substitute_file);
    let src = core::load_file(&substitute_file);
    let pos = scopes::coords_to_point(&*src, linenum, charnum);

    core::find_definition(&*src, &fpath, pos, &session).map(match_fn);
    println!("END");
}

#[cfg(not(test))]
fn print_usage() {
    let program = std::env::args().next().unwrap().clone();
    println!("usage: {} complete linenum charnum fname [substitute_file]", program);
    println!("or:    {} find-definition linenum charnum fname [substitute_file]", program);
    println!("or:    {} complete fullyqualifiedname   (e.g. std::io::)", program);
    println!("or:    {} prefix linenum charnum fname", program);
    println!("or replace complete with complete-with-snippet for more detailed completions.");
    println!("or:    {} daemon     - to start a process that receives the above commands via stdin", program);
}

#[cfg(not(test))]
fn check_rust_src_env_var() {
    if let Ok(srcpaths) = std::env::var("RUST_SRC_PATH") {
        let v = srcpaths.split(PATH_SEP).collect::<Vec<_>>();
        if !v.is_empty() {
            let f = Path::new(v[0]);
            if !path_exists(f) {
                println!("racer can't find the directory pointed to by the RUST_SRC_PATH variable \"{}\". Try using an absolute fully qualified path and make sure it points to the src directory of a rust checkout - e.g. \"/home/foouser/src/rust/src\".", srcpaths);
                std::process::exit(1);
            } else if !path_exists(f.join("libstd")) {
                println!("Unable to find libstd under RUST_SRC_PATH. N.B. RUST_SRC_PATH variable needs to point to the *src* directory inside a rust checkout e.g. \"/home/foouser/src/rust/src\". Current value \"{}\"", srcpaths);
                std::process::exit(1);
            }
        }
    } else {
        println!("RUST_SRC_PATH environment variable must be set to point to the src directory of a rust checkout. E.g. \"/home/foouser/src/rust/src\"");
        std::process::exit(1);
    }
}

#[cfg(not(test))]
fn daemon() {
    use std::io;
    let mut input = String::new();
    while let Ok(n) = io::stdin().read_line(&mut input) {
        if n == 0 {
            break;
        }
        let args: Vec<String> = input.split(" ").map(|s| s.trim().to_string()).collect();
        run(&args);

        input.clear();
    }
}

#[cfg(not(test))]
fn main() {
    env_logger::init().unwrap();
    check_rust_src_env_var();

    let args: Vec<String> = std::env::args().collect();

    if args.len() == 1 {
        print_usage();
        std::process::exit(1);
    }

    let args = &args[1..];
    run(args);
}

#[cfg(not(test))]
fn run(args: &[String]) {
    let command  = &args[0];
    match &command[..] {
        "daemon" => daemon(),
        "prefix" => prefix(&args),
        "complete" => complete(&match_fn, &args),
        "complete-with-snippet" => complete(&match_with_snippet_fn, &args),
        "find-definition" => find_definition(&args),
        "help" => print_usage(),
        cmd => {
            println!("Sorry, I didn't understand command {}", cmd);
            print_usage();
            std::process::exit(1);
        }
    }
}
<commit_msg>set RUST_BACKTRACE in main to ensure we get stacktraces on panic<commit_after>#![cfg_attr(all(test, feature = "nightly"), feature(test))] // we only need test feature when testing

#[macro_use] extern crate log;

extern crate syntex_syntax;
extern crate toml;
extern crate env_logger;

extern crate racer;

#[cfg(not(test))]
use racer::core;
#[cfg(not(test))]
use racer::util;
#[cfg(not(test))]
use racer::core::Match;
#[cfg(not(test))]
use racer::util::{getline, path_exists};
#[cfg(not(test))]
use racer::nameres::{do_file_search, do_external_search, PATH_SEP};
#[cfg(not(test))]
use racer::scopes;
#[cfg(not(test))]
use std::path::Path;

#[cfg(not(test))]
fn match_with_snippet_fn(m: Match) {
    let (linenum, charnum) = scopes::point_to_coords_from_file(&m.filepath, m.point).unwrap();
    if m.matchstr == "" {
        panic!("MATCHSTR is empty - waddup?");
    }

    let snippet = racer::snippets::snippet_for_match(&m);
    println!("MATCH {};{};{};{};{};{:?};{}", m.matchstr,
                                    snippet,
                                    linenum.to_string(),
                                    charnum.to_string(),
                                    m.filepath.to_str().unwrap(),
                                    m.mtype,
                                    m.contextstr
             );
}

#[cfg(not(test))]
fn match_fn(m: Match) {
    if let Some((linenum, charnum)) = scopes::point_to_coords_from_file(&m.filepath, m.point) {
        println!("MATCH {},{},{},{},{:?},{}", m.matchstr,
                                    linenum.to_string(),
                                    charnum.to_string(),
                                    m.filepath.to_str().unwrap(),
                                    m.mtype,
                                    m.contextstr
                 );
    } else {
        error!("Could not resolve file coords for match {:?}", m);
    }
}

#[cfg(not(test))]
fn complete(match_found: &Fn(Match), args: &[String]) {
    if args.len() < 1 {
        println!("Provide more arguments!");
        print_usage();
        std::process::exit(1);
    }
    match args[1].parse::<usize>() {
        Ok(linenum) => {
            // input: linenum, colnum, fname
            if args.len() < 4 {
                println!("Provide more arguments!");
                print_usage();
                std::process::exit(1);
            }
            let charnum = args[2].parse::<usize>().unwrap();
            let fname = &args[3];
            let substitute_file = Path::new(match args.len() > 4 {
                true => &args[4],
                false => fname
            });
            let fpath = Path::new(fname);
            let src = core::load_file(&substitute_file);
            let line = &*getline(&substitute_file, linenum);
            let (start, pos) = util::expand_ident(line, charnum);
            println!("PREFIX {},{},{}", start, pos, &line[start..pos]);

            let session = core::Session::from_path(&fpath, &substitute_file);
            let point = scopes::coords_to_point(&*src, linenum, charnum);
            for m in core::complete_from_file(&*src, &fpath, point, &session) {
                match_found(m);
            }
            println!("END");
        }
        Err(_) => {
            // input: a command line string passed in
            let arg = &args[1];
            let it = arg.split("::");
            let p: Vec<&str> = it.collect();

            for m in do_file_search(p[0], &Path::new(".")) {
                if p.len() == 1 {
                    match_found(m);
                } else {
                    for m in do_external_search(&p[1..], &m.filepath, m.point, core::SearchType::StartsWith, core::Namespace::BothNamespaces, &m.session) {
                        match_found(m);
                    }
                }
            }
        }
    }
}

#[cfg(not(test))]
fn prefix(args: &[String]) {
    if args.len() < 4 {
        println!("Provide more arguments!");
        print_usage();
        std::process::exit(1);
    }
    let linenum = args[1].parse::<usize>().unwrap();
    let charnum = args[2].parse::<usize>().unwrap();
    let fname = &args[3];

    // print the start, end, and the identifier prefix being matched
    let path = Path::new(fname);
    let line = &*getline(&path, linenum);
    let (start, pos) = util::expand_ident(line, charnum);
    println!("PREFIX {},{},{}", start, pos, &line[start..pos]);
}

#[cfg(not(test))]
fn find_definition(args: &[String]) {
    if args.len() < 4 {
        println!("Provide more arguments!");
        print_usage();
        std::process::exit(1);
    }
    let linenum = args[1].parse::<usize>().unwrap();
    let charnum = args[2].parse::<usize>().unwrap();
    let fname = &args[3];
    let substitute_file = Path::new(match args.len() > 4 {
        true => &args[4],
        false => fname
    });
    let fpath = Path::new(&fname);
    let session = core::Session::from_path(&fpath, &substitute_file);
    let src = core::load_file(&substitute_file);
    let pos = scopes::coords_to_point(&*src, linenum, charnum);

    core::find_definition(&*src, &fpath, pos, &session).map(match_fn);
    println!("END");
}

#[cfg(not(test))]
fn print_usage() {
    let program = std::env::args().next().unwrap().clone();
    println!("usage: {} complete linenum charnum fname [substitute_file]", program);
    println!("or:    {} find-definition linenum charnum fname [substitute_file]", program);
    println!("or:    {} complete fullyqualifiedname   (e.g. std::io::)", program);
    println!("or:    {} prefix linenum charnum fname", program);
    println!("or replace complete with complete-with-snippet for more detailed completions.");
    println!("or:    {} daemon     - to start a process that receives the above commands via stdin", program);
}

#[cfg(not(test))]
fn check_rust_src_env_var() {
    if let Ok(srcpaths) = std::env::var("RUST_SRC_PATH") {
        let v = srcpaths.split(PATH_SEP).collect::<Vec<_>>();
        if !v.is_empty() {
            let f = Path::new(v[0]);
            if !path_exists(f) {
                println!("racer can't find the directory pointed to by the RUST_SRC_PATH variable \"{}\". Try using an absolute fully qualified path and make sure it points to the src directory of a rust checkout - e.g. \"/home/foouser/src/rust/src\".", srcpaths);
                std::process::exit(1);
            } else if !path_exists(f.join("libstd")) {
                println!("Unable to find libstd under RUST_SRC_PATH. N.B. RUST_SRC_PATH variable needs to point to the *src* directory inside a rust checkout e.g. \"/home/foouser/src/rust/src\". Current value \"{}\"", srcpaths);
                std::process::exit(1);
            }
        }
    } else {
        println!("RUST_SRC_PATH environment variable must be set to point to the src directory of a rust checkout. E.g. \"/home/foouser/src/rust/src\"");
        std::process::exit(1);
    }
}

#[cfg(not(test))]
fn daemon() {
    use std::io;
    let mut input = String::new();
    while let Ok(n) = io::stdin().read_line(&mut input) {
        if n == 0 {
            break;
        }
        let args: Vec<String> = input.split(" ").map(|s| s.trim().to_string()).collect();
        run(&args);

        input.clear();
    }
}

#[cfg(not(test))]
fn main() {
    // make sure we get a stack trace ifwe panic
    ::std::env::set_var("RUST_BACKTRACE","1");

    env_logger::init().unwrap();
    check_rust_src_env_var();

    let args: Vec<String> = std::env::args().collect();

    if args.len() == 1 {
        print_usage();
        std::process::exit(1);
    }

    let args = &args[1..];
    run(args);
}

#[cfg(not(test))]
fn run(args: &[String]) {
    let command  = &args[0];
    match &command[..] {
        "daemon" => daemon(),
        "prefix" => prefix(&args),
        "complete" => complete(&match_fn, &args),
        "complete-with-snippet" => complete(&match_with_snippet_fn, &args),
        "find-definition" => find_definition(&args),
        "help" => print_usage(),
        cmd => {
            println!("Sorry, I didn't understand command {}", cmd);
            print_usage();
            std::process::exit(1);
        }
    }
}
<|endoftext|>


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

//! Combines data chunks into larger blobs to be stored externally.

use backend::StoreBackend;
use capnp;
use errors;
use hash::Hash;
use hash::tree::HashRef;
use std::borrow::Cow;
use std::mem;
use std::sync::{Arc, Mutex, MutexGuard};
use std::thread;
use tags;
use util::FnBox;
use key;

mod chunk;
mod blob;
mod index;
#[cfg(test)]
pub mod tests;

#[cfg(all(test, feature = "benchmarks"))]
mod benchmarks;

pub use self::blob::Blob;
pub use self::chunk::{ChunkRef, Key, NodeType, LeafType, Packing};
pub use self::index::{BlobDesc, BlobIndex};

error_type! {
    #[derive(Debug)]
    pub enum BlobError {
        Message(Cow<'static, str>) {
            desc (e) &**e;
            from (s: &'static str) s.into();
            from (s: String) s.into();
        },
        CryptoError(errors::CryptoError) {
            cause;
        },
        DataSerialization(capnp::Error) {
            cause;
        }
    }
}

pub struct BlobStore<B>(Arc<Mutex<StoreInner<B>>>);

pub struct StoreInner<B> {
    backend: Arc<B>,
    blob_index: Arc<BlobIndex>,
    blob_desc: BlobDesc,
    blob_refs: Vec<(Box<FnBox<(), ()>>)>,
    blob: Blob,
}

impl<B> Drop for StoreInner<B> {
    fn drop(&mut self) {
        // Sanity check that we flushed this blob store before dropping it.
        assert_eq!(0, self.blob.upperbound_len());
    }
}

impl<B: StoreBackend> StoreInner<B> {
    fn new(index: Arc<BlobIndex>, backend: Arc<B>, max_blob_size: usize) -> StoreInner<B> {
        let mut bs = StoreInner {
            backend: backend,
            blob_index: index,
            blob_desc: Default::default(),
            blob_refs: Vec::new(),
            blob: Blob::new(max_blob_size),
        };
        bs.reserve_new_blob();
        bs
    }

    fn reserve_new_blob(&mut self) -> BlobDesc {
        mem::replace(&mut self.blob_desc, self.blob_index.reserve())
    }

    fn flush(&mut self) {
        let ct = match self.blob.to_ciphertext() {
            None => return,
            Some(ct) => ct,
        };

        // Replace blob id
        let old_blob_desc = self.reserve_new_blob();

        self.blob_index.in_air(&old_blob_desc);
        self.backend
            .store(&old_blob_desc.name[..], &ct)
            .expect("Store operation failed");
        self.blob_index.commit_done(&old_blob_desc);

        // Go through callbacks
        while let Some(callback) = self.blob_refs.pop() {
            callback.call(());
        }
    }

    fn store(&mut self,
             chunk: &[u8],
             hash: Hash,
             node: NodeType,
             leaf: LeafType,
             info: Option<&key::Info>,
             callback: Box<FnBox<(), ()>>)
             -> HashRef {
        if chunk.is_empty() {
            let href = HashRef {
                hash: hash,
                node: node,
                leaf: leaf,
                info: None,
                persistent_ref: ChunkRef {
                    blob_id: None,
                    blob_name: vec![0],
                    offset: 0,
                    length: 0,
                    packing: None,
                    key: None,
                },
            };
            thread::spawn(move || callback.call(()));
            return href;
        }

        let mut href = HashRef {
            hash: hash,
            node: node,
            leaf: leaf,
            info: info.cloned(),
            persistent_ref: ChunkRef {
                blob_id: Some(self.blob_desc.id),
                blob_name: self.blob_desc.name.clone(),
                packing: None,
                // updated by try_append:
                offset: 0,
                length: 0,
                key: None,
            },
        };

        if let Err(()) = self.blob.try_append(chunk, &mut href) {
            self.flush();

            href.persistent_ref.blob_name = self.blob_desc.name.clone();
            self.blob.try_append(chunk, &mut href).unwrap();
        }
        self.blob_refs.push(callback);

        // Info is internal to the blob only.
        href.info = None;

        // To avoid unnecessary blocking, we reply with the ID *before* possibly flushing.
        href
    }

    fn retrieve(&mut self, hash: &Hash, cref: &ChunkRef) -> Result<Option<Vec<u8>>, BlobError> {
        if cref.offset == 0 && cref.length == 0 {
            return Ok(Some(Vec::new()));
        }
        match self.backend.retrieve(&cref.blob_name[..]) {
            Ok(Some(blob)) => Ok(Some(Blob::read_chunk(&blob, hash, cref)?)),
            Ok(None) => Ok(None),
            Err(e) => Err(e.into()),
        }
    }

    fn retrieve_refs(&mut self, blob: BlobDesc) -> Result<Option<Vec<HashRef>>, BlobError> {
        match self.backend.retrieve(&blob.name[..])? {
            None => Ok(None),
            Some(ct) => {
                let hrefs = self.blob.refs_from_bytes(&ct)?;
                if hrefs.len() == 0 {
                    Ok(None)
                } else {
                    assert_eq!(&blob.name[..], &hrefs[0].persistent_ref.blob_name[..]);
                    Ok(Some(hrefs))
                }
            }
        }
    }

    fn recover(&mut self) -> Result<(), String> {
        self.backend.list()?.into_iter()
            .filter(|b| b.len() > 4)  // FIXME(jos): Remove when "root" is gone.
            .map(|b| self.blob_index.recover(b.into_vec())).last();
        Ok(())
    }

    fn tag(&mut self, chunk: ChunkRef, tag: tags::Tag) {
        self.blob_index.tag(&BlobDesc {
                                id: chunk.blob_id.unwrap_or(0),
                                name: chunk.blob_name,
                            },
                            tag);
    }

    fn tag_all(&mut self, tag: tags::Tag) {
        self.blob_index.tag_all(tag);
    }

    fn delete_by_tag(&mut self, tag: tags::Tag) -> Result<(), String> {
        let blobs = self.blob_index.list_by_tag(tag);
        for b in &blobs {
            self.backend.delete(&b.name)?;
        }
        self.blob_index.delete_by_tag(tag);
        Ok(())
    }
}

impl<B: StoreBackend> BlobStore<B> {
    pub fn new(index: Arc<BlobIndex>, backend: Arc<B>, max_blob_size: usize) -> BlobStore<B> {
        BlobStore(Arc::new(Mutex::new(StoreInner::new(index, backend, max_blob_size))))
    }

    fn lock(&self) -> MutexGuard<StoreInner<B>> {
        self.0.lock().expect("Blob store was poisoned")
    }

    /// Store a new data chunk into the current blob. The callback is triggered after the blob
    /// containing the chunk has been committed to persistent storage (it is then safe to use the
    /// `ChunkRef` as persistent reference).
    pub fn store(&self,
                 chunk: &[u8],
                 hash: Hash,
                 node: NodeType,
                 leaf: LeafType,
                 info: Option<&key::Info>,
                 callback: Box<FnBox<(), ()>>)
                 -> HashRef {
        let mut guard = self.lock();
        guard.store(chunk, hash, node, leaf, info, callback)
    }

    /// Retrieve the data chunk identified by `ChunkRef`.
    pub fn retrieve(&self, hash: &Hash, cref: &ChunkRef) -> Result<Option<Vec<u8>>, BlobError> {
        self.lock().retrieve(hash, cref)
    }

    /// Fetch a blob and recover the HashRefs for its contents.
    pub fn retrieve_refs(&self, blob: BlobDesc) -> Result<Option<Vec<HashRef>>, BlobError> {
        self.lock().retrieve_refs(blob)
    }

    /// Reinstall a blob recovered from external storage.
    pub fn recover(&self) -> Result<(), String> {
        self.lock().recover()
    }

    pub fn tag(&self, chunk: ChunkRef, tag: tags::Tag) {
        self.lock().tag(chunk, tag)
    }

    pub fn tag_all(&self, tag: tags::Tag) {
        self.lock().tag_all(tag)
    }

    pub fn delete_by_tag(&self, tag: tags::Tag) -> Result<(), String> {
        self.lock().delete_by_tag(tag)
    }

    pub fn list_by_tag(&self, tag: tags::Tag) -> Vec<BlobDesc> {
        self.lock().blob_index.list_by_tag(tag)
    }

    pub fn find(&self, name: &[u8]) -> Option<BlobDesc> {
        self.lock().blob_index.find(name)
    }

    /// Flush the current blob, independent of its size.
    pub fn flush(&self) {
        let mut guard = self.lock();
        guard.flush();
        guard.blob_index.flush();
    }
}
<commit_msg>Update blob_name after flush as it changes!<commit_after>// Copyright 2014 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Combines data chunks into larger blobs to be stored externally.

use backend::StoreBackend;
use capnp;
use errors;
use hash::Hash;
use hash::tree::HashRef;
use std::borrow::Cow;
use std::mem;
use std::sync::{Arc, Mutex, MutexGuard};
use std::thread;
use tags;
use util::FnBox;
use key;

mod chunk;
mod blob;
mod index;
#[cfg(test)]
pub mod tests;

#[cfg(all(test, feature = "benchmarks"))]
mod benchmarks;

pub use self::blob::Blob;
pub use self::chunk::{ChunkRef, Key, NodeType, LeafType, Packing};
pub use self::index::{BlobDesc, BlobIndex};

error_type! {
    #[derive(Debug)]
    pub enum BlobError {
        Message(Cow<'static, str>) {
            desc (e) &**e;
            from (s: &'static str) s.into();
            from (s: String) s.into();
        },
        CryptoError(errors::CryptoError) {
            cause;
        },
        DataSerialization(capnp::Error) {
            cause;
        }
    }
}

pub struct BlobStore<B>(Arc<Mutex<StoreInner<B>>>);

pub struct StoreInner<B> {
    backend: Arc<B>,
    blob_index: Arc<BlobIndex>,
    blob_desc: BlobDesc,
    blob_refs: Vec<(Box<FnBox<(), ()>>)>,
    blob: Blob,
}

impl<B> Drop for StoreInner<B> {
    fn drop(&mut self) {
        // Sanity check that we flushed this blob store before dropping it.
        assert_eq!(0, self.blob.upperbound_len());
    }
}

impl<B: StoreBackend> StoreInner<B> {
    fn new(index: Arc<BlobIndex>, backend: Arc<B>, max_blob_size: usize) -> StoreInner<B> {
        let mut bs = StoreInner {
            backend: backend,
            blob_index: index,
            blob_desc: Default::default(),
            blob_refs: Vec::new(),
            blob: Blob::new(max_blob_size),
        };
        bs.reserve_new_blob();
        bs
    }

    fn reserve_new_blob(&mut self) -> BlobDesc {
        mem::replace(&mut self.blob_desc, self.blob_index.reserve())
    }

    fn flush(&mut self) {
        let ct = match self.blob.to_ciphertext() {
            None => return,
            Some(ct) => ct,
        };

        // Replace blob id
        let old_blob_desc = self.reserve_new_blob();

        self.blob_index.in_air(&old_blob_desc);
        self.backend
            .store(&old_blob_desc.name[..], &ct)
            .expect("Store operation failed");
        self.blob_index.commit_done(&old_blob_desc);

        // Go through callbacks
        while let Some(callback) = self.blob_refs.pop() {
            callback.call(());
        }
    }

    fn store(&mut self,
             chunk: &[u8],
             hash: Hash,
             node: NodeType,
             leaf: LeafType,
             info: Option<&key::Info>,
             callback: Box<FnBox<(), ()>>)
             -> HashRef {
        let mut href = HashRef {
            hash: hash,
            node: node,
            leaf: leaf,
            info: info.cloned(),
            persistent_ref: ChunkRef {
                blob_id: Some(0),
                blob_name: vec![0],
                packing: None,
                // Updated by try_append.
                offset: 0,
                length: 0,
                key: None,
            },
        };

        if chunk.is_empty() {
            // We are not going to store an empty chunk, so commit it ASAP.
            thread::spawn(move || callback.call(()));
        } else {
            href.persistent_ref.blob_id = Some(self.blob_desc.id);
            href.persistent_ref.blob_name = self.blob_desc.name.clone();
            if let Err(()) = self.blob.try_append(chunk, &mut href) {
                self.flush();
                href.persistent_ref.blob_id = Some(self.blob_desc.id);
                href.persistent_ref.blob_name = self.blob_desc.name.clone();

                self.blob.try_append(chunk, &mut href).unwrap();
            }

            // Queue the callback; we will trigger it when the blob has been pushed.
            self.blob_refs.push(callback);
        }

        // Info is internal to the blob only.
        href.info = None;
        // To avoid unnecessary blocking, we reply with the ID *before* possibly flushing.
        href
    }

    fn retrieve(&mut self, hash: &Hash, cref: &ChunkRef) -> Result<Option<Vec<u8>>, BlobError> {
        if cref.offset == 0 && cref.length == 0 {
            return Ok(Some(Vec::new()));
        }
        match self.backend.retrieve(&cref.blob_name[..]) {
            Ok(Some(blob)) => Ok(Some(Blob::read_chunk(&blob, hash, cref)?)),
            Ok(None) => Ok(None),
            Err(e) => Err(e.into()),
        }
    }

    fn retrieve_refs(&mut self, blob: BlobDesc) -> Result<Option<Vec<HashRef>>, BlobError> {
        match self.backend.retrieve(&blob.name[..])? {
            None => Ok(None),
            Some(ct) => {
                let hrefs = self.blob.refs_from_bytes(&ct)?;
                if hrefs.len() == 0 {
                    Ok(None)
                } else {
                    assert_eq!(&blob.name[..], &hrefs[0].persistent_ref.blob_name[..]);
                    Ok(Some(hrefs))
                }
            }
        }
    }

    fn recover(&mut self) -> Result<(), String> {
        self.backend.list()?.into_iter()
            .filter(|b| b.len() > 4)  // FIXME(jos): Remove when "root" is gone.
            .map(|b| self.blob_index.recover(b.into_vec())).last();
        Ok(())
    }

    fn tag(&mut self, chunk: ChunkRef, tag: tags::Tag) {
        self.blob_index.tag(&BlobDesc {
                                id: chunk.blob_id.unwrap_or(0),
                                name: chunk.blob_name,
                            },
                            tag);
    }

    fn tag_all(&mut self, tag: tags::Tag) {
        self.blob_index.tag_all(tag);
    }

    fn delete_by_tag(&mut self, tag: tags::Tag) -> Result<(), String> {
        let blobs = self.blob_index.list_by_tag(tag);
        for b in &blobs {
            self.backend.delete(&b.name)?;
        }
        self.blob_index.delete_by_tag(tag);
        Ok(())
    }
}

impl<B: StoreBackend> BlobStore<B> {
    pub fn new(index: Arc<BlobIndex>, backend: Arc<B>, max_blob_size: usize) -> BlobStore<B> {
        BlobStore(Arc::new(Mutex::new(StoreInner::new(index, backend, max_blob_size))))
    }

    fn lock(&self) -> MutexGuard<StoreInner<B>> {
        self.0.lock().expect("Blob store was poisoned")
    }

    /// Store a new data chunk into the current blob. The callback is triggered after the blob
    /// containing the chunk has been committed to persistent storage (it is then safe to use the
    /// `ChunkRef` as persistent reference).
    pub fn store(&self,
                 chunk: &[u8],
                 hash: Hash,
                 node: NodeType,
                 leaf: LeafType,
                 info: Option<&key::Info>,
                 callback: Box<FnBox<(), ()>>)
                 -> HashRef {
        let mut guard = self.lock();
        guard.store(chunk, hash, node, leaf, info, callback)
    }

    /// Retrieve the data chunk identified by `ChunkRef`.
    pub fn retrieve(&self, hash: &Hash, cref: &ChunkRef) -> Result<Option<Vec<u8>>, BlobError> {
        self.lock().retrieve(hash, cref)
    }

    /// Fetch a blob and recover the HashRefs for its contents.
    pub fn retrieve_refs(&self, blob: BlobDesc) -> Result<Option<Vec<HashRef>>, BlobError> {
        self.lock().retrieve_refs(blob)
    }

    /// Reinstall a blob recovered from external storage.
    pub fn recover(&self) -> Result<(), String> {
        self.lock().recover()
    }

    pub fn tag(&self, chunk: ChunkRef, tag: tags::Tag) {
        self.lock().tag(chunk, tag)
    }

    pub fn tag_all(&self, tag: tags::Tag) {
        self.lock().tag_all(tag)
    }

    pub fn delete_by_tag(&self, tag: tags::Tag) -> Result<(), String> {
        self.lock().delete_by_tag(tag)
    }

    pub fn list_by_tag(&self, tag: tags::Tag) -> Vec<BlobDesc> {
        self.lock().blob_index.list_by_tag(tag)
    }

    pub fn find(&self, name: &[u8]) -> Option<BlobDesc> {
        self.lock().blob_index.find(name)
    }

    /// Flush the current blob, independent of its size.
    pub fn flush(&self) {
        let mut guard = self.lock();
        guard.flush();
        guard.blob_index.flush();
    }
}
<|endoftext|>