<commit_before>use std::collections::VecDeque;
use std::sync::Arc;

use CreationError;
use CursorState;
use Event;
use MouseCursor;
use WindowAttributes;
use libc;

use api::wayland;
use api::x11;
use api::x11::XConnection;
use api::x11::XError;
use api::x11::XNotSupported;

#[derive(Clone, Default)]
pub struct PlatformSpecificWindowBuilderAttributes;

enum Backend {
    X(Arc<XConnection>),
    Wayland(Arc<wayland::WaylandContext>),
    Error(XNotSupported),
}

lazy_static!(
    static ref BACKEND: Backend = {
        // Wayland backend is not production-ready yet so we disable it
        if let Some(ctxt) = wayland::WaylandContext::init() {
            Backend::Wayland(Arc::new(ctxt))
        } else {
            match XConnection::new(Some(x_error_callback)) {
                Ok(x) => Backend::X(Arc::new(x)),
                Err(e) => Backend::Error(e),
            }
        }
    };
);

pub enum Window {
    #[doc(hidden)]
    X(x11::Window),
    #[doc(hidden)]
    Wayland(wayland::Window)
}

#[derive(Clone)]
pub enum WindowProxy {
    #[doc(hidden)]
    X(x11::WindowProxy),
    #[doc(hidden)]
    Wayland(wayland::WindowProxy)
}

impl WindowProxy {
    #[inline]
    pub fn wakeup_event_loop(&self) {
        match self {
            &WindowProxy::X(ref wp) => wp.wakeup_event_loop(),
            &WindowProxy::Wayland(ref wp) => wp.wakeup_event_loop()
        }
    }
}

#[derive(Clone)]
pub enum MonitorId {
    #[doc(hidden)]
    X(x11::MonitorId),
    #[doc(hidden)]
    Wayland(wayland::MonitorId),
    #[doc(hidden)]
    None,
}

#[inline]
pub fn get_available_monitors() -> VecDeque<MonitorId> {
    match *BACKEND {
        Backend::Wayland(ref ctxt) => wayland::get_available_monitors(ctxt)
                                .into_iter()
                                .map(MonitorId::Wayland)
                                .collect(),
        Backend::X(ref connec) => x11::get_available_monitors(connec)
                                    .into_iter()
                                    .map(MonitorId::X)
                                    .collect(),
        Backend::Error(_) => { let mut d = VecDeque::new(); d.push_back(MonitorId::None); d},
    }
}

#[inline]
pub fn get_primary_monitor() -> MonitorId {
    match *BACKEND {
        Backend::Wayland(ref ctxt) => MonitorId::Wayland(wayland::get_primary_monitor(ctxt)),
        Backend::X(ref connec) => MonitorId::X(x11::get_primary_monitor(connec)),
        Backend::Error(_) => MonitorId::None,
    }
}

impl MonitorId {
    #[inline]
    pub fn get_name(&self) -> Option<String> {
        match self {
            &MonitorId::X(ref m) => m.get_name(),
            &MonitorId::Wayland(ref m) => m.get_name(),
            &MonitorId::None => None,
        }
    }

    #[inline]
    pub fn get_native_identifier(&self) -> ::native_monitor::NativeMonitorId {
        match self {
            &MonitorId::X(ref m) => m.get_native_identifier(),
            &MonitorId::Wayland(ref m) => m.get_native_identifier(),
            &MonitorId::None => unimplemented!()        // FIXME:
        }
    }

    #[inline]
    pub fn get_dimensions(&self) -> (u32, u32) {
        match self {
            &MonitorId::X(ref m) => m.get_dimensions(),
            &MonitorId::Wayland(ref m) => m.get_dimensions(),
            &MonitorId::None => (800, 600),     // FIXME:
        }
    }
}

pub enum PollEventsIterator<'a> {
    #[doc(hidden)]
    X(x11::PollEventsIterator<'a>),
    #[doc(hidden)]
    Wayland(wayland::PollEventsIterator<'a>)
}

impl<'a> Iterator for PollEventsIterator<'a> {
    type Item = Event;

    #[inline]
    fn next(&mut self) -> Option<Event> {
        match self {
            &mut PollEventsIterator::X(ref mut it) => it.next(),
            &mut PollEventsIterator::Wayland(ref mut it) => it.next()
        }
    }
}

pub enum WaitEventsIterator<'a> {
    #[doc(hidden)]
    X(x11::WaitEventsIterator<'a>),
    #[doc(hidden)]
    Wayland(wayland::WaitEventsIterator<'a>)
}

impl<'a> Iterator for WaitEventsIterator<'a> {
    type Item = Event;

    #[inline]
    fn next(&mut self) -> Option<Event> {
        match self {
            &mut WaitEventsIterator::X(ref mut it) => it.next(),
            &mut WaitEventsIterator::Wayland(ref mut it) => it.next()
        }
    }
}

impl Window {
    #[inline]
    pub fn new(window: &WindowAttributes, _: &PlatformSpecificWindowBuilderAttributes)
               -> Result<Window, CreationError>
    {
        match *BACKEND {
            Backend::Wayland(ref ctxt) => {
                wayland::Window::new(ctxt.clone(), window).map(Window::Wayland)
            },

            Backend::X(ref connec) => {
                x11::Window::new(connec, window).map(Window::X)
            },

            Backend::Error(ref error) => {
                panic!()        // FIXME: supposed to return an error
                //Err(CreationError::NoBackendAvailable(Box::new(error.clone())))
            }
        }
    }

    #[inline]
    pub fn set_title(&self, title: &str) {
        match self {
            &Window::X(ref w) => w.set_title(title),
            &Window::Wayland(ref w) => w.set_title(title)
        }
    }

    #[inline]
    pub fn show(&self) {
        match self {
            &Window::X(ref w) => w.show(),
            &Window::Wayland(ref w) => w.show()
        }
    }

    #[inline]
    pub fn hide(&self) {
        match self {
            &Window::X(ref w) => w.hide(),
            &Window::Wayland(ref w) => w.hide()
        }
    }

    #[inline]
    pub fn get_position(&self) -> Option<(i32, i32)> {
        match self {
            &Window::X(ref w) => w.get_position(),
            &Window::Wayland(ref w) => w.get_position()
        }
    }

    #[inline]
    pub fn set_position(&self, x: i32, y: i32) {
        match self {
            &Window::X(ref w) => w.set_position(x, y),
            &Window::Wayland(ref w) => w.set_position(x, y)
        }
    }

    #[inline]
    pub fn get_inner_size(&self) -> Option<(u32, u32)> {
        match self {
            &Window::X(ref w) => w.get_inner_size(),
            &Window::Wayland(ref w) => w.get_inner_size()
        }
    }

    #[inline]
    pub fn get_outer_size(&self) -> Option<(u32, u32)> {
        match self {
            &Window::X(ref w) => w.get_outer_size(),
            &Window::Wayland(ref w) => w.get_outer_size()
        }
    }

    #[inline]
    pub fn set_inner_size(&self, x: u32, y: u32) {
        match self {
            &Window::X(ref w) => w.set_inner_size(x, y),
            &Window::Wayland(ref w) => w.set_inner_size(x, y)
        }
    }

    #[inline]
    pub fn create_window_proxy(&self) -> WindowProxy {
        match self {
            &Window::X(ref w) => WindowProxy::X(w.create_window_proxy()),
            &Window::Wayland(ref w) => WindowProxy::Wayland(w.create_window_proxy())
        }
    }

    #[inline]
    pub fn poll_events(&self) -> PollEventsIterator {
        match self {
            &Window::X(ref w) => PollEventsIterator::X(w.poll_events()),
            &Window::Wayland(ref w) => PollEventsIterator::Wayland(w.poll_events())
        }
    }

    #[inline]
    pub fn wait_events(&self) -> WaitEventsIterator {
        match self {
            &Window::X(ref w) => WaitEventsIterator::X(w.wait_events()),
            &Window::Wayland(ref w) => WaitEventsIterator::Wayland(w.wait_events())
        }
    }

    #[inline]
    pub fn set_window_resize_callback(&mut self, callback: Option<fn(u32, u32)>) {
        match self {
            &mut Window::X(ref mut w) => w.set_window_resize_callback(callback),
            &mut Window::Wayland(ref mut w) => w.set_window_resize_callback(callback)
        }
    }

    #[inline]
    pub fn set_cursor(&self, cursor: MouseCursor) {
        match self {
            &Window::X(ref w) => w.set_cursor(cursor),
            &Window::Wayland(ref w) => w.set_cursor(cursor)
        }
    }

    #[inline]
    pub fn set_cursor_state(&self, state: CursorState) -> Result<(), String> {
        match self {
            &Window::X(ref w) => w.set_cursor_state(state),
            &Window::Wayland(ref w) => w.set_cursor_state(state)
        }
    }

    #[inline]
    pub fn hidpi_factor(&self) -> f32 {
       match self {
            &Window::X(ref w) => w.hidpi_factor(),
            &Window::Wayland(ref w) => w.hidpi_factor()
        }
    }

    #[inline]
    pub fn set_cursor_position(&self, x: i32, y: i32) -> Result<(), ()> {
        match self {
            &Window::X(ref w) => w.set_cursor_position(x, y),
            &Window::Wayland(ref w) => w.set_cursor_position(x, y)
        }
    }

    #[inline]
    pub fn platform_display(&self) -> *mut libc::c_void {
        use wayland_client::Proxy;
        match self {
            &Window::X(ref w) => w.platform_display(),
            &Window::Wayland(ref w) => w.get_display().ptr() as *mut _
        }
    }

    #[inline]
    pub fn platform_window(&self) -> *mut libc::c_void {
        use wayland_client::Proxy;
        match self {
            &Window::X(ref w) => w.platform_window(),
            &Window::Wayland(ref w) => w.get_surface().ptr() as *mut _
        }
    }
}

unsafe extern "C" fn x_error_callback(dpy: *mut x11::ffi::Display, event: *mut x11::ffi::XErrorEvent)
                                      -> libc::c_int
{
    use std::ffi::CStr;

    if let Backend::X(ref x) = *BACKEND {
        let mut buff: Vec<u8> = Vec::with_capacity(1024);
        (x.xlib.XGetErrorText)(dpy, (*event).error_code as i32, buff.as_mut_ptr() as *mut libc::c_char, buff.capacity() as i32);
        let description = CStr::from_ptr(buff.as_mut_ptr() as *const libc::c_char).to_string_lossy();

        let error = XError {
            description: description.into_owned(),
            error_code: (*event).error_code,
            request_code: (*event).request_code,
            minor_code: (*event).minor_code,
        };

        *x.latest_error.lock().unwrap() = Some(error);
    }

    0
}
<commit_msg>linux: remove outdate comment<commit_after>use std::collections::VecDeque;
use std::sync::Arc;

use CreationError;
use CursorState;
use Event;
use MouseCursor;
use WindowAttributes;
use libc;

use api::wayland;
use api::x11;
use api::x11::XConnection;
use api::x11::XError;
use api::x11::XNotSupported;

#[derive(Clone, Default)]
pub struct PlatformSpecificWindowBuilderAttributes;

enum Backend {
    X(Arc<XConnection>),
    Wayland(Arc<wayland::WaylandContext>),
    Error(XNotSupported),
}

lazy_static!(
    static ref BACKEND: Backend = {
        if let Some(ctxt) = wayland::WaylandContext::init() {
            Backend::Wayland(Arc::new(ctxt))
        } else {
            match XConnection::new(Some(x_error_callback)) {
                Ok(x) => Backend::X(Arc::new(x)),
                Err(e) => Backend::Error(e),
            }
        }
    };
);

pub enum Window {
    #[doc(hidden)]
    X(x11::Window),
    #[doc(hidden)]
    Wayland(wayland::Window)
}

#[derive(Clone)]
pub enum WindowProxy {
    #[doc(hidden)]
    X(x11::WindowProxy),
    #[doc(hidden)]
    Wayland(wayland::WindowProxy)
}

impl WindowProxy {
    #[inline]
    pub fn wakeup_event_loop(&self) {
        match self {
            &WindowProxy::X(ref wp) => wp.wakeup_event_loop(),
            &WindowProxy::Wayland(ref wp) => wp.wakeup_event_loop()
        }
    }
}

#[derive(Clone)]
pub enum MonitorId {
    #[doc(hidden)]
    X(x11::MonitorId),
    #[doc(hidden)]
    Wayland(wayland::MonitorId),
    #[doc(hidden)]
    None,
}

#[inline]
pub fn get_available_monitors() -> VecDeque<MonitorId> {
    match *BACKEND {
        Backend::Wayland(ref ctxt) => wayland::get_available_monitors(ctxt)
                                .into_iter()
                                .map(MonitorId::Wayland)
                                .collect(),
        Backend::X(ref connec) => x11::get_available_monitors(connec)
                                    .into_iter()
                                    .map(MonitorId::X)
                                    .collect(),
        Backend::Error(_) => { let mut d = VecDeque::new(); d.push_back(MonitorId::None); d},
    }
}

#[inline]
pub fn get_primary_monitor() -> MonitorId {
    match *BACKEND {
        Backend::Wayland(ref ctxt) => MonitorId::Wayland(wayland::get_primary_monitor(ctxt)),
        Backend::X(ref connec) => MonitorId::X(x11::get_primary_monitor(connec)),
        Backend::Error(_) => MonitorId::None,
    }
}

impl MonitorId {
    #[inline]
    pub fn get_name(&self) -> Option<String> {
        match self {
            &MonitorId::X(ref m) => m.get_name(),
            &MonitorId::Wayland(ref m) => m.get_name(),
            &MonitorId::None => None,
        }
    }

    #[inline]
    pub fn get_native_identifier(&self) -> ::native_monitor::NativeMonitorId {
        match self {
            &MonitorId::X(ref m) => m.get_native_identifier(),
            &MonitorId::Wayland(ref m) => m.get_native_identifier(),
            &MonitorId::None => unimplemented!()        // FIXME:
        }
    }

    #[inline]
    pub fn get_dimensions(&self) -> (u32, u32) {
        match self {
            &MonitorId::X(ref m) => m.get_dimensions(),
            &MonitorId::Wayland(ref m) => m.get_dimensions(),
            &MonitorId::None => (800, 600),     // FIXME:
        }
    }
}

pub enum PollEventsIterator<'a> {
    #[doc(hidden)]
    X(x11::PollEventsIterator<'a>),
    #[doc(hidden)]
    Wayland(wayland::PollEventsIterator<'a>)
}

impl<'a> Iterator for PollEventsIterator<'a> {
    type Item = Event;

    #[inline]
    fn next(&mut self) -> Option<Event> {
        match self {
            &mut PollEventsIterator::X(ref mut it) => it.next(),
            &mut PollEventsIterator::Wayland(ref mut it) => it.next()
        }
    }
}

pub enum WaitEventsIterator<'a> {
    #[doc(hidden)]
    X(x11::WaitEventsIterator<'a>),
    #[doc(hidden)]
    Wayland(wayland::WaitEventsIterator<'a>)
}

impl<'a> Iterator for WaitEventsIterator<'a> {
    type Item = Event;

    #[inline]
    fn next(&mut self) -> Option<Event> {
        match self {
            &mut WaitEventsIterator::X(ref mut it) => it.next(),
            &mut WaitEventsIterator::Wayland(ref mut it) => it.next()
        }
    }
}

impl Window {
    #[inline]
    pub fn new(window: &WindowAttributes, _: &PlatformSpecificWindowBuilderAttributes)
               -> Result<Window, CreationError>
    {
        match *BACKEND {
            Backend::Wayland(ref ctxt) => {
                wayland::Window::new(ctxt.clone(), window).map(Window::Wayland)
            },

            Backend::X(ref connec) => {
                x11::Window::new(connec, window).map(Window::X)
            },

            Backend::Error(ref error) => {
                panic!()        // FIXME: supposed to return an error
                //Err(CreationError::NoBackendAvailable(Box::new(error.clone())))
            }
        }
    }

    #[inline]
    pub fn set_title(&self, title: &str) {
        match self {
            &Window::X(ref w) => w.set_title(title),
            &Window::Wayland(ref w) => w.set_title(title)
        }
    }

    #[inline]
    pub fn show(&self) {
        match self {
            &Window::X(ref w) => w.show(),
            &Window::Wayland(ref w) => w.show()
        }
    }

    #[inline]
    pub fn hide(&self) {
        match self {
            &Window::X(ref w) => w.hide(),
            &Window::Wayland(ref w) => w.hide()
        }
    }

    #[inline]
    pub fn get_position(&self) -> Option<(i32, i32)> {
        match self {
            &Window::X(ref w) => w.get_position(),
            &Window::Wayland(ref w) => w.get_position()
        }
    }

    #[inline]
    pub fn set_position(&self, x: i32, y: i32) {
        match self {
            &Window::X(ref w) => w.set_position(x, y),
            &Window::Wayland(ref w) => w.set_position(x, y)
        }
    }

    #[inline]
    pub fn get_inner_size(&self) -> Option<(u32, u32)> {
        match self {
            &Window::X(ref w) => w.get_inner_size(),
            &Window::Wayland(ref w) => w.get_inner_size()
        }
    }

    #[inline]
    pub fn get_outer_size(&self) -> Option<(u32, u32)> {
        match self {
            &Window::X(ref w) => w.get_outer_size(),
            &Window::Wayland(ref w) => w.get_outer_size()
        }
    }

    #[inline]
    pub fn set_inner_size(&self, x: u32, y: u32) {
        match self {
            &Window::X(ref w) => w.set_inner_size(x, y),
            &Window::Wayland(ref w) => w.set_inner_size(x, y)
        }
    }

    #[inline]
    pub fn create_window_proxy(&self) -> WindowProxy {
        match self {
            &Window::X(ref w) => WindowProxy::X(w.create_window_proxy()),
            &Window::Wayland(ref w) => WindowProxy::Wayland(w.create_window_proxy())
        }
    }

    #[inline]
    pub fn poll_events(&self) -> PollEventsIterator {
        match self {
            &Window::X(ref w) => PollEventsIterator::X(w.poll_events()),
            &Window::Wayland(ref w) => PollEventsIterator::Wayland(w.poll_events())
        }
    }

    #[inline]
    pub fn wait_events(&self) -> WaitEventsIterator {
        match self {
            &Window::X(ref w) => WaitEventsIterator::X(w.wait_events()),
            &Window::Wayland(ref w) => WaitEventsIterator::Wayland(w.wait_events())
        }
    }

    #[inline]
    pub fn set_window_resize_callback(&mut self, callback: Option<fn(u32, u32)>) {
        match self {
            &mut Window::X(ref mut w) => w.set_window_resize_callback(callback),
            &mut Window::Wayland(ref mut w) => w.set_window_resize_callback(callback)
        }
    }

    #[inline]
    pub fn set_cursor(&self, cursor: MouseCursor) {
        match self {
            &Window::X(ref w) => w.set_cursor(cursor),
            &Window::Wayland(ref w) => w.set_cursor(cursor)
        }
    }

    #[inline]
    pub fn set_cursor_state(&self, state: CursorState) -> Result<(), String> {
        match self {
            &Window::X(ref w) => w.set_cursor_state(state),
            &Window::Wayland(ref w) => w.set_cursor_state(state)
        }
    }

    #[inline]
    pub fn hidpi_factor(&self) -> f32 {
       match self {
            &Window::X(ref w) => w.hidpi_factor(),
            &Window::Wayland(ref w) => w.hidpi_factor()
        }
    }

    #[inline]
    pub fn set_cursor_position(&self, x: i32, y: i32) -> Result<(), ()> {
        match self {
            &Window::X(ref w) => w.set_cursor_position(x, y),
            &Window::Wayland(ref w) => w.set_cursor_position(x, y)
        }
    }

    #[inline]
    pub fn platform_display(&self) -> *mut libc::c_void {
        use wayland_client::Proxy;
        match self {
            &Window::X(ref w) => w.platform_display(),
            &Window::Wayland(ref w) => w.get_display().ptr() as *mut _
        }
    }

    #[inline]
    pub fn platform_window(&self) -> *mut libc::c_void {
        use wayland_client::Proxy;
        match self {
            &Window::X(ref w) => w.platform_window(),
            &Window::Wayland(ref w) => w.get_surface().ptr() as *mut _
        }
    }
}

unsafe extern "C" fn x_error_callback(dpy: *mut x11::ffi::Display, event: *mut x11::ffi::XErrorEvent)
                                      -> libc::c_int
{
    use std::ffi::CStr;

    if let Backend::X(ref x) = *BACKEND {
        let mut buff: Vec<u8> = Vec::with_capacity(1024);
        (x.xlib.XGetErrorText)(dpy, (*event).error_code as i32, buff.as_mut_ptr() as *mut libc::c_char, buff.capacity() as i32);
        let description = CStr::from_ptr(buff.as_mut_ptr() as *const libc::c_char).to_string_lossy();

        let error = XError {
            description: description.into_owned(),
            error_code: (*event).error_code,
            request_code: (*event).request_code,
            minor_code: (*event).minor_code,
        };

        *x.latest_error.lock().unwrap() = Some(error);
    }

    0
}
<|endoftext|>


<commit_before>use syntax::ast::{self, AstNode};

use crate::{AssistContext, AssistId, AssistKind, Assists};

// Assist: convert_two_arm_bool_match_to_matches_macro
//
// Convert 2-arm match that evaluates to a boolean into the equivalent matches! invocation.
//
// ```
// fn main() {
//     match scrutinee$0 {
//         Some(val) if val.cond() => true,
//         _ => false,
//     }
// }
// ```
// ->
// ```
// fn main() {
//     matches!(scrutinee, Some(val) if val.cond())
// }
// ```
pub(crate) fn convert_two_arm_bool_match_to_matches_macro(
    acc: &mut Assists,
    ctx: &AssistContext<'_>,
) -> Option<()> {
    let match_expr = ctx.find_node_at_offset::<ast::MatchExpr>()?;
    let match_arm_list = match_expr.match_arm_list()?;
    let mut arms = match_arm_list.arms();
    let first_arm = arms.next()?;
    let second_arm = arms.next()?;
    if arms.next().is_some() {
        cov_mark::hit!(non_two_arm_match);
        return None;
    }
    let first_arm_expr = first_arm.expr();

    let invert_matches = if is_bool_literal_expr(&first_arm_expr, true) {
        false
    } else if is_bool_literal_expr(&first_arm_expr, false) {
        true
    } else {
        cov_mark::hit!(non_bool_literal_match);
        return None;
    };

    let target_range = ctx.sema.original_range(match_expr.syntax()).range;
    let expr = match_expr.expr()?;

    acc.add(
        AssistId("convert_two_arm_bool_match_to_matches_macro", AssistKind::RefactorRewrite),
        "Convert to matches!",
        target_range,
        |builder| {
            let mut arm_str = String::new();
            if let Some(ref pat) = first_arm.pat() {
                arm_str += &pat.to_string();
            }
            if let Some(ref guard) = first_arm.guard() {
                arm_str += &format!(" {}", &guard.to_string());
            }
            if invert_matches {
                builder.replace(target_range, format!("!matches!({}, {})", expr, arm_str));
            } else {
                builder.replace(target_range, format!("matches!({}, {})", expr, arm_str));
            }
        },
    )
}

fn is_bool_literal_expr(expr: &Option<ast::Expr>, expect_bool: bool) -> bool {
    if let Some(ast::Expr::Literal(lit)) = expr {
        if let ast::LiteralKind::Bool(b) = lit.kind() {
            return b == expect_bool;
        }
    }

    return false;
}

#[cfg(test)]
mod tests {
    use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};

    use super::convert_two_arm_bool_match_to_matches_macro;

    #[test]
    fn not_applicable_outside_of_range_left() {
        check_assist_not_applicable(
            convert_two_arm_bool_match_to_matches_macro,
            r#"
fn foo(a: Option<u32>) -> bool {
    $0 match a {
        Some(_val) => true,
        _ => false
    }
}
        "#,
        );
    }

    #[test]
    fn not_applicable_non_two_arm_match() {
        cov_mark::check!(non_two_arm_match);
        check_assist_not_applicable(
            convert_two_arm_bool_match_to_matches_macro,
            r#"
fn foo(a: Option<u32>) -> bool {
    match a$0 {
        Some(3) => true,
        Some(4) => true,
        _ => false
    }
}
        "#,
        );
    }

    #[test]
    fn not_applicable_non_bool_literal_arms() {
        cov_mark::check!(non_bool_literal_match);
        check_assist_not_applicable(
            convert_two_arm_bool_match_to_matches_macro,
            r#"
fn foo(a: Option<u32>) -> bool {
    match a$0 {
        Some(val) => val == 3,
        _ => false
    }
}
        "#,
        );
    }

    #[test]
    fn convert_simple_case() {
        check_assist(
            convert_two_arm_bool_match_to_matches_macro,
            r#"
fn foo(a: Option<u32>) -> bool {
    match a$0 {
        Some(_val) => true,
        _ => false
    }
}
"#,
            r#"
fn foo(a: Option<u32>) -> bool {
    matches!(a, Some(_val))
}
"#,
        );
    }

    #[test]
    fn convert_simple_invert_case() {
        check_assist(
            convert_two_arm_bool_match_to_matches_macro,
            r#"
fn foo(a: Option<u32>) -> bool {
    match a$0 {
        Some(_val) => false,
        _ => true
    }
}
"#,
            r#"
fn foo(a: Option<u32>) -> bool {
    !matches!(a, Some(_val))
}
"#,
        );
    }

    #[test]
    fn convert_with_guard_case() {
        check_assist(
            convert_two_arm_bool_match_to_matches_macro,
            r#"
fn foo(a: Option<u32>) -> bool {
    match a$0 {
        Some(val) if val > 3 => true,
        _ => false
    }
}
"#,
            r#"
fn foo(a: Option<u32>) -> bool {
    matches!(a, Some(val) if val > 3)
}
"#,
        );
    }

    #[test]
    fn convert_enum_match_cases() {
        check_assist(
            convert_two_arm_bool_match_to_matches_macro,
            r#"
enum X { A, B }

fn foo(a: X) -> bool {
    match a$0 {
        X::A => true,
        _ => false
    }
}
"#,
            r#"
enum X { A, B }

fn foo(a: X) -> bool {
    matches!(a, X::A)
}
"#,
        );
    }

    #[test]
    fn convert_target_simple() {
        check_assist_target(
            convert_two_arm_bool_match_to_matches_macro,
            r#"
fn foo(a: Option<u32>) -> bool {
    match a$0 {
        Some(val) => true,
        _ => false
    }
}
"#,
            r#"match a {
        Some(val) => true,
        _ => false
    }"#,
        );
    }

    #[test]
    fn convert_target_complex() {
        check_assist_target(
            convert_two_arm_bool_match_to_matches_macro,
            r#"
enum E { X, Y }

fn main() {
    match E::X$0 {
        E::X => true,
        _ => false,
    }
}
"#,
            "match E::X {
        E::X => true,
        _ => false,
    }",
        );
    }
}
<commit_msg>feature: Check if first_arm bool and second_arm bool is inverted or not.<commit_after>use syntax::ast::{self, AstNode};

use crate::{AssistContext, AssistId, AssistKind, Assists};

// Assist: convert_two_arm_bool_match_to_matches_macro
//
// Convert 2-arm match that evaluates to a boolean into the equivalent matches! invocation.
//
// ```
// fn main() {
//     match scrutinee$0 {
//         Some(val) if val.cond() => true,
//         _ => false,
//     }
// }
// ```
// ->
// ```
// fn main() {
//     matches!(scrutinee, Some(val) if val.cond())
// }
// ```
pub(crate) fn convert_two_arm_bool_match_to_matches_macro(
    acc: &mut Assists,
    ctx: &AssistContext<'_>,
) -> Option<()> {
    let match_expr = ctx.find_node_at_offset::<ast::MatchExpr>()?;
    let match_arm_list = match_expr.match_arm_list()?;
    let mut arms = match_arm_list.arms();
    let first_arm = arms.next()?;
    let second_arm = arms.next()?;
    if arms.next().is_some() {
        cov_mark::hit!(non_two_arm_match);
        return None;
    }
    let first_arm_expr = first_arm.expr();
    let second_arm_expr = second_arm.expr();

    let invert_matches = if is_bool_literal_expr(&first_arm_expr, true)
        && is_bool_literal_expr(&second_arm_expr, false)
    {
        false
    } else if is_bool_literal_expr(&first_arm_expr, false)
        && is_bool_literal_expr(&second_arm_expr, true)
    {
        true
    } else {
        cov_mark::hit!(non_invert_bool_literal_arms);
        return None;
    };

    let target_range = ctx.sema.original_range(match_expr.syntax()).range;
    let expr = match_expr.expr()?;

    acc.add(
        AssistId("convert_two_arm_bool_match_to_matches_macro", AssistKind::RefactorRewrite),
        "Convert to matches!",
        target_range,
        |builder| {
            let mut arm_str = String::new();
            if let Some(ref pat) = first_arm.pat() {
                arm_str += &pat.to_string();
            }
            if let Some(ref guard) = first_arm.guard() {
                arm_str += &format!(" {}", &guard.to_string());
            }
            if invert_matches {
                builder.replace(target_range, format!("!matches!({}, {})", expr, arm_str));
            } else {
                builder.replace(target_range, format!("matches!({}, {})", expr, arm_str));
            }
        },
    )
}

fn is_bool_literal_expr(expr: &Option<ast::Expr>, expect_bool: bool) -> bool {
    if let Some(ast::Expr::Literal(lit)) = expr {
        if let ast::LiteralKind::Bool(b) = lit.kind() {
            return b == expect_bool;
        }
    }

    return false;
}

#[cfg(test)]
mod tests {
    use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};

    use super::convert_two_arm_bool_match_to_matches_macro;

    #[test]
    fn not_applicable_outside_of_range_left() {
        check_assist_not_applicable(
            convert_two_arm_bool_match_to_matches_macro,
            r#"
fn foo(a: Option<u32>) -> bool {
    $0 match a {
        Some(_val) => true,
        _ => false
    }
}
        "#,
        );
    }

    #[test]
    fn not_applicable_non_two_arm_match() {
        cov_mark::check!(non_two_arm_match);
        check_assist_not_applicable(
            convert_two_arm_bool_match_to_matches_macro,
            r#"
fn foo(a: Option<u32>) -> bool {
    match a$0 {
        Some(3) => true,
        Some(4) => true,
        _ => false
    }
}
        "#,
        );
    }

    #[test]
    fn not_applicable_non_bool_literal_arms() {
        cov_mark::check!(non_invert_bool_literal_arms);
        check_assist_not_applicable(
            convert_two_arm_bool_match_to_matches_macro,
            r#"
fn foo(a: Option<u32>) -> bool {
    match a$0 {
        Some(val) => val == 3,
        _ => false
    }
}
        "#,
        );
    }
    #[test]
    fn not_applicable_both_false_arms() {
        cov_mark::check!(non_invert_bool_literal_arms);
        check_assist_not_applicable(
            convert_two_arm_bool_match_to_matches_macro,
            r#"
fn foo(a: Option<u32>) -> bool {
    match a$0 {
        Some(val) => false,
        _ => false
    }
}
        "#,
        );
    }

    #[test]
    fn not_applicable_both_true_arms() {
        cov_mark::check!(non_invert_bool_literal_arms);
        check_assist_not_applicable(
            convert_two_arm_bool_match_to_matches_macro,
            r#"
fn foo(a: Option<u32>) -> bool {
    match a$0 {
        Some(val) => true,
        _ => true
    }
}
        "#,
        );
    }

    #[test]
    fn convert_simple_case() {
        check_assist(
            convert_two_arm_bool_match_to_matches_macro,
            r#"
fn foo(a: Option<u32>) -> bool {
    match a$0 {
        Some(_val) => true,
        _ => false
    }
}
"#,
            r#"
fn foo(a: Option<u32>) -> bool {
    matches!(a, Some(_val))
}
"#,
        );
    }

    #[test]
    fn convert_simple_invert_case() {
        check_assist(
            convert_two_arm_bool_match_to_matches_macro,
            r#"
fn foo(a: Option<u32>) -> bool {
    match a$0 {
        Some(_val) => false,
        _ => true
    }
}
"#,
            r#"
fn foo(a: Option<u32>) -> bool {
    !matches!(a, Some(_val))
}
"#,
        );
    }

    #[test]
    fn convert_with_guard_case() {
        check_assist(
            convert_two_arm_bool_match_to_matches_macro,
            r#"
fn foo(a: Option<u32>) -> bool {
    match a$0 {
        Some(val) if val > 3 => true,
        _ => false
    }
}
"#,
            r#"
fn foo(a: Option<u32>) -> bool {
    matches!(a, Some(val) if val > 3)
}
"#,
        );
    }

    #[test]
    fn convert_enum_match_cases() {
        check_assist(
            convert_two_arm_bool_match_to_matches_macro,
            r#"
enum X { A, B }

fn foo(a: X) -> bool {
    match a$0 {
        X::A => true,
        _ => false
    }
}
"#,
            r#"
enum X { A, B }

fn foo(a: X) -> bool {
    matches!(a, X::A)
}
"#,
        );
    }

    #[test]
    fn convert_target_simple() {
        check_assist_target(
            convert_two_arm_bool_match_to_matches_macro,
            r#"
fn foo(a: Option<u32>) -> bool {
    match a$0 {
        Some(val) => true,
        _ => false
    }
}
"#,
            r#"match a {
        Some(val) => true,
        _ => false
    }"#,
        );
    }

    #[test]
    fn convert_target_complex() {
        check_assist_target(
            convert_two_arm_bool_match_to_matches_macro,
            r#"
enum E { X, Y }

fn main() {
    match E::X$0 {
        E::X => true,
        _ => false,
    }
}
"#,
            "match E::X {
        E::X => true,
        _ => false,
    }",
        );
    }
}
<|endoftext|>


<commit_before>use bifrost::utils::async_locks::RwLockReadGuard as AsyncRwLockReadGuard;
use bifrost::utils::time::get_time;
use parking_lot::{Mutex, RwLock};
use ram::cell::{Cell, CellHeader, ReadError, WriteError};
use ram::entry::{Entry, EntryContent, EntryType};
use ram::schema::LocalSchemasCache;
use ram::segs::{Segment, MAX_SEGMENT_SIZE, MAX_SEGMENT_SIZE_U32};
use ram::tombstone::{Tombstone, TOMBSTONE_ENTRY_SIZE, TOMBSTONE_SIZE, TOMBSTONE_SIZE_U32};
use ram::types::{Id, Value};
use server::ServerMeta;
use std::collections::BTreeMap;
use std::ops::Bound::{Excluded, Included};
use std::rc::Rc;
use std::sync::atomic::{AtomicU64, AtomicUsize, Ordering};
use std::sync::Arc;
use utils::chashmap::{CHashMap, ReadGuard, WriteGuard};
use utils::raii_mutex_table::RAIIMutexTable;
use utils::ring_buffer::RingBuffer;

pub type CellReadGuard<'a> = ReadGuard<'a, u64, usize>;
pub type CellWriteGuard<'a> = WriteGuard<'a, u64, usize>;

pub struct Chunk {
    pub id: usize,
    pub index: Arc<CHashMap<u64, usize>>,
    pub segs: Arc<CHashMap<u64, Arc<Segment>>>,
    // Used only for locating segment for address
    // when putting tombstone, not normal data access
    pub addrs_seg: RwLock<BTreeMap<usize, u64>>,
    seg_counter: AtomicU64,
    pub head_seg: RwLock<Arc<Segment>>,
    pub meta: Arc<ServerMeta>,
    pub backup_storage: Option<String>,
    pub wal_storage: Option<String>,
    pub total_space: AtomicUsize,
    pub dead_entries: RingBuffer,
    pub capacity: usize,
    pub unstable_cells: RAIIMutexTable<u64>,
}

impl Chunk {
    fn new(
        id: usize,
        size: usize,
        meta: Arc<ServerMeta>,
        backup_storage: Option<String>,
        wal_storage: Option<String>,
    ) -> Chunk {
        let first_seg_id = 0;
        let bootstrap_segment_ref = Arc::new(Segment::new(
            first_seg_id,
            MAX_SEGMENT_SIZE,
            &backup_storage,
            &wal_storage,
        ));
        let segs = Arc::new(CHashMap::new());
        let index = Arc::new(CHashMap::new());
        let chunk = Chunk {
            id,
            segs,
            index,
            meta,
            backup_storage,
            wal_storage,
            capacity: size,
            total_space: AtomicUsize::new(0),
            seg_counter: AtomicU64::new(0),
            head_seg: RwLock::new(bootstrap_segment_ref.clone()),
            addrs_seg: RwLock::new(BTreeMap::new()),
            dead_entries: RingBuffer::new((size / MAX_SEGMENT_SIZE + 1) * 100),
            unstable_cells: RAIIMutexTable::new(),
        };
        chunk.put_segment(bootstrap_segment_ref);
        return chunk;
    }

    pub fn try_acquire(&self, size: u32) -> Option<PendingEntry> {
        let mut retried = 0;
        loop {
            let head = self.head_seg.read().clone();
            let mut head_seg_id = head.id;
            match head.try_acquire(size) {
                Some(addr) => {
                    debug!(
                        "Chunk {} acquired address {} for size {} in segment {}",
                        self.id, addr, size, head.id
                    );
                    head.references.fetch_add(1, Ordering::Relaxed);
                    return Some(PendingEntry {
                        addr,
                        seg: head,
                        size,
                    });
                }
                None => {
                    if self.total_space.load(Ordering::Relaxed) >= self.capacity - MAX_SEGMENT_SIZE
                    {
                        // No space left
                        return None;
                    }
                    let mut acquired_header = self.head_seg.write();
                    if head_seg_id == acquired_header.id {
                        // head segment did not changed and locked, suitable for creating a new segment and point it to
                        let new_seg_id = self.next_segment_id();
                        let new_seg = Segment::new(
                            new_seg_id,
                            MAX_SEGMENT_SIZE,
                            &self.backup_storage,
                            &self.wal_storage,
                        );
                        // for performance, won't CAS total_space
                        self.total_space
                            .fetch_add(MAX_SEGMENT_SIZE, Ordering::Relaxed);
                        let new_seg_ref = Arc::new(new_seg);
                        self.put_segment(new_seg_ref.clone());
                        debug!(
                            "Dropping old header segment {}. Reference count: {}",
                            acquired_header.id,
                            Arc::strong_count(&*acquired_header)
                        );
                        *acquired_header = new_seg_ref;
                        debug!(
                            "Dropped old header segment, new {}. Reference count: {}",
                            acquired_header.id,
                            Arc::strong_count(&*acquired_header)
                        );
                    }
                    // whether the segment acquisition success or not,
                    // try to get the new segment and try again
                }
            }
        }
    }

    pub fn next_segment_id(&self) -> u64 {
        self.seg_counter.fetch_add(1, Ordering::Relaxed) + 1
    }

    pub fn location_for_read<'a>(&self, hash: u64) -> Result<CellReadGuard, ReadError> {
        match self.index.get(&hash) {
            Some(index) => {
                if *index == 0 {
                    return Err(ReadError::CellDoesNotExisted);
                }
                return Ok(index);
            }
            None => {
                if hash == 0 {
                    Err(ReadError::CellIdIsUnitId)
                } else {
                    Err(ReadError::CellDoesNotExisted)
                }
            }
        }
    }

    pub fn location_for_write(&self, hash: u64) -> Option<CellWriteGuard> {
        match self.index.get_mut(&hash) {
            Some(index) => {
                if *index == 0 {
                    return None;
                }
                return Some(index);
            }
            None => None,
        }
    }

    fn head_cell(&self, hash: u64) -> Result<CellHeader, ReadError> {
        Cell::header_from_chunk_raw(*self.location_for_read(hash)?).map(|pair| pair.0)
    }

    fn read_cell(&self, hash: u64) -> Result<Cell, ReadError> {
        Cell::from_chunk_raw(*self.location_for_read(hash)?, self)
    }

    fn read_selected(&self, hash: u64, fields: &[u64]) -> Result<Vec<Value>, ReadError> {
        let loc = self.location_for_read(hash)?;
        let selected_data = Cell::select_from_chunk_raw(*loc, self, fields)?;
        let mut result = Vec::with_capacity(fields.len());
        match selected_data {
            Value::Map(map) => {
                for field_id in fields {
                    result.push(map.get_by_key_id(*field_id).clone())
                }
                return Ok(result);
            }
            _ => Err(ReadError::CellTypeIsNotMapForSelect),
        }
    }

    fn read_partial_raw(&self, hash: u64, offset: usize, len: usize) -> Result<Vec<u8>, ReadError> {
        let loc = self.location_for_read(hash)?;
        let mut head_ptr = *loc + offset;
        let mut data = Vec::with_capacity(len);
        for ptr in head_ptr..(head_ptr + len) {
            data.push(unsafe { (*(ptr as *const u8)) });
        }
        Ok(data.to_vec())
    }

    fn write_cell_unchecked(&self, cell: &mut Cell) -> Result<CellHeader, WriteError> {
        debug!("Writing cell {:?} to chunk {}", cell.id(), self.id);
        let loc = cell.write_to_chunk(self)?;
        let mut need_rollback = false;
        self.index.upsert(
            cell.header.hash,
            || loc,
            |inserted_loc| {
                if *inserted_loc == 0 {
                    *inserted_loc = loc
                } else {
                    need_rollback = true;
                }
            },
        );
        if need_rollback {
            return Err(WriteError::CellAlreadyExisted);
        }
        return Ok(cell.header);
    }

    fn write_cell(&self, cell: &mut Cell) -> Result<CellHeader, WriteError> {
        let hash = cell.header.hash;
        if self.location_for_read(hash).is_ok() {
            return Err(WriteError::CellAlreadyExisted);
        } else {
            self.write_cell_unchecked(cell)
        }
    }

    #[inline]
    fn update_cell_from_guard(
        &self,
        cell: &mut Cell,
        guard: &mut CellWriteGuard,
    ) -> Result<CellHeader, WriteError> {
        let old_location = **guard;
        let new_location = cell.write_to_chunk(self)?;
        **guard = new_location;
        return Ok(cell.header);
    }

    fn update_cell(&self, cell: &mut Cell) -> Result<CellHeader, WriteError> {
        let hash = cell.header.hash;
        let (res, loc) = if let Some(mut cell_location) = self.location_for_write(hash) {
            (
                self.update_cell_from_guard(cell, &mut cell_location),
                *cell_location,
            )
        } else {
            return Err(WriteError::CellDoesNotExisted);
        };
        self.mark_dead_entry(loc);
        return res;
    }

    fn upsert_cell(&self, cell: &mut Cell) -> Result<CellHeader, WriteError> {
        let hash = cell.header.hash;
        let (update_res, loc) = if let Some(mut cell_location) = self.location_for_write(hash) {
            (
                self.update_cell_from_guard(cell, &mut cell_location),
                *cell_location,
            )
        } else {
            return self.write_cell(cell);
        };
        self.mark_dead_entry(loc);
        return update_res;
    }

    fn update_cell_by<U>(&self, hash: u64, update: U) -> Result<Cell, WriteError>
    where
        U: Fn(Cell) -> Option<Cell>,
    {
        let (res, loc) = if let Some(mut cell_location) = self.location_for_write(hash) {
            let cell = Cell::from_chunk_raw(*cell_location, self);
            match cell {
                Ok(cell) => {
                    let mut new_cell = update(cell);
                    if let Some(mut new_cell) = new_cell {
                        let old_location = *cell_location;
                        let new_location = new_cell.write_to_chunk(self)?;
                        *cell_location = new_location;
                        (Ok(new_cell), old_location)
                    } else {
                        return Err(WriteError::UserCanceledUpdate);
                    }
                }
                Err(e) => return Err(WriteError::ReadError(e)),
            }
        } else {
            return Err(WriteError::CellDoesNotExisted);
        };
        self.mark_dead_entry(loc);
        return res;
    }
    fn remove_cell(&self, hash: u64) -> Result<(), WriteError> {
        let unstable_guard = self.unstable_cells.lock(hash);
        if let Some(cell_location) = self.index.remove(&hash) {
            self.put_tombstone_by_cell_loc(cell_location)?;
            Ok(())
        } else {
            Err(WriteError::CellDoesNotExisted)
        }
    }
    fn remove_cell_by<P>(&self, hash: u64, predict: P) -> Result<(), WriteError>
    where
        P: Fn(Cell) -> bool,
    {
        let mut result = Ok(());
        self.index.alter(hash, |loc_opt| match loc_opt {
            Some(cell_location) => {
                let cell = Cell::from_chunk_raw(cell_location, self);
                match cell {
                    Ok(cell) => {
                        if predict(cell) {
                            let put_tombstone_result =
                                self.put_tombstone_by_cell_loc(cell_location);
                            if put_tombstone_result.is_err() {
                                result = put_tombstone_result;
                                loc_opt
                            } else {
                                None
                            }
                        } else {
                            result = Err(WriteError::CellDoesNotExisted);
                            loc_opt
                        }
                    }
                    Err(e) => {
                        result = Err(WriteError::ReadError(e));
                        None
                    }
                }
            }
            None => {
                result = Err(WriteError::CellDoesNotExisted);
                None
            }
        });
        return result;
    }

    pub fn put_segment(&self, segment: Arc<Segment>) {
        debug!(
            "Putting segment for chunk {} with id {}",
            self.id, segment.id
        );
        let segment_id = segment.id;
        let segment_addr = segment.addr;
        let original_pos = self.segs.get(&segment_id).map(|seg| seg.addr);
        {
            let mut seg_index_guard = self.addrs_seg.write();
            if let Some(seg_original_pos) = original_pos {
                // remove old segment by it's position
                seg_index_guard.remove(&seg_original_pos);
            }
            seg_index_guard.insert(segment_addr, segment_id);
        }
        self.segs.insert(segment_id, segment);
    }

    pub fn remove_segment(&self, segment_id: u64) {
        debug!(
            "Removing segment for chunk {} with id {}",
            self.id, segment_id
        );
        if let Some(seg) = self.segs.remove(&segment_id) {
            self.addrs_seg.write().remove(&seg.addr).unwrap();
            seg.dispense();
        }
    }

    fn locate_segment(&self, addr: usize) -> Option<Arc<Segment>> {
        let segs_addr_range = self.addrs_seg.read();
        debug!("locating segment addr {} in {:?}", addr, *segs_addr_range);
        segs_addr_range
            .range((Excluded(addr - MAX_SEGMENT_SIZE), Included(addr)))
            .last()
            .and_then(|(_, seg_id)| {
                self.segs
                    .get(seg_id)
                    .map(|guard| guard.clone())
                    .and_then(|seg| {
                        if addr < seg.bound && addr >= seg.addr {
                            return Some(seg);
                        } else {
                            return None;
                        }
                    })
            })
    }

    #[inline]
    fn put_tombstone(&self, cell_location: usize, cell_header: &CellHeader) {
        let cell_seg = self.locate_segment(cell_location).expect(
            format!(
                "cannot locate cell segment for tombstone. Cell id: {:?} at {}",
                cell_header.id(),
                cell_location
            )
            .as_str(),
        );
        let pending_entry = (|| loop {
            if let Some(pending_entry) = self.try_acquire(*TOMBSTONE_ENTRY_SIZE) {
                return pending_entry;
            }
            warn!(
                "Chunk {} is too full to put a tombstone. Will retry.",
                self.id
            )
        })();
        Tombstone::put(
            pending_entry.addr,
            cell_seg.id,
            cell_header.version,
            cell_header.partition,
            cell_header.hash,
        );
        pending_entry.seg.tombstones.fetch_add(1, Ordering::Relaxed);
    }

    fn put_tombstone_by_cell_loc(&self, cell_location: usize) -> Result<(), WriteError> {
        debug!(
            "Put tombstone for chunk {} for cell {}",
            self.id, cell_location
        );
        let header = Cell::header_from_chunk_raw(cell_location)
            .map_err(|e| WriteError::ReadError(e))?
            .0;
        self.put_tombstone(cell_location, &header);
        self.mark_dead_entry(cell_location);
        Ok(())
    }

    // put dead entry address in a ideally non-blocking queue and wait for a worker to
    // make the changes in corresponding segments.
    // Because calculate segment from location is computation intensive, it have to be done lazily
    #[inline]
    fn mark_dead_entry(&self, loc: usize) {
        self.dead_entries.push(loc);
    }

    pub fn segments(&self) -> Vec<Arc<Segment>> {
        let addr_segs = self.addrs_seg.read();
        let segs = addr_segs.values();
        let mut list = Vec::with_capacity(self.segs.len());
        for seg_id in segs {
            if let Some(segment) = self.segs.get(seg_id) {
                list.push(segment.clone());
            }
        }
        return list;
    }

    // this function should be invoked repeatedly to flush the queue
    pub fn apply_dead_entry(&self) {
        trace!("Applying dead entries in buffer");
        let marks = self.dead_entries.iter();
        for addr in marks {
            if let Some(seg) = self.locate_segment(addr) {
                let (entry, _) = Entry::decode_from(addr, |_, _| {});
                seg.dead_space
                    .fetch_add(entry.content_length, Ordering::Relaxed);
            }
        }
    }

    #[inline]
    pub fn segment_ids(&self) -> Vec<u64> {
        let addrs_guard = self.addrs_seg.read();
        addrs_guard.values().cloned().collect()
    }

    // Scan for dead tombstone. This will scan the whole segment, decoding all entry header
    // and looking for those with entry type tombstone.
    // It is resource intensive so there will be some rules to skip the scan.
    // This function should be invoked repeatedly by cleaner
    // Actual cleaning will be performed by cleaner regardless tombstone survival condition
    pub fn scan_tombstone_survival(&self) {
        trace!("Scanning tombstones");
        let seg_ids = self.segment_ids();
        for seg_id in seg_ids {
            if let Some(segment) = self.segs.get(&seg_id).map(|s| (*s).clone()) {
                let now = get_time();
                let tombstones = segment.tombstones.load(Ordering::Relaxed);
                let dead_tombstones = segment.dead_tombstones.load(Ordering::Relaxed);
                let mut death_count = 0;
                if
                // have not much tombstones
                (tombstones as f64) * (TOMBSTONE_SIZE as f64) < (MAX_SEGMENT_SIZE as f64) * 0.2 ||
                        // large partition have been scanned
                        (dead_tombstones as f32 / tombstones as f32) > 0.8 ||
                        // have been scanned recently
                        now - segment.last_tombstones_scanned.load(Ordering::Relaxed) < 5000
                {
                    continue;
                }
                debug!(
                    "Scanning tombstones in chunk {}, segment {}",
                    self.id, seg_id
                );
                for entry_meta in segment.entry_iter() {
                    if entry_meta.entry_header.entry_type == EntryType::Tombstone {
                        let tombstone =
                            Tombstone::read_from_entry_content_addr(entry_meta.body_pos);
                        if !self.segs.contains_key(&tombstone.segment_id) {
                            // segment that the tombstone pointed to have been cleaned by compact or combined cleaner
                            death_count += 1;
                        }
                    }
                }
                // store the death count for following cleaners will just reset it
                segment
                    .dead_tombstones
                    .store(death_count, Ordering::Relaxed);
                segment
                    .last_tombstones_scanned
                    .store(now, Ordering::Relaxed);
                debug!(
                    "Scanned tombstones in chunk {}, segment {}, death count {}",
                    self.id, seg_id, death_count
                );
            } else {
                warn!("leaked segment in addrs_seg: {}", seg_id)
            }
        }
    }

    pub fn segs_for_compact_cleaner(&self) -> Vec<Arc<Segment>> {
        let utilization_selection = self
            .segments()
            .into_iter()
            .map(|seg| {
                let rate = seg.living_rate();
                (seg, rate)
            })
            .filter(|(_, utilization)| *utilization < 90f32);
        let head_seg = self.head_seg.read();
        let mut list: Vec<_> = utilization_selection
            .filter(|(seg, _)| seg.id != head_seg.id && seg.no_references())
            .collect();
        list.sort_by(|pair1, pair2| pair1.1.partial_cmp(&pair2.1).unwrap());
        return list.into_iter().map(|pair| pair.0).collect();
    }

    pub fn segs_for_combine_cleaner(&self) -> Vec<Arc<Segment>> {
        let head_seg = self.head_seg.read();
        let mut mapping: Vec<_> = self
            .segments()
            .into_iter()
            .map(|seg| {
                let living = seg.living_space() as f32;
                let segment_utilization = living / MAX_SEGMENT_SIZE_U32 as f32;
                (seg, segment_utilization)
            })
            .filter(|(seg, utilization)| {
                *utilization < 50f32 && head_seg.id != seg.id && seg.no_references()
            })
            .collect();
        mapping.sort_by(|pair1, pair2| pair1.1.partial_cmp(&pair2.1).unwrap());
        return mapping.into_iter().map(|(seg, _)| seg).collect();
    }

    pub fn check_and_archive_segments(&self) {
        let seg_ids = self.segment_ids();
        let head_id = self.head_seg.read().id;
        for seg_id in seg_ids {
            if seg_id == head_id {
                continue;
            } // never archive head segments
            if let Some(segment) = self.segs.get(&seg_id) {
                if !segment
                    .archived
                    .compare_and_swap(false, true, Ordering::Relaxed)
                {
                    if let Err(e) = segment.archive() {
                        error!("cannot archive segment {}, reason:{:?}", self.id, e)
                    }
                }
            }
        }
    }

    pub fn live_entries(&self, seg: &Arc<Segment>) -> impl Iterator<Item = Entry> {
        let seg_owned = seg.clone();
        let chunk_id = self.id;
        let chunk_index = self.index.clone();
        let chunk_segs = self.segs.clone();
        seg.entry_iter()
            .filter_map(move |entry_meta| {
                let seg = &seg_owned;
                let entry_size = entry_meta.entry_size;
                let entry_header = entry_meta.entry_header;
                debug!("Iterating live entries on chunk {} segment {}. Got {:?} at {} size {}",
                       chunk_id, seg.id, entry_header.entry_type, entry_meta.entry_pos, entry_size);
                match entry_header.entry_type {
                    EntryType::Cell => {
                        debug!("Entry at {} is a cell", entry_meta.entry_pos);
                        let cell_header =
                            Cell::cell_header_from_entry_content_addr(
                                entry_meta.body_pos, &entry_header);
                        debug!("Cell header read, id is {:?}", cell_header.id());
                        if chunk_index
                            .get(&cell_header.hash)
                            .map(|g| *g) == Some(entry_meta.entry_pos) {
                            debug!("Cell entry {:?} is valid", cell_header.id());
                            return Some(Entry {
                                meta: entry_meta,
                                content: EntryContent::Cell(cell_header)
                            });
                        } else {
                            debug!("Cell entry index mismatch for {:?}, will be ditched", cell_header.id());
                        }
                    },
                    EntryType::Tombstone => {
                        debug!("Entry at {} is a tombstone", entry_meta.entry_pos);
                        let tombstone =
                            Tombstone::read_from_entry_content_addr(entry_meta.body_pos);
                        if chunk_segs.contains_key(&tombstone.segment_id) {
                            debug!("Tomestone entry {:?} - {:?} at {} is valid",
                                   tombstone.partition, tombstone.hash, tombstone.segment_id);
                            return Some(Entry {
                                meta: entry_meta,
                                content: EntryContent::Tombstone(tombstone)
                            });
                        } else {
                            debug!("Tombstone target at {} have been removed, will be ditched", tombstone.segment_id)
                        }
                    },
                    _ => panic!("Unexpected cell type on getting live entries at {}: type {:?}, size {}, append header {}, ends at {}",
                                entry_meta.entry_pos, entry_header, entry_size,
                                seg.append_header.load(Ordering::Relaxed),
                                entry_meta.entry_pos + entry_size)
                }
                return None
            })
    }

    pub fn cell_addresses(&self) -> BTreeMap<u64, usize> {
        (*self.index).clone().into_iter().collect()
    }
}

pub struct PendingEntry {
    pub seg: Arc<Segment>,
    pub addr: usize,
    pub size: u32,
}

impl Drop for PendingEntry {
    // dealing with entry write ahead log
    fn drop(&mut self) {
        self.seg.write_wal(self.addr, self.size);
        self.seg.references.fetch_sub(1, Ordering::Relaxed);
    }
}

pub struct Chunks {
    pub list: Vec<Chunk>,
}

impl Chunks {
    pub fn new(
        count: usize,
        size: usize,
        meta: Arc<ServerMeta>,
        backup_storage: Option<String>,
        wal_storage: Option<String>,
    ) -> Arc<Chunks> {
        let chunk_size = size / count;
        let mut chunks = Vec::new();
        debug!("Creating {} chunks, total {} bytes", count, size);
        for i in 0..count {
            let backup_storage = backup_storage
                .clone()
                .map(|dir| format!("{}/chunk-bk-{}", dir, i));
            let wal_storage = wal_storage
                .clone()
                .map(|dir| format!("{}/chunk-wal-{}", dir, i));
            chunks.push(Chunk::new(
                i,
                chunk_size,
                meta.clone(),
                backup_storage,
                wal_storage,
            ));
        }
        Arc::new(Chunks { list: chunks })
    }
    pub fn new_dummy(count: usize, size: usize) -> Arc<Chunks> {
        Chunks::new(
            count,
            size,
            Arc::<ServerMeta>::new(ServerMeta {
                schemas: LocalSchemasCache::new("", None).unwrap(),
            }),
            None,
            None,
        )
    }
    fn locate_chunk_by_partition(&self, partition: u64) -> &Chunk {
        let chunk_id = partition as usize % self.list.len();
        return &self.list[chunk_id];
    }
    fn locate_chunk_by_key(&self, key: &Id) -> (&Chunk, u64) {
        return (self.locate_chunk_by_partition(key.higher), key.lower);
    }
    pub fn read_cell(&self, key: &Id) -> Result<Cell, ReadError> {
        let (chunk, hash) = self.locate_chunk_by_key(key);
        return chunk.read_cell(hash);
    }
    pub fn read_selected(&self, key: &Id, fields: &[u64]) -> Result<Vec<Value>, ReadError> {
        let (chunk, hash) = self.locate_chunk_by_key(key);
        return chunk.read_selected(hash, fields);
    }
    pub fn read_partial_raw(
        &self,
        key: &Id,
        offset: usize,
        len: usize,
    ) -> Result<Vec<u8>, ReadError> {
        let (chunk, hash) = self.locate_chunk_by_key(key);
        return chunk.read_partial_raw(hash, offset, len);
    }
    pub fn head_cell(&self, key: &Id) -> Result<CellHeader, ReadError> {
        let (chunk, hash) = self.locate_chunk_by_key(key);
        return chunk.head_cell(hash);
    }
    pub fn location_for_read(&self, key: &Id) -> Result<CellReadGuard, ReadError> {
        let (chunk, hash) = self.locate_chunk_by_key(key);
        chunk.location_for_read(hash)
    }
    pub fn write_cell(&self, cell: &mut Cell) -> Result<CellHeader, WriteError> {
        let chunk = self.locate_chunk_by_partition(cell.header.partition);
        return chunk.write_cell(cell);
    }
    pub fn update_cell(&self, cell: &mut Cell) -> Result<CellHeader, WriteError> {
        let chunk = self.locate_chunk_by_partition(cell.header.partition);
        return chunk.update_cell(cell);
    }
    pub fn update_cell_by<U>(&self, key: &Id, update: U) -> Result<Cell, WriteError>
    where
        U: Fn(Cell) -> Option<Cell>,
    {
        let (chunk, hash) = self.locate_chunk_by_key(key);
        return chunk.update_cell_by(hash, update);
    }
    pub fn upsert_cell(&self, cell: &mut Cell) -> Result<CellHeader, WriteError> {
        let chunk = self.locate_chunk_by_partition(cell.header.partition);
        return chunk.upsert_cell(cell);
    }
    pub fn remove_cell(&self, key: &Id) -> Result<(), WriteError> {
        let (chunk, hash) = self.locate_chunk_by_key(key);
        return chunk.remove_cell(hash);
    }
    pub fn remove_cell_by<P>(&self, key: &Id, predict: P) -> Result<(), WriteError>
    where
        P: Fn(Cell) -> bool,
    {
        let (chunk, hash) = self.locate_chunk_by_key(key);
        return chunk.remove_cell_by(hash, predict);
    }
    pub fn address_of(&self, key: &Id) -> usize {
        let (chunk, hash) = self.locate_chunk_by_key(key);
        return *chunk.location_for_read(hash).unwrap();
    }
}
<commit_msg>remove conflict<commit_after><|endoftext|>


<commit_before>use kekbit_codecs::codecs::text::PlainTextDataFormat;
use kekbit_core::api::{Reader, Writer};
use kekbit_core::header::Header;
use kekbit_core::shm::{shm_reader, shm_writer};
use kekbit_core::tick::TickUnit;
use std::sync::atomic::AtomicBool;
use std::sync::atomic::Ordering;
use std::sync::Arc;
use std::time::Duration;

const FOREVER: u64 = 999_999_999_999;

fn run_writer(your_id: u64, channel_id: u64, run: Arc<AtomicBool>) {
    let tmp_dir = std::env::temp_dir().join("kekchat");
    let msg_size = 1000;
    let header = Header::new(your_id, channel_id, msg_size * 1000, msg_size, FOREVER, TickUnit::Nanos);
    let mut writer = shm_writer(&tmp_dir, &header, PlainTextDataFormat).unwrap();
    std::thread::sleep(Duration::from_millis(100));
    while run.load(Ordering::Relaxed) == true {
        let mut input = String::new();
        std::io::stdin().read_line(&mut input).expect("Failed read");
        let data = input.trim();
        writer.write(&data).unwrap();
        if input.trim() == "Bye".to_string() {
            println!("Sent Bye. Exiting.....");
            run.store(false, Ordering::Relaxed);
            break;
        }
    }
}

fn run_reader(other_id: u64, channel_id: u64, run: Arc<AtomicBool>) {
    let tmp_dir = std::env::temp_dir().join("kekchat");
    let mut tries = 5;
    let mut reader_res = shm_reader(&tmp_dir, channel_id);
    std::thread::sleep(Duration::from_millis(1000));
    while tries > 0 && reader_res.is_err() {
        reader_res = shm_reader(&tmp_dir, channel_id);
        if reader_res.is_ok() {
            break;
        }
        std::thread::sleep(Duration::from_millis(300));
        tries -= 1;
    }
    if reader_res.is_err() {
        println!("Could not connect to chat partner");
        std::process::exit(-1);
    }
    let mut reader = reader_res.unwrap();
    while run.load(Ordering::Relaxed) == true {
        let mut stop = false;
        let read_res = reader.read(
            &mut |_pos, msg: &[u8]| {
                let msg_str = std::str::from_utf8(&msg).unwrap();
                println!("{} >{}", other_id, msg_str);
                if msg_str == "Bye".to_string() {
                    println!("Received Bye. Exiting.....");
                    stop = true;
                }
            },
            10,
        );
        if stop {
            run.store(false, Ordering::Relaxed);
            std::process::exit(0);
        } else {
            match read_res {
                Ok(bytes_count) => {
                    if bytes_count == 0 {
                        std::thread::sleep(Duration::from_millis(1000));
                    }
                }
                Err(err) => {
                    println!("Error occured {:?} ", err);
                    run.store(false, Ordering::Relaxed);
                    std::process::exit(0);
                }
            }
        }
    }
}

pub fn main() {
    let args: Vec<u64> = std::env::args().skip(1).map(|id| id.parse().unwrap()).collect();
    assert!(args.len() == 3);
    let your_id = args[0];
    let other_id = args[1];
    let channel_id = args[2];
    println!("{} will talk with {} on channel {}", your_id, other_id, channel_id);
    let run = Arc::new(AtomicBool::new(true));
    let run_w = run.clone();
    let run_r = run.clone();
    let handle_w = std::thread::spawn(move || run_writer(your_id, channel_id, run_w));
    let handle_r = std::thread::spawn(move || run_reader(other_id, channel_id, run_r));
    handle_r.join().unwrap();
    handle_w.join().unwrap();
}
<commit_msg>Chat example Updated!<commit_after>use kekbit_codecs::codecs::text::PlainTextDataFormat;
use kekbit_core::api::{Reader, Writer};
use kekbit_core::header::Header;
use kekbit_core::shm::{shm_writer, try_shm_reader};
use kekbit_core::tick::TickUnit;
use std::sync::atomic::AtomicBool;
use std::sync::atomic::Ordering;
use std::sync::Arc;
use std::time::Duration;

const FOREVER: u64 = 999_999_999_999;

fn run_writer(channel_id: u64, run: Arc<AtomicBool>) {
    let tmp_dir = std::env::temp_dir().join("kekchat");
    let msg_size = 1000;
    let header = Header::new(1111, channel_id, msg_size * 1000, msg_size, FOREVER, TickUnit::Nanos);
    let mut writer = shm_writer(&tmp_dir, &header, PlainTextDataFormat).unwrap();
    std::thread::yield_now();
    while run.load(Ordering::Relaxed) == true {
        let mut input = String::new();
        std::io::stdin().read_line(&mut input).expect("Failed read");
        let data = input.trim();
        if data.len() > 0 {
            writer.write(&data).unwrap();
            if input.trim() == "Bye".to_string() {
                println!("Sent Bye. Exiting.....");
                run.store(false, Ordering::Relaxed);
                break;
            }
        }
    }
}

fn run_reader(channel_id: u64, run: Arc<AtomicBool>) {
    let tmp_dir = std::env::temp_dir().join("kekchat");
    let reader_res = try_shm_reader(&tmp_dir, channel_id, 10_000, 30);
    if reader_res.is_err() {
        println!("Could not connect to chat partner");
        std::process::exit(0);
    }
    let mut reader = reader_res.unwrap();
    while run.load(Ordering::Relaxed) == true {
        let mut stop = false;
        let read_res = reader.read(
            &mut |_pos, msg: &[u8]| {
                let msg_str = std::str::from_utf8(&msg).unwrap();
                println!(">>>{}", msg_str);
                if msg_str == "Bye".to_string() {
                    println!("Received Bye. Exiting.....");
                    stop = true;
                }
            },
            10,
        );
        if stop {
            run.store(false, Ordering::Relaxed);
            std::process::exit(0);
        } else {
            match read_res {
                Ok(bytes_count) => {
                    if bytes_count == 0 {
                        std::thread::sleep(Duration::from_millis(300));
                    }
                }
                Err(err) => {
                    println!("Error occured {:?} ", err);
                    run.store(false, Ordering::Relaxed);
                    std::process::exit(0);
                }
            }
        }
    }
}

pub fn main() {
    let args: Vec<u64> = std::env::args().skip(1).map(|id| id.parse().unwrap()).collect();
    assert!(args.len() == 2);
    let your_channel_id = args[0];
    let other_channel_id = args[1];
    let run = Arc::new(AtomicBool::new(true));
    let run_w = run.clone();
    let run_r = run.clone();
    let handle_w = std::thread::spawn(move || run_writer(your_channel_id, run_w));
    let handle_r = std::thread::spawn(move || run_reader(other_channel_id, run_r));
    handle_r.join().unwrap();
    handle_w.join().unwrap();
}
<|endoftext|>


<commit_before>// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

extern mod std;
extern mod extra;

use std::io;
use std::io::{File, Reader};
use std::io::process::ExitStatus;
use std::os;
use std::run::{Process, ProcessOptions};
use std::str;
use extra::test::{DynTestName, DynTestFn, TestDesc, TestOpts, TestDescAndFn};
use extra::test::run_tests_console;

fn main() {
    let args = os::args();
    if args.len() < 2 {
        println("error: at least one reftest list must be given");
        os::set_exit_status(1);
        return;
    }

    let tests = parse_lists(args.tail());
    let test_opts = TestOpts {
        filter: None,
        run_ignored: false,
        logfile: None,
        run_tests: true,
        run_benchmarks: false,
        ratchet_noise_percent: None,
        ratchet_metrics: None,
        save_metrics: None,
        test_shard: None,
    };

    if !run_tests_console(&test_opts, tests) {
        os::set_exit_status(1);
    }
}

enum ReftestKind {
    Same,
    Different,
}

struct Reftest {
    name: ~str,
    kind: ReftestKind,
    left: ~str,
    right: ~str,
    id: uint,
}

fn parse_lists(filenames: &[~str]) -> ~[TestDescAndFn] {
    let mut tests: ~[TestDescAndFn] = ~[];
    let mut next_id = 0;
    for file in filenames.iter() {
        let file_path = Path::new(file.clone());
        let contents = match File::open_mode(&file_path, io::Open, io::Read) {
            Some(mut f) => str::from_utf8_owned(f.read_to_end()),
            None => fail!("Could not open file")
        };

        for line in contents.lines() {
            let parts: ~[&str] = line.split(' ').filter(|p| !p.is_empty()).collect();

            if parts.len() != 3 {
                fail!("reftest line: '{:s}' doesn't match 'KIND LEFT RIGHT'", line);
            }

            let kind = match parts[0] {
                "==" => Same,
                "!=" => Different,
                _ => fail!("reftest line: '{:s}' has invalid kind '{:s}'",
                           line, parts[0])
            };
            let src_path = file_path.dir_path();
            let src_dir = src_path.display().to_str();
            let file_left =  src_dir + "/" + parts[1];
            let file_right = src_dir + "/" + parts[2];

            let reftest = Reftest {
                name: parts[1] + " / " + parts[2],
                kind: kind,
                left: file_left,
                right: file_right,
                id: next_id,
            };

            next_id += 1;

            tests.push(make_test(reftest));
        }
    }
    tests
}

fn make_test(reftest: Reftest) -> TestDescAndFn {
    let name = reftest.name.clone();
    TestDescAndFn {
        desc: TestDesc {
            name: DynTestName(name),
            ignore: false,
            should_fail: false,
        },
        testfn: DynTestFn(proc() {
            check_reftest(reftest);
        }),
    }
}

fn check_reftest(reftest: Reftest) {
    let left_filename = format!("/tmp/servo-reftest-{:06u}-left.png", reftest.id);
    let right_filename = format!("/tmp/servo-reftest-{:06u}-right.png", reftest.id);

    let args = ~[~"-o", left_filename.clone(), reftest.left.clone()];
    let mut process = Process::new("./servo", args, ProcessOptions::new()).unwrap();
    let _retval = process.finish();
    // assert!(retval == 0);

    let args = ~[~"-o", right_filename.clone(), reftest.right.clone()];
    let mut process = Process::new("./servo", args, ProcessOptions::new()).unwrap();
    let _retval = process.finish();
    // assert!(retval == 0);

    // check the pngs are bit equal
    let args = ~[left_filename.clone(), right_filename.clone()];
    let mut process = Process::new("cmp", args, ProcessOptions::new()).unwrap();
    let retval = process.finish();

    match reftest.kind {
        Same => assert!(retval == ExitStatus(0)),
        Different => assert!(retval != ExitStatus(0)),
    }
}
<commit_msg>Enable checks for reftest exit status.<commit_after>// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

extern mod std;
extern mod extra;

use std::io;
use std::io::{File, Reader};
use std::io::process::ExitStatus;
use std::os;
use std::run::{Process, ProcessOptions};
use std::str;
use extra::test::{DynTestName, DynTestFn, TestDesc, TestOpts, TestDescAndFn};
use extra::test::run_tests_console;

fn main() {
    let args = os::args();
    if args.len() < 2 {
        println("error: at least one reftest list must be given");
        os::set_exit_status(1);
        return;
    }

    let tests = parse_lists(args.tail());
    let test_opts = TestOpts {
        filter: None,
        run_ignored: false,
        logfile: None,
        run_tests: true,
        run_benchmarks: false,
        ratchet_noise_percent: None,
        ratchet_metrics: None,
        save_metrics: None,
        test_shard: None,
    };

    if !run_tests_console(&test_opts, tests) {
        os::set_exit_status(1);
    }
}

enum ReftestKind {
    Same,
    Different,
}

struct Reftest {
    name: ~str,
    kind: ReftestKind,
    left: ~str,
    right: ~str,
    id: uint,
}

fn parse_lists(filenames: &[~str]) -> ~[TestDescAndFn] {
    let mut tests: ~[TestDescAndFn] = ~[];
    let mut next_id = 0;
    for file in filenames.iter() {
        let file_path = Path::new(file.clone());
        let contents = match File::open_mode(&file_path, io::Open, io::Read) {
            Some(mut f) => str::from_utf8_owned(f.read_to_end()),
            None => fail!("Could not open file")
        };

        for line in contents.lines() {
            let parts: ~[&str] = line.split(' ').filter(|p| !p.is_empty()).collect();

            if parts.len() != 3 {
                fail!("reftest line: '{:s}' doesn't match 'KIND LEFT RIGHT'", line);
            }

            let kind = match parts[0] {
                "==" => Same,
                "!=" => Different,
                _ => fail!("reftest line: '{:s}' has invalid kind '{:s}'",
                           line, parts[0])
            };
            let src_path = file_path.dir_path();
            let src_dir = src_path.display().to_str();
            let file_left =  src_dir + "/" + parts[1];
            let file_right = src_dir + "/" + parts[2];

            let reftest = Reftest {
                name: parts[1] + " / " + parts[2],
                kind: kind,
                left: file_left,
                right: file_right,
                id: next_id,
            };

            next_id += 1;

            tests.push(make_test(reftest));
        }
    }
    tests
}

fn make_test(reftest: Reftest) -> TestDescAndFn {
    let name = reftest.name.clone();
    TestDescAndFn {
        desc: TestDesc {
            name: DynTestName(name),
            ignore: false,
            should_fail: false,
        },
        testfn: DynTestFn(proc() {
            check_reftest(reftest);
        }),
    }
}

fn check_reftest(reftest: Reftest) {
    let left_filename = format!("/tmp/servo-reftest-{:06u}-left.png", reftest.id);
    let right_filename = format!("/tmp/servo-reftest-{:06u}-right.png", reftest.id);

    let args = ~[~"-o", left_filename.clone(), reftest.left.clone()];
    let mut process = Process::new("./servo", args, ProcessOptions::new()).unwrap();
    let retval = process.finish();
    assert!(retval == ExitStatus(0));

    let args = ~[~"-o", right_filename.clone(), reftest.right.clone()];
    let mut process = Process::new("./servo", args, ProcessOptions::new()).unwrap();
    let retval = process.finish();
    assert!(retval == ExitStatus(0));

    // check the pngs are bit equal
    let args = ~[left_filename.clone(), right_filename.clone()];
    let mut process = Process::new("cmp", args, ProcessOptions::new()).unwrap();
    let retval = process.finish();

    match reftest.kind {
        Same => assert!(retval == ExitStatus(0)),
        Different => assert!(retval != ExitStatus(0)),
    }
}
<|endoftext|>


<commit_before>//! Common helper code for ABI lowering.
//!
//! This module provides functions and data structures that are useful for implementing the
//! `TargetIsa::legalize_signature()` method.

use ir::{AbiParam, ArgumentExtension, ArgumentLoc, Type};
use std::cmp::Ordering;
use std::vec::Vec;

/// Legalization action to perform on a single argument or return value when converting a
/// signature.
///
/// An argument may go through a sequence of legalization steps before it reaches the final
/// `Assign` action.
#[derive(Clone, Copy, Debug)]
pub enum ArgAction {
    /// Assign the argument to the given location.
    Assign(ArgumentLoc),

    /// Convert the argument, then call again.
    ///
    /// This action can split an integer type into two smaller integer arguments, or it can split a
    /// SIMD vector into halves.
    Convert(ValueConversion),
}

impl From<ArgumentLoc> for ArgAction {
    fn from(x: ArgumentLoc) -> Self {
        ArgAction::Assign(x)
    }
}

impl From<ValueConversion> for ArgAction {
    fn from(x: ValueConversion) -> Self {
        ArgAction::Convert(x)
    }
}

/// Legalization action to be applied to a value that is being passed to or from a legalized ABI.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ValueConversion {
    /// Split an integer types into low and high parts, using `isplit`.
    IntSplit,

    /// Split a vector type into halves with identical lane types, using `vsplit`.
    VectorSplit,

    /// Bit-cast to an integer type of the same size.
    IntBits,

    /// Sign-extend integer value to the required type.
    Sext(Type),

    /// Unsigned zero-extend value to the required type.
    Uext(Type),
}

impl ValueConversion {
    /// Apply this conversion to a type, return the converted type.
    pub fn apply(self, ty: Type) -> Type {
        match self {
            ValueConversion::IntSplit => ty.half_width().expect("Integer type too small to split"),
            ValueConversion::VectorSplit => ty.half_vector().expect("Not a vector"),
            ValueConversion::IntBits => Type::int(ty.bits()).expect("Bad integer size"),
            ValueConversion::Sext(nty) | ValueConversion::Uext(nty) => nty,
        }
    }

    /// Is this a split conversion that results in two arguments?
    pub fn is_split(self) -> bool {
        match self {
            ValueConversion::IntSplit | ValueConversion::VectorSplit => true,
            _ => false,
        }
    }
}

/// Common trait for assigning arguments to registers or stack locations.
///
/// This will be implemented by individual ISAs.
pub trait ArgAssigner {
    /// Pick an assignment action for function argument (or return value) `arg`.
    fn assign(&mut self, arg: &AbiParam) -> ArgAction;
}

/// Legalize the arguments in `args` using the given argument assigner.
///
/// This function can be used for both arguments and return values.
pub fn legalize_args<AA: ArgAssigner>(args: &mut Vec<AbiParam>, aa: &mut AA) {
    // Iterate over the arguments.
    // We may need to mutate the vector in place, so don't use a normal iterator, and clone the
    // argument to avoid holding a reference.
    let mut argno = 0;
    while let Some(arg) = args.get(argno).cloned() {
        // Leave the pre-assigned arguments alone.
        // We'll assume that they don't interfere with our assignments.
        if arg.location.is_assigned() {
            argno += 1;
            continue;
        }

        match aa.assign(&arg) {
            // Assign argument to a location and move on to the next one.
            ArgAction::Assign(loc) => {
                args[argno].location = loc;
                argno += 1;
            }
            // Split this argument into two smaller ones. Then revisit both.
            ArgAction::Convert(conv) => {
                let new_arg = AbiParam {
                    value_type: conv.apply(arg.value_type),
                    ..arg
                };
                args[argno].value_type = new_arg.value_type;
                if conv.is_split() {
                    args.insert(argno + 1, new_arg);
                }
            }
        }
    }
}

/// Determine the right action to take when passing a `have` value type to a call signature where
/// the next argument is `arg` which has a different value type.
///
/// The signature legalization process in `legalize_args` above can replace a single argument value
/// with multiple arguments of smaller types. It can also change the type of an integer argument to
/// a larger integer type, requiring the smaller value to be sign- or zero-extended.
///
/// The legalizer needs to repair the values at all ABI boundaries:
///
/// - Incoming function arguments to the entry EBB.
/// - Function arguments passed to a call.
/// - Return values from a call.
/// - Return values passed to a return instruction.
///
/// The `legalize_abi_value` function helps the legalizer with the process. When the legalizer
/// needs to pass a pre-legalized `have` argument, but the ABI argument `arg` has a different value
/// type, `legalize_abi_value(have, arg)` tells the legalizer how to create the needed value type
/// for the argument.
///
/// It may be necessary to call `legalize_abi_value` more than once for a given argument before the
/// desired argument type appears. This will happen when a vector or integer type needs to be split
/// more than once, for example.
pub fn legalize_abi_value(have: Type, arg: &AbiParam) -> ValueConversion {
    let have_bits = have.bits();
    let arg_bits = arg.value_type.bits();

    match have_bits.cmp(&arg_bits) {
        // We have fewer bits than the ABI argument.
        Ordering::Less => {
            debug_assert!(
                have.is_int() && arg.value_type.is_int(),
                "Can only extend integer values"
            );
            match arg.extension {
                ArgumentExtension::Uext => ValueConversion::Uext(arg.value_type),
                ArgumentExtension::Sext => ValueConversion::Sext(arg.value_type),
                _ => panic!("No argument extension specified"),
            }
        }
        // We have the same number of bits as the argument.
        Ordering::Equal => {
            // This must be an integer vector that is split and then extended.
            debug_assert!(arg.value_type.is_int());
            debug_assert!(have.is_vector(), "expected vector type, got {}", have);
            ValueConversion::VectorSplit
        }
        // We have more bits than the argument.
        Ordering::Greater => {
            if have.is_vector() {
                ValueConversion::VectorSplit
            } else if have.is_float() {
                // Convert a float to int so it can be split the next time.
                // ARM would do this to pass an `f64` in two registers.
                ValueConversion::IntBits
            } else {
                ValueConversion::IntSplit
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use ir::types;
    use ir::AbiParam;

    #[test]
    fn legalize() {
        let mut arg = AbiParam::new(types::I32);

        assert_eq!(
            legalize_abi_value(types::I64X2, &arg),
            ValueConversion::VectorSplit
        );
        assert_eq!(
            legalize_abi_value(types::I64, &arg),
            ValueConversion::IntSplit
        );

        // Vector of integers is broken down, then sign-extended.
        arg.extension = ArgumentExtension::Sext;
        assert_eq!(
            legalize_abi_value(types::I16X4, &arg),
            ValueConversion::VectorSplit
        );
        assert_eq!(
            legalize_abi_value(types::I16.by(2).unwrap(), &arg),
            ValueConversion::VectorSplit
        );
        assert_eq!(
            legalize_abi_value(types::I16, &arg),
            ValueConversion::Sext(types::I32)
        );

        // 64-bit float is split as an integer.
        assert_eq!(
            legalize_abi_value(types::F64, &arg),
            ValueConversion::IntBits
        );
    }
}
<commit_msg>Minor code cleanup.<commit_after>//! Common helper code for ABI lowering.
//!
//! This module provides functions and data structures that are useful for implementing the
//! `TargetIsa::legalize_signature()` method.

use ir::{AbiParam, ArgumentExtension, ArgumentLoc, Type};
use std::cmp::Ordering;
use std::vec::Vec;

/// Legalization action to perform on a single argument or return value when converting a
/// signature.
///
/// An argument may go through a sequence of legalization steps before it reaches the final
/// `Assign` action.
#[derive(Clone, Copy, Debug)]
pub enum ArgAction {
    /// Assign the argument to the given location.
    Assign(ArgumentLoc),

    /// Convert the argument, then call again.
    ///
    /// This action can split an integer type into two smaller integer arguments, or it can split a
    /// SIMD vector into halves.
    Convert(ValueConversion),
}

impl From<ArgumentLoc> for ArgAction {
    fn from(x: ArgumentLoc) -> Self {
        ArgAction::Assign(x)
    }
}

impl From<ValueConversion> for ArgAction {
    fn from(x: ValueConversion) -> Self {
        ArgAction::Convert(x)
    }
}

/// Legalization action to be applied to a value that is being passed to or from a legalized ABI.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ValueConversion {
    /// Split an integer types into low and high parts, using `isplit`.
    IntSplit,

    /// Split a vector type into halves with identical lane types, using `vsplit`.
    VectorSplit,

    /// Bit-cast to an integer type of the same size.
    IntBits,

    /// Sign-extend integer value to the required type.
    Sext(Type),

    /// Unsigned zero-extend value to the required type.
    Uext(Type),
}

impl ValueConversion {
    /// Apply this conversion to a type, return the converted type.
    pub fn apply(self, ty: Type) -> Type {
        match self {
            ValueConversion::IntSplit => ty.half_width().expect("Integer type too small to split"),
            ValueConversion::VectorSplit => ty.half_vector().expect("Not a vector"),
            ValueConversion::IntBits => Type::int(ty.bits()).expect("Bad integer size"),
            ValueConversion::Sext(nty) | ValueConversion::Uext(nty) => nty,
        }
    }

    /// Is this a split conversion that results in two arguments?
    pub fn is_split(self) -> bool {
        match self {
            ValueConversion::IntSplit | ValueConversion::VectorSplit => true,
            _ => false,
        }
    }
}

/// Common trait for assigning arguments to registers or stack locations.
///
/// This will be implemented by individual ISAs.
pub trait ArgAssigner {
    /// Pick an assignment action for function argument (or return value) `arg`.
    fn assign(&mut self, arg: &AbiParam) -> ArgAction;
}

/// Legalize the arguments in `args` using the given argument assigner.
///
/// This function can be used for both arguments and return values.
pub fn legalize_args<AA: ArgAssigner>(args: &mut Vec<AbiParam>, aa: &mut AA) {
    // Iterate over the arguments.
    // We may need to mutate the vector in place, so don't use a normal iterator, and clone the
    // argument to avoid holding a reference.
    let mut argno = 0;
    while let Some(arg) = args.get(argno).cloned() {
        // Leave the pre-assigned arguments alone.
        // We'll assume that they don't interfere with our assignments.
        if arg.location.is_assigned() {
            argno += 1;
            continue;
        }

        match aa.assign(&arg) {
            // Assign argument to a location and move on to the next one.
            ArgAction::Assign(loc) => {
                args[argno].location = loc;
                argno += 1;
            }
            // Split this argument into two smaller ones. Then revisit both.
            ArgAction::Convert(conv) => {
                let value_type = conv.apply(arg.value_type);
                let new_arg = AbiParam { value_type, ..arg };
                args[argno].value_type = value_type;
                if conv.is_split() {
                    args.insert(argno + 1, new_arg);
                }
            }
        }
    }
}

/// Determine the right action to take when passing a `have` value type to a call signature where
/// the next argument is `arg` which has a different value type.
///
/// The signature legalization process in `legalize_args` above can replace a single argument value
/// with multiple arguments of smaller types. It can also change the type of an integer argument to
/// a larger integer type, requiring the smaller value to be sign- or zero-extended.
///
/// The legalizer needs to repair the values at all ABI boundaries:
///
/// - Incoming function arguments to the entry EBB.
/// - Function arguments passed to a call.
/// - Return values from a call.
/// - Return values passed to a return instruction.
///
/// The `legalize_abi_value` function helps the legalizer with the process. When the legalizer
/// needs to pass a pre-legalized `have` argument, but the ABI argument `arg` has a different value
/// type, `legalize_abi_value(have, arg)` tells the legalizer how to create the needed value type
/// for the argument.
///
/// It may be necessary to call `legalize_abi_value` more than once for a given argument before the
/// desired argument type appears. This will happen when a vector or integer type needs to be split
/// more than once, for example.
pub fn legalize_abi_value(have: Type, arg: &AbiParam) -> ValueConversion {
    let have_bits = have.bits();
    let arg_bits = arg.value_type.bits();

    match have_bits.cmp(&arg_bits) {
        // We have fewer bits than the ABI argument.
        Ordering::Less => {
            debug_assert!(
                have.is_int() && arg.value_type.is_int(),
                "Can only extend integer values"
            );
            match arg.extension {
                ArgumentExtension::Uext => ValueConversion::Uext(arg.value_type),
                ArgumentExtension::Sext => ValueConversion::Sext(arg.value_type),
                _ => panic!("No argument extension specified"),
            }
        }
        // We have the same number of bits as the argument.
        Ordering::Equal => {
            // This must be an integer vector that is split and then extended.
            debug_assert!(arg.value_type.is_int());
            debug_assert!(have.is_vector(), "expected vector type, got {}", have);
            ValueConversion::VectorSplit
        }
        // We have more bits than the argument.
        Ordering::Greater => {
            if have.is_vector() {
                ValueConversion::VectorSplit
            } else if have.is_float() {
                // Convert a float to int so it can be split the next time.
                // ARM would do this to pass an `f64` in two registers.
                ValueConversion::IntBits
            } else {
                ValueConversion::IntSplit
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use ir::types;
    use ir::AbiParam;

    #[test]
    fn legalize() {
        let mut arg = AbiParam::new(types::I32);

        assert_eq!(
            legalize_abi_value(types::I64X2, &arg),
            ValueConversion::VectorSplit
        );
        assert_eq!(
            legalize_abi_value(types::I64, &arg),
            ValueConversion::IntSplit
        );

        // Vector of integers is broken down, then sign-extended.
        arg.extension = ArgumentExtension::Sext;
        assert_eq!(
            legalize_abi_value(types::I16X4, &arg),
            ValueConversion::VectorSplit
        );
        assert_eq!(
            legalize_abi_value(types::I16.by(2).unwrap(), &arg),
            ValueConversion::VectorSplit
        );
        assert_eq!(
            legalize_abi_value(types::I16, &arg),
            ValueConversion::Sext(types::I32)
        );

        // 64-bit float is split as an integer.
        assert_eq!(
            legalize_abi_value(types::F64, &arg),
            ValueConversion::IntBits
        );
    }
}
<|endoftext|>