<commit_before>use libc::c_char;
use libc::c_void;
use slp::sequence::hlsl_to_cl::hlsl_to_cl;
use slp::sequence::hlsl_to_cl::Input;
use slp::shared::IncludeError;
use slp::shared::IncludeHandler;
use slp::shared::KernelParamSlot;
use std::collections::HashMap;
use std::ffi::CStr;
use std::ffi::CString;
use std::mem;
use std::ptr;
use std::thread;

#[repr(C)]
pub struct slp_hlsl_to_cl_input {
    pub entry_point: *const libc::c_char,
    pub main_file: *const libc::c_char,
    pub main_file_name: *const libc::c_char,
    pub include_handler: extern "C" fn(*mut c_void, *const c_char) -> *const c_char,
    pub user_data: *mut c_void,
    pub kernel_name: *const libc::c_char,
}

#[repr(C)]
pub struct slp_hlsl_to_cl_bind {
    pub hlsl_slot: u16,
    pub cl_slot: u16,
}

#[repr(C)]
pub struct slp_hlsl_to_cl_bind_table {
    pub binds: *mut slp_hlsl_to_cl_bind,
    pub count: u16,
}

impl slp_hlsl_to_cl_bind_table {
    fn empty() -> slp_hlsl_to_cl_bind_table {
        slp_hlsl_to_cl_bind_table {
            binds: std::ptr::null_mut(),
            count: 0,
        }
    }

    unsafe fn new(slots: HashMap<u32, KernelParamSlot>) -> slp_hlsl_to_cl_bind_table {
        let len = slots.len();
        if len == 0 {
            return slp_hlsl_to_cl_bind_table::empty();
        }

        let mut vec = Vec::with_capacity(len);

        for (hlsl_slot, cl_slot) in slots {
            vec.push(slp_hlsl_to_cl_bind {
                hlsl_slot: hlsl_slot as u16,
                cl_slot: cl_slot as u16,
            });
        }

        assert_eq!(vec.capacity(), len);
        assert_eq!(vec.len(), len);
        let res = slp_hlsl_to_cl_bind_table {
            binds: vec.as_mut_ptr(),
            count: len as u16,
        };
        mem::forget(vec);
        res
    }

    unsafe fn free(self) {
        if self.binds != ptr::null_mut() {
            Vec::from_raw_parts(self.binds, self.count as usize, self.count as usize);
        }
    }
}

#[repr(C)]
pub struct slp_hlsl_to_cl_output {
    pub error: *mut libc::c_char,
    pub source: *mut libc::c_char,
    pub kernel_name: *mut libc::c_char,
    pub constant_buffers: slp_hlsl_to_cl_bind_table,
    pub shader_resource_views: slp_hlsl_to_cl_bind_table,
    pub sampler_states: slp_hlsl_to_cl_bind_table,
    pub unordered_access_views: slp_hlsl_to_cl_bind_table,
    pub dim_x: u64,
    pub dim_y: u64,
    pub dim_z: u64,
}

pub struct CIncludeHandler {
    pub include_handler: extern "C" fn(*mut c_void, *const c_char) -> *const c_char,
    pub user_data: *mut c_void,
}

impl IncludeHandler for CIncludeHandler {
    fn load(&mut self, file_name: &str) -> Result<String, IncludeError> {
        let c_name = CString::new(file_name).expect("expect file_name in CIncludeHandler");
        let c_user_data = self.user_data as *mut c_void;
        let data = (self.include_handler)(c_user_data, c_name.as_ptr());
        if data == ptr::null() {
            Err(IncludeError::FileNotFound)
        } else {
            let ret = unsafe { CStr::from_ptr(data).to_string_lossy().into_owned() };
            Ok(ret)
        }
    }
}

unsafe impl Send for CIncludeHandler {}

#[no_mangle]
pub unsafe extern "C" fn slp_hlsl_to_cl(input: slp_hlsl_to_cl_input) -> slp_hlsl_to_cl_output {
    unsafe fn slp_hlsl_to_cl_impl(
        input: slp_hlsl_to_cl_input,
    ) -> Result<slp_hlsl_to_cl_output, String> {
        let entry_point = CStr::from_ptr(input.entry_point)
            .to_string_lossy()
            .into_owned();
        let main_file = CStr::from_ptr(input.main_file)
            .to_string_lossy()
            .into_owned();
        let main_file_name = CStr::from_ptr(input.main_file_name)
            .to_string_lossy()
            .into_owned();
        let kernel_name = CStr::from_ptr(input.kernel_name)
            .to_string_lossy()
            .into_owned();

        let include_handler = CIncludeHandler {
            include_handler: input.include_handler,
            user_data: input.user_data,
        };

        let join_handle_res = thread::Builder::new()
            .stack_size(8 * 1024 * 1024)
            .spawn(move || {
                let input = Input {
                    entry_point: entry_point,
                    file_loader: Box::new(include_handler),
                    main_file: main_file,
                    main_file_name: main_file_name,
                    kernel_name: kernel_name,
                };

                hlsl_to_cl(input)
            });

        let join_handle = match join_handle_res {
            Ok(jh) => jh,
            Err(_) => {
                return Err("Failed to start worker thread".to_string());
            }
        };

        let output_res = match join_handle.join() {
            Ok(output) => output,
            Err(_) => {
                return Err("Failed to join worker thread".to_string());
            }
        };

        match output_res {
            Ok(output) => {
                let source_cstring =
                    CString::new(output.code.to_string()).expect("missing null byte");
                let kernel_name_cstring =
                    CString::new(output.kernel_name).expect("missing null byte");

                Ok(slp_hlsl_to_cl_output {
                    error: ptr::null_mut(),
                    source: source_cstring.into_raw(),
                    kernel_name: kernel_name_cstring.into_raw(),
                    constant_buffers: slp_hlsl_to_cl_bind_table::new(output.binds.cbuffer_map),
                    shader_resource_views: slp_hlsl_to_cl_bind_table::new(output.binds.read_map),
                    sampler_states: slp_hlsl_to_cl_bind_table::new(output.binds.sampler_map),
                    unordered_access_views: slp_hlsl_to_cl_bind_table::new(output.binds.write_map),
                    dim_x: output.dimensions.0,
                    dim_y: output.dimensions.1,
                    dim_z: output.dimensions.2,
                })
            }
            Err(err) => Err(format!("{}", err)),
        }
    }

    match slp_hlsl_to_cl_impl(input) {
        Ok(input) => input,
        Err(cstring) => {
            let error_cstring = CString::new(cstring).expect("missing null byte");

            slp_hlsl_to_cl_output {
                error: error_cstring.into_raw(),
                source: ptr::null_mut(),
                kernel_name: ptr::null_mut(),
                constant_buffers: slp_hlsl_to_cl_bind_table::empty(),
                shader_resource_views: slp_hlsl_to_cl_bind_table::empty(),
                sampler_states: slp_hlsl_to_cl_bind_table::empty(),
                unordered_access_views: slp_hlsl_to_cl_bind_table::empty(),
                dim_x: 0,
                dim_y: 0,
                dim_z: 0,
            }
        }
    }
}

#[no_mangle]
pub unsafe extern "C" fn slp_hlsl_to_cl_free(free: slp_hlsl_to_cl_output) {
    if free.error != ptr::null_mut() {
        CString::from_raw(free.error);
    }
    if free.source != ptr::null_mut() {
        CString::from_raw(free.source);
    }
    if free.kernel_name != ptr::null_mut() {
        CString::from_raw(free.kernel_name);
    }
    free.constant_buffers.free();
    free.shader_resource_views.free();
    free.sampler_states.free();
    free.unordered_access_views.free();
}
<commit_msg>Fix warnings in capi<commit_after>use libc::c_char;
use libc::c_void;
use slp::sequence::hlsl_to_cl::hlsl_to_cl;
use slp::sequence::hlsl_to_cl::Input;
use slp::shared::IncludeError;
use slp::shared::IncludeHandler;
use slp::shared::KernelParamSlot;
use std::collections::HashMap;
use std::ffi::CStr;
use std::ffi::CString;
use std::mem;
use std::ptr;
use std::thread;

#[repr(C)]
pub struct slp_hlsl_to_cl_input {
    pub entry_point: *const libc::c_char,
    pub main_file: *const libc::c_char,
    pub main_file_name: *const libc::c_char,
    pub include_handler: extern "C" fn(*mut c_void, *const c_char) -> *const c_char,
    pub user_data: *mut c_void,
    pub kernel_name: *const libc::c_char,
}

#[repr(C)]
pub struct slp_hlsl_to_cl_bind {
    pub hlsl_slot: u16,
    pub cl_slot: u16,
}

#[repr(C)]
pub struct slp_hlsl_to_cl_bind_table {
    pub binds: *mut slp_hlsl_to_cl_bind,
    pub count: u16,
}

impl slp_hlsl_to_cl_bind_table {
    fn empty() -> slp_hlsl_to_cl_bind_table {
        slp_hlsl_to_cl_bind_table {
            binds: std::ptr::null_mut(),
            count: 0,
        }
    }

    unsafe fn new(slots: HashMap<u32, KernelParamSlot>) -> slp_hlsl_to_cl_bind_table {
        let len = slots.len();
        if len == 0 {
            return slp_hlsl_to_cl_bind_table::empty();
        }

        let mut vec = Vec::with_capacity(len);

        for (hlsl_slot, cl_slot) in slots {
            vec.push(slp_hlsl_to_cl_bind {
                hlsl_slot: hlsl_slot as u16,
                cl_slot: cl_slot as u16,
            });
        }

        assert_eq!(vec.capacity(), len);
        assert_eq!(vec.len(), len);
        let res = slp_hlsl_to_cl_bind_table {
            binds: vec.as_mut_ptr(),
            count: len as u16,
        };
        mem::forget(vec);
        res
    }

    unsafe fn free(self) {
        if self.binds != ptr::null_mut() {
            Vec::from_raw_parts(self.binds, self.count as usize, self.count as usize);
        }
    }
}

#[repr(C)]
pub struct slp_hlsl_to_cl_output {
    pub error: *mut libc::c_char,
    pub source: *mut libc::c_char,
    pub kernel_name: *mut libc::c_char,
    pub constant_buffers: slp_hlsl_to_cl_bind_table,
    pub shader_resource_views: slp_hlsl_to_cl_bind_table,
    pub sampler_states: slp_hlsl_to_cl_bind_table,
    pub unordered_access_views: slp_hlsl_to_cl_bind_table,
    pub dim_x: u64,
    pub dim_y: u64,
    pub dim_z: u64,
}

pub struct CIncludeHandler {
    pub include_handler: extern "C" fn(*mut c_void, *const c_char) -> *const c_char,
    pub user_data: *mut c_void,
}

impl IncludeHandler for CIncludeHandler {
    fn load(&mut self, file_name: &str) -> Result<String, IncludeError> {
        let c_name = CString::new(file_name).expect("expect file_name in CIncludeHandler");
        let c_user_data = self.user_data as *mut c_void;
        let data = (self.include_handler)(c_user_data, c_name.as_ptr());
        if data == ptr::null() {
            Err(IncludeError::FileNotFound)
        } else {
            let ret = unsafe { CStr::from_ptr(data).to_string_lossy().into_owned() };
            Ok(ret)
        }
    }
}

unsafe impl Send for CIncludeHandler {}

#[no_mangle]
pub unsafe extern "C" fn slp_hlsl_to_cl(input: slp_hlsl_to_cl_input) -> slp_hlsl_to_cl_output {
    unsafe fn slp_hlsl_to_cl_impl(
        input: slp_hlsl_to_cl_input,
    ) -> Result<slp_hlsl_to_cl_output, String> {
        let entry_point = CStr::from_ptr(input.entry_point)
            .to_string_lossy()
            .into_owned();
        let main_file = CStr::from_ptr(input.main_file)
            .to_string_lossy()
            .into_owned();
        let main_file_name = CStr::from_ptr(input.main_file_name)
            .to_string_lossy()
            .into_owned();
        let kernel_name = CStr::from_ptr(input.kernel_name)
            .to_string_lossy()
            .into_owned();

        let include_handler = CIncludeHandler {
            include_handler: input.include_handler,
            user_data: input.user_data,
        };

        let join_handle_res = thread::Builder::new()
            .stack_size(8 * 1024 * 1024)
            .spawn(move || {
                let input = Input {
                    entry_point: entry_point,
                    file_loader: Box::new(include_handler),
                    main_file: main_file,
                    main_file_name: main_file_name,
                    kernel_name: kernel_name,
                };

                hlsl_to_cl(input)
            });

        let join_handle = match join_handle_res {
            Ok(jh) => jh,
            Err(_) => {
                return Err("Failed to start worker thread".to_string());
            }
        };

        let output_res = match join_handle.join() {
            Ok(output) => output,
            Err(_) => {
                return Err("Failed to join worker thread".to_string());
            }
        };

        match output_res {
            Ok(output) => {
                let source_cstring =
                    CString::new(output.code.to_string()).expect("missing null byte");
                let kernel_name_cstring =
                    CString::new(output.kernel_name).expect("missing null byte");

                Ok(slp_hlsl_to_cl_output {
                    error: ptr::null_mut(),
                    source: source_cstring.into_raw(),
                    kernel_name: kernel_name_cstring.into_raw(),
                    constant_buffers: slp_hlsl_to_cl_bind_table::new(output.binds.cbuffer_map),
                    shader_resource_views: slp_hlsl_to_cl_bind_table::new(output.binds.read_map),
                    sampler_states: slp_hlsl_to_cl_bind_table::new(output.binds.sampler_map),
                    unordered_access_views: slp_hlsl_to_cl_bind_table::new(output.binds.write_map),
                    dim_x: output.dimensions.0,
                    dim_y: output.dimensions.1,
                    dim_z: output.dimensions.2,
                })
            }
            Err(err) => Err(format!("{}", err)),
        }
    }

    match slp_hlsl_to_cl_impl(input) {
        Ok(input) => input,
        Err(cstring) => {
            let error_cstring = CString::new(cstring).expect("missing null byte");

            slp_hlsl_to_cl_output {
                error: error_cstring.into_raw(),
                source: ptr::null_mut(),
                kernel_name: ptr::null_mut(),
                constant_buffers: slp_hlsl_to_cl_bind_table::empty(),
                shader_resource_views: slp_hlsl_to_cl_bind_table::empty(),
                sampler_states: slp_hlsl_to_cl_bind_table::empty(),
                unordered_access_views: slp_hlsl_to_cl_bind_table::empty(),
                dim_x: 0,
                dim_y: 0,
                dim_z: 0,
            }
        }
    }
}

#[no_mangle]
pub unsafe extern "C" fn slp_hlsl_to_cl_free(free: slp_hlsl_to_cl_output) {
    if free.error != ptr::null_mut() {
        let _ = CString::from_raw(free.error);
    }
    if free.source != ptr::null_mut() {
        let _ = CString::from_raw(free.source);
    }
    if free.kernel_name != ptr::null_mut() {
        let _ = CString::from_raw(free.kernel_name);
    }
    free.constant_buffers.free();
    free.shader_resource_views.free();
    free.sampler_states.free();
    free.unordered_access_views.free();
}
<|endoftext|>


<commit_before>use path_builder::*;

extern crate cairo;

fn parse_path (path_str: &str) -> RsvgPathBuilder {
    let builder = RsvgPathBuilder::new ();

    builder
}

#[cfg(test)]
mod tests {
    use super::*;
    use path_builder::*;
    extern crate cairo;

    fn path_segment_vectors_are_equal (a: &Vec<cairo::PathSegment>,
                                       b: &Vec<cairo::PathSegment>) -> bool {
        if a.len () == 0 && b.len () == 0 {
            return true;
        }

        let mut iter = a.iter().zip (b);

        loop {
            if let Some ((seg1, seg2)) = iter.next () {
                match *seg1 {
                    cairo::PathSegment::MoveTo ((x, y)) => {
                        if let cairo::PathSegment::MoveTo ((ox, oy)) = *seg2 { return (x, y) == (ox, oy); }
                    },

                    cairo::PathSegment::LineTo ((x, y)) => {
                        if let cairo::PathSegment::LineTo ((ox, oy)) = *seg2 { return (x, y) == (ox, oy); }
                    },

                    cairo::PathSegment::CurveTo ((x2, y2), (x3, y3), (x4, y4)) => {
                        if let cairo::PathSegment::CurveTo ((ox2, oy2), (ox3, oy3), (ox4, oy4)) = *seg2 {
                            return (ox2, oy2, ox3, oy3, ox4, oy4) == (x2, y2, x3, y3, x4, y4);
                        }
                    },

                    cairo::PathSegment::ClosePath => {
                        if let cairo::PathSegment::ClosePath = *seg2 { return true; }
                    }
                }
            } else {
                return false;
            }
        }
    }

    #[test]
    fn path_parser_handles_empty_data () {
        let builder = super::parse_path ("");
        let segments = builder.get_path_segments ();
        let expected_segments = Vec::<cairo::PathSegment>::new ();

        assert! (path_segment_vectors_are_equal (&expected_segments, segments));
    }
}
<commit_msg>path_parser.rs: Start implementing a recursive descent parser<commit_after>use std::str;
use std::str::Chars;
use std::iter::Enumerate;
use path_builder::*;

extern crate cairo;

struct Parser<'external> {
    path_str: &'external str,
    chars_enumerator: Enumerate<Chars<'external>>,
    lookahead: Option <char>, /* None if we are in EOF */
    current_pos: usize,

    builder: &'external mut RsvgPathBuilder,

    error_message: &'static str,
    has_error: bool,

    current_x: f64,
    current_y: f64
}

impl<'external> Parser<'external> {
    pub fn new (builder: &'external mut RsvgPathBuilder, path_str: &'external str) -> Parser<'external> {
        Parser {
            path_str: path_str,
            chars_enumerator: path_str.chars ().enumerate (),
            lookahead: None,
            current_pos: 0,

            builder: builder,

            error_message: "",
            has_error: false,

            current_x: 0.0,
            current_y: 0.0
        }
    }

    pub fn parse (&mut self) -> bool {
        self.getchar ();

        return self.optional_whitespace () &&
            self.moveto_drawto_command_groups () &&
            self.optional_whitespace ();
    }

    fn getchar (&mut self) {
        if let Some ((pos, c)) = self.chars_enumerator.next () {
            self.lookahead = Some (c);
            self.current_pos = pos;
        } else {
            self.lookahead = None;
            self.current_pos += 1; /* this is EOF; point just past the end the string */
        }
    }

    fn error (&mut self, message: &'static str) -> bool {
        self.error_message = message;
        self.has_error = true;
        false
    }

    fn match_char (&mut self, c: char) -> bool {
        if let Some (x) = self.lookahead {
            if c == x {
                self.getchar ();
                return true;
            }
        }

        false
    }

    fn whitespace (&mut self) -> bool {
        if let Some (c) = self.lookahead {
            if c.is_whitespace () {
                assert! (self.match_char (c));

                while let Some (c) = self.lookahead {
                    if c.is_whitespace () {
                        assert! (self.match_char (c));
                        continue;
                    } else {
                        break;
                    }
                }

                return true;
            } else {
                return false;
            }
        }

        false
    }

    fn optional_whitespace (&mut self) -> bool {
        self.whitespace ();
        true
    }

    fn optional_comma_whitespace (&mut self) -> bool {
        assert! (self.optional_whitespace ());
        if self.match_char (',') {
            self.optional_whitespace ()
        } else {
            true
        }
    }

    fn lookahead_is_digit (&self, d: &mut char) -> bool {
        if let Some (c) = self.lookahead {
            if c.is_digit (10) {
                *d = c;
                return true;
            }
        }

        false
    }

    fn number (&mut self) -> Option <f64> {
        let mut has_sign: bool;
        let mut value: f64;
        let mut sign: f64;
        let mut exponent_sign: f64;
        let mut exponent: f64;

        has_sign = false;
        sign = 1.0;
        value = 0.0;
        exponent_sign = 1.0;
        exponent = 0.0;

        if self.match_char ('+') {
            sign = 1.0;
            has_sign = true;
        } else if self.match_char ('-') {
            sign = -1.0;
            has_sign = true;
        }

        let mut c: char = ' ';

        if self.lookahead_is_digit (&mut c) {
            /* Integer part */

            while self.lookahead_is_digit (&mut c) {
                value = value * 10.0 + char_to_digit (c) as f64;

                assert! (self.match_char (c));
            }

            /* Fractional part */

            if self.match_char ('.') {
                let mut fraction: f64 = 1.0;

                let mut c: char = ' ';

                while self.lookahead_is_digit (&mut c) {
                    fraction = fraction / 10.0;
                    value += fraction * char_to_digit (c) as f64;

                    assert! (self.match_char (c));
                }
            }

            if self.match_char ('E') || self.match_char ('e') {
                /* exponent sign */

                if self.match_char ('+') {
                    exponent_sign = 1.0;
                } else if self.match_char ('-') {
                    exponent_sign = -1.0;
                }

                /* exponent */

                let mut c: char = ' ';

                if self.lookahead_is_digit (&mut c) {
                    while self.lookahead_is_digit (&mut c) {
                        exponent = exponent * 10.0 + char_to_digit (c) as f64;

                        assert! (self.match_char (c));
                    }
                } else {
                    self.error ("Expected digits for exponent");
                    return None;
                }
            }

            Some (value * sign * 10.0f64.powf (exponent * exponent_sign))
        } else {
            if has_sign {
                self.error ("Expected number after sign");
            }

            None
        }
    }

    fn coordinate_pair (&mut self) -> Option<(f64, f64)> {
        if let Some (num1) = self.number () {
            assert! (self.optional_comma_whitespace ());

            if let Some (num2) = self.number () {
                return Some ((num1, num2));
            } else {
                self.error ("Expected second coordinate of coordinate pair");
                return None
            }
        }

        None
    }

    fn lookahead_is (&self, c: char) -> bool {
        if let Some (x) = self.lookahead {
            if x == c {
                return true;
            }
        }

        false
    }

    fn emit_line_to (&mut self, absolute: bool, x: f64, y: f64) {
        if absolute {
            self.current_x = x;
            self.current_y = y;
        } else {
            self.current_x += x;
            self.current_y += y;
        }

        self.builder.line_to (self.current_x, self.current_y);
        println! ("emitting lineto {} {}", self.current_x, self.current_y);
    }

    fn lineto_argument_sequence (&mut self, absolute: bool) -> bool {
        if let Some ((x, y)) = self.coordinate_pair () {
            self.emit_line_to (absolute, x, y);

            self.whitespace ();

            if self.lookahead_is (',') {
                assert! (self.match_char (','));
                assert! (self.optional_whitespace ());

                if !self.lineto_argument_sequence (absolute) {
                    self.error ("Expected coordinate pair after comma");
                    return false;
                }
            }

            self.lineto_argument_sequence (absolute);
            true
        } else {
            false
        }
    }

    fn emit_move_to (&mut self, absolute: bool, x: f64, y: f64) {
        if absolute {
            self.current_x = x;
            self.current_y = y;
        } else {
            self.current_x += x;
            self.current_y += y;
        }

        self.builder.move_to (self.current_x, self.current_y);
        println! ("emitting moveto {} {}", self.current_x, self.current_y);
    }

    fn moveto_argument_sequence (&mut self, absolute: bool, is_initial_moveto: bool) -> bool {
        if let Some ((x, y)) = self.coordinate_pair () {
            if is_initial_moveto {
                self.emit_move_to (true, x, y);
            } else {
                self.emit_move_to (absolute, x, y);
            }

            self.whitespace ();

            if self.lookahead_is (',') {
                assert! (self.match_char (','));
                assert! (self.optional_whitespace ());

                if !self.lineto_argument_sequence (absolute) {
                    self.error ("Expected coordinate pair after comma");
                    return false;
                }
            }

            self.lineto_argument_sequence (absolute);
            true
        } else {
            self.error ("Expected coordinate pair after moveto")
        }
    }

    fn moveto (&mut self, is_initial_moveto: bool) -> bool {
        if self.lookahead_is ('M') || self.lookahead_is ('m') {
            let absolute: bool;

            if self.match_char ('M') {
                absolute = true;
            } else {
                assert! (self.match_char ('m'));
                absolute = false;
            }

            return self.optional_whitespace () &&
                self.moveto_argument_sequence (absolute, is_initial_moveto);
        }

        false
    }

    fn moveto_drawto_command_group (&mut self, is_initial_moveto: bool) -> bool {
        if self.moveto (is_initial_moveto) {
            return self.optional_whitespace () &&
                true; // FIXME self.optional_drawto_commands ();
        } else {
            false
        }
    }

    fn moveto_drawto_command_groups (&mut self) -> bool {
        if self.moveto_drawto_command_group (true) {
            loop {
                self.optional_whitespace ();
                if !self.moveto_drawto_command_group (false) {
                    break;
                }
            }

            true
        } else {
            self.error ("Expected moveto command")
        }
    }
}

fn char_to_digit (c: char) -> i32 {
    c as i32 - '0' as i32
}

fn print_error (parser: &Parser) {
    let prefix = "Error in \"";

    println! ("");

    println! ("{}{}\"", prefix, &parser.path_str);

    for _ in 0 .. (prefix.len() + parser.current_pos) {
        print! (" ");
    }

    println! ("^ pos {}", parser.current_pos);
    println! ("{}", &parser.error_message);
}

fn parse_path (path_str: &str) -> RsvgPathBuilder {
    let mut builder = RsvgPathBuilder::new ();

    {
        let mut parser = Parser::new (&mut builder, path_str);
        if parser.parse () {
            /* all okay */
        } else {
            print_error (&parser);

            /* FIXME: we aren't returning errors at all.  Just return the
             * parsed path up to here, per the spec.
             */
        }
    }

    builder
}

#[cfg(test)]
mod tests {
    extern crate cairo;

    fn path_segment_vectors_are_equal (a: &Vec<cairo::PathSegment>,
                                       b: &Vec<cairo::PathSegment>) -> bool {

        if a.len() != b.len () {
            return false;
        }

        if a.len () == 0 && b.len () == 0 {
            return true;
        }

        let mut iter = a.iter().zip (b);

        loop {
            if let Some ((seg1, seg2)) = iter.next () {
                match *seg1 {
                    cairo::PathSegment::MoveTo ((x, y)) => {
                        if let cairo::PathSegment::MoveTo ((ox, oy)) = *seg2 {
                            println! ("{} {} {} {}", x, y, ox, oy);
                            if (x, y) != (ox, oy) {
                                return false;
                            }
                        } else {
                            return false;
                        }
                    },

                    cairo::PathSegment::LineTo ((x, y)) => {
                        if let cairo::PathSegment::LineTo ((ox, oy)) = *seg2 {
                            println! ("{} {} {} {}", x, y, ox, oy);
                            if (x, y) != (ox, oy) {
                                return false;
                            }
                        } else {
                            return false;
                        }
                    },

                    cairo::PathSegment::CurveTo ((x2, y2), (x3, y3), (x4, y4)) => {
                        if let cairo::PathSegment::CurveTo ((ox2, oy2), (ox3, oy3), (ox4, oy4)) = *seg2 {
                            if (ox2, oy2, ox3, oy3, ox4, oy4) != (x2, y2, x3, y3, x4, y4) {
                                return false;
                            }
                        } else {
                            return false;
                        }
                    },

                    cairo::PathSegment::ClosePath => {
                        if let cairo::PathSegment::ClosePath = *seg2 {
                            /* okay */
                        } else {
                            return false;
                        }
                    }
                }
            } else {
                break;
            }
        }

        true
    }

    fn test_parser (path_str: &str,
                    expected_segments: &Vec<cairo::PathSegment>) {
        let builder = super::parse_path (path_str);
        let segments = builder.get_path_segments ();

        assert! (path_segment_vectors_are_equal (expected_segments, segments));
    }

    fn moveto (x: f64, y: f64) -> cairo::PathSegment {
        cairo::PathSegment::MoveTo ((x, y))
    }

    fn lineto (x: f64, y: f64) -> cairo::PathSegment {
        cairo::PathSegment::LineTo ((x, y))
    }

    fn curveto (x2: f64, y2: f64, x3: f64, y3: f64, x4: f64, y4: f64) -> cairo::PathSegment {
        cairo::PathSegment::CurveTo ((x2, y2), (x3, y3), (x4, y4))
    }

    fn closepath () -> cairo::PathSegment {
        cairo::PathSegment::ClosePath
    }

    #[test]
    fn path_parser_handles_empty_data () {
        test_parser ("",
                     &Vec::<cairo::PathSegment>::new ());
    }

    #[test]
    fn path_parser_handles_single_moveto () {
        test_parser ("M 10 20",
                     &vec![
                         moveto (10.0, 20.0)
                     ]);

        test_parser ("M10,20",
                     &vec![
                         moveto (10.0, 20.0)
                     ]);

        test_parser ("M10 20",
                     &vec![
                         moveto (10.0, 20.0)
                     ]);

        test_parser ("    M10,20     ",
                     &vec![
                         moveto (10.0, 20.0)
                     ]);
    }

    #[test]
    fn path_parser_handles_relative_moveto () {
        test_parser ("m10 20",
                     &vec![
                         moveto (10.0, 20.0)
                     ]);
    }

    #[test]
    fn path_parser_handles_absolute_moveto_with_implicit_linetos () {
        test_parser ("M10 20 30 40",
                     &vec![
                         moveto (10.0, 20.0),
                         lineto (30.0, 40.0)
                     ]);
    }

    #[test]
    fn path_parser_handles_relative_moveto_with_implicit_linetos () {
        test_parser ("m10 20 30 40",
                     &vec![
                         moveto (10.0, 20.0),
                         lineto (40.0, 60.0)
                     ]);
    }
}
<|endoftext|>


<commit_before>extern crate ratelimit_meter;
#[macro_use]
extern crate nonzero_ext;

use ratelimit_meter::{DirectRateLimiter, LeakyBucket, NegativeMultiDecision, NonConformance};
use std::thread;
use std::time::{Duration, Instant};

#[test]
fn accepts_first_cell() {
    let mut lb = DirectRateLimiter::<LeakyBucket>::per_second(nonzero!(5u32));
    assert_eq!(Ok(()), lb.check());
}

#[test]
fn rejects_too_many() {
    let mut lb = DirectRateLimiter::<LeakyBucket>::per_second(nonzero!(2u32));
    let now = Instant::now();
    let ms = Duration::from_millis(1);
    assert_eq!(Ok(()), lb.check_at(now));
    assert_eq!(Ok(()), lb.check_at(now));
    assert!(!lb.check_at(now + ms * 2).is_ok());
    // should be ok again in 1s:
    let next = now + Duration::from_millis(1002);
    assert_eq!(Ok(()), lb.check_at(next));
    assert_eq!(Ok(()), lb.check_at(next + ms));
    assert!(!lb.check_at(next + ms * 2).is_ok(), "{:?}", lb);
}

#[test]
fn never_allows_more_than_capacity() {
    let mut lb = DirectRateLimiter::<LeakyBucket>::per_second(nonzero!(5u32));
    let now = Instant::now();
    let ms = Duration::from_millis(1);

    // Should not allow the first 15 cells on a capacity 5 bucket:
    assert!(lb.check_n_at(15, now).is_err());

    // After 3 and 20 seconds, it should not allow 15 on that bucket either:
    assert!(lb.check_n_at(15, now + (ms * 3 * 1000)).is_err());

    let result = lb.check_n_at(15, now + (ms * 20 * 1000));
    match result {
        Err(NegativeMultiDecision::InsufficientCapacity(n)) => assert_eq!(n, 15),
        _ => panic!("Did not expect {:?}", result),
    }
}

#[test]
fn correct_wait_time() {
    // Bucket adding a new element per 200ms:
    let mut lb = DirectRateLimiter::<LeakyBucket>::per_second(nonzero!(5u32));
    let mut now = Instant::now();
    let ms = Duration::from_millis(1);
    let mut conforming = 0;
    for _i in 0..20 {
        now += ms;
        let res = lb.check_at(now);
        match res {
            Ok(()) => {
                conforming += 1;
            }
            Err(wait) => {
                now += wait.wait_time_from(now);
                assert_eq!(Ok(()), lb.check_at(now));
                conforming += 1;
            }
        }
    }
    assert_eq!(20, conforming);
}

#[test]
fn prevents_time_travel() {
    let mut lb = DirectRateLimiter::<LeakyBucket>::per_second(nonzero!(5u32));
    let now = Instant::now();
    let ms = Duration::from_millis(1);

    assert!(lb.check_at(now).is_ok());
    assert!(lb.check_at(now - ms).is_ok());
    assert!(lb.check_at(now - ms * 500).is_ok());
}

#[test]
fn actual_threadsafety() {
    let mut lim = DirectRateLimiter::<LeakyBucket>::per_second(nonzero!(20u32));
    let now = Instant::now();
    let ms = Duration::from_millis(1);
    let mut children = vec![];

    lim.check_at(now).unwrap();
    for _i in 0..20 {
        let mut lim = lim.clone();
        children.push(thread::spawn(move || lim.check_at(now).is_ok()));
    }
    for child in children {
        child.join().unwrap();
    }
    assert!(!lim.check_at(now + ms * 2).is_ok());
    assert_eq!(Ok(()), lim.check_at(now + ms * 1000));
}
<commit_msg>Preliminary leaky bucket test refactoring<commit_after>extern crate ratelimit_meter;
#[macro_use]
extern crate nonzero_ext;

use ratelimit_meter::{DirectRateLimiter, LeakyBucket, NegativeMultiDecision, NonConformance};
use std::thread;
use std::time::{Duration, Instant};

#[test]
fn accepts_first_cell() {
    let mut lb = DirectRateLimiter::<LeakyBucket>::per_second(nonzero!(5u32));
    assert_eq!(Ok(()), lb.check_at(Instant::now()));
}

#[test]
fn rejects_too_many() {
    let mut lb = DirectRateLimiter::<LeakyBucket>::per_second(nonzero!(2u32));
    let now = Instant::now();
    let ms = Duration::from_millis(1);
    assert_eq!(Ok(()), lb.check_at(now));
    assert_eq!(Ok(()), lb.check_at(now));

    assert_ne!(Ok(()), lb.check_at(now + ms * 2));

    // should be ok again in 1s:
    let next = now + Duration::from_millis(1002);
    assert_eq!(Ok(()), lb.check_at(next));
    assert_eq!(Ok(()), lb.check_at(next + ms));

    assert_ne!(Ok(()), lb.check_at(next + ms * 2), "{:?}", lb);
}

#[test]
fn never_allows_more_than_capacity() {
    let mut lb = DirectRateLimiter::<LeakyBucket>::per_second(nonzero!(5u32));
    let now = Instant::now();
    let ms = Duration::from_millis(1);

    // Should not allow the first 15 cells on a capacity 5 bucket:
    assert_ne!(Ok(()), lb.check_n_at(15, now));

    // After 3 and 20 seconds, it should not allow 15 on that bucket either:
    assert_ne!(Ok(()), lb.check_n_at(15, now + (ms * 3 * 1000)));
    let result = lb.check_n_at(15, now + (ms * 20 * 1000));
    match result {
        Err(NegativeMultiDecision::InsufficientCapacity(n)) => assert_eq!(n, 15),
        _ => panic!("Did not expect {:?}", result),
    }
}

#[test]
fn correct_wait_time() {
    // Bucket adding a new element per 200ms:
    let mut lb = DirectRateLimiter::<LeakyBucket>::per_second(nonzero!(5u32));
    let mut now = Instant::now();
    let ms = Duration::from_millis(1);
    let mut conforming = 0;
    for _i in 0..20 {
        now += ms;
        let res = lb.check_at(now);
        match res {
            Ok(()) => {
                conforming += 1;
            }
            Err(wait) => {
                now += wait.wait_time_from(now);
                assert_eq!(Ok(()), lb.check_at(now));
                conforming += 1;
            }
        }
    }
    assert_eq!(20, conforming);
}

#[test]
fn prevents_time_travel() {
    let mut lb = DirectRateLimiter::<LeakyBucket>::per_second(nonzero!(5u32));
    let now = Instant::now();
    let ms = Duration::from_millis(1);

    assert!(lb.check_at(now).is_ok());
    assert!(lb.check_at(now - ms).is_ok());
    assert!(lb.check_at(now - ms * 500).is_ok());
}

#[test]
fn actual_threadsafety() {
    let mut lim = DirectRateLimiter::<LeakyBucket>::per_second(nonzero!(20u32));
    let now = Instant::now();
    let ms = Duration::from_millis(1);
    let mut children = vec![];

    lim.check_at(now).unwrap();
    for _i in 0..20 {
        let mut lim = lim.clone();
        children.push(thread::spawn(move || lim.check_at(now).is_ok()));
    }
    for child in children {
        child.join().unwrap();
    }
    assert!(!lim.check_at(now + ms * 2).is_ok());
    assert_eq!(Ok(()), lim.check_at(now + ms * 1000));
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! The style bloom filter is used as an optimization when matching deep
//! descendant selectors.

#![deny(missing_docs)]

use dom::{SendElement, TElement};
use selectors::bloom::BloomFilter;
use smallvec::SmallVec;

/// A struct that allows us to fast-reject deep descendant selectors avoiding
/// selector-matching.
///
/// This is implemented using a counting bloom filter, and it's a standard
/// optimization. See Gecko's `AncestorFilter`, and Blink's and WebKit's
/// `SelectorFilter`.
///
/// The constraints for Servo's style system are a bit different compared to
/// traditional style systems given Servo does a parallel breadth-first
/// traversal instead of a sequential depth-first traversal.
///
/// This implies that we need to track a bit more state than other browsers to
/// ensure we're doing the correct thing during the traversal, and being able to
/// apply this optimization effectively.
///
/// Concretely, we have a bloom filter instance per worker thread, and we track
/// the current DOM depth in order to find a common ancestor when it doesn't
/// match the previous element we've styled.
///
/// This is usually a pretty fast operation (we use to be one level deeper than
/// the previous one), but in the case of work-stealing, we may needed to push
/// and pop multiple elements.
///
/// See the `insert_parents_recovering`, where most of the magic happens.
///
/// Regarding thread-safety, this struct is safe because:
///
///  * We clear this after a restyle.
///  * The DOM shape and attributes (and every other thing we access here) are
///    immutable during a restyle.
///
pub struct StyleBloom<E: TElement> {
    /// The bloom filter per se.
    filter: Box<BloomFilter>,

    /// The stack of elements that this bloom filter contains.
    elements: Vec<SendElement<E>>,
}

fn each_relevant_element_hash<E, F>(element: E, mut f: F)
    where E: TElement,
          F: FnMut(u32),
{
    f(element.get_local_name().get_hash());
    f(element.get_namespace().get_hash());

    if let Some(id) = element.get_id() {
        f(id.get_hash());
    }

    // TODO: case-sensitivity depends on the document type and quirks mode.
    //
    // TODO(emilio): It's not clear whether that's relevant here though?
    // Classes and ids should be normalized already I think.
    element.each_class(|class| {
        f(class.get_hash())
    });
}

impl<E: TElement> StyleBloom<E> {
    /// Create an empty `StyleBloom`.
    pub fn new() -> Self {
        StyleBloom {
            filter: Box::new(BloomFilter::new()),
            elements: vec![],
        }
    }

    /// Return the bloom filter used properly by the `selectors` crate.
    pub fn filter(&self) -> &BloomFilter {
        &*self.filter
    }

    /// Push an element to the bloom filter, knowing that it's a child of the
    /// last element parent.
    pub fn push(&mut self, element: E) {
        if cfg!(debug_assertions) {
            if self.elements.is_empty() {
                assert!(element.traversal_parent().is_none());
            }
        }
        self.push_internal(element);
    }

    /// Same as `push`, but without asserting, in order to use it from
    /// `rebuild`.
    fn push_internal(&mut self, element: E) {
        each_relevant_element_hash(element, |hash| {
            self.filter.insert_hash(hash);
        });
        self.elements.push(unsafe { SendElement::new(element) });
    }

    /// Pop the last element in the bloom filter and return it.
    fn pop(&mut self) -> Option<E> {
        let popped = self.elements.pop().map(|el| *el);

        if let Some(popped) = popped {
            each_relevant_element_hash(popped, |hash| {
                self.filter.remove_hash(hash);
            })
        }

        popped
    }

    /// Returns true if the bloom filter is empty.
    pub fn is_empty(&self) -> bool {
        self.elements.is_empty()
    }

    /// Returns the DOM depth of elements that can be correctly
    /// matched against the bloom filter (that is, the number of
    /// elements in our list).
    pub fn matching_depth(&self) -> usize {
        self.elements.len()
    }

    /// Clears the bloom filter.
    pub fn clear(&mut self) {
        self.filter.clear();
        self.elements.clear();
    }

    /// Rebuilds the bloom filter up to the parent of the given element.
    pub fn rebuild(&mut self, mut element: E) {
        self.clear();

        while let Some(parent) = element.traversal_parent() {
            self.push_internal(parent);
            element = parent;
        }

        // Put them in the order we expect, from root to `element`'s parent.
        self.elements.reverse();
    }

    /// In debug builds, asserts that all the parents of `element` are in the
    /// bloom filter.
    ///
    /// Goes away in release builds.
    pub fn assert_complete(&self, mut element: E) {
        if cfg!(debug_assertions) {
            let mut checked = 0;
            while let Some(parent) = element.traversal_parent() {
                assert_eq!(parent, *self.elements[self.elements.len() - 1 - checked]);
                element = parent;
                checked += 1;
            }
            assert_eq!(checked, self.elements.len());
        }
    }

    /// Get the element that represents the chain of things inserted
    /// into the filter right now.  That chain is the given element
    /// (if any) and its ancestors.
    #[inline]
    pub fn current_parent(&self) -> Option<E> {
        self.elements.last().map(|el| **el)
    }

    /// Insert the parents of an element in the bloom filter, trying to recover
    /// the filter if the last element inserted doesn't match.
    ///
    /// Gets the element depth in the dom, to make it efficient, or if not
    /// provided always rebuilds the filter from scratch.
    ///
    /// Returns the new bloom filter depth, that the traversal code is
    /// responsible to keep around if it wants to get an effective filter.
    pub fn insert_parents_recovering(&mut self,
                                     element: E,
                                     element_depth: usize)
    {
        // Easy case, we're in a different restyle, or we're empty.
        if self.elements.is_empty() {
            self.rebuild(element);
            return;
        }

        let traversal_parent = match element.traversal_parent() {
            Some(parent) => parent,
            None => {
                // Yay, another easy case.
                self.clear();
                return;
            }
        };

        if self.current_parent() == Some(traversal_parent) {
            // Ta da, cache hit, we're all done.
            return;
        }

        if element_depth == 0 {
            self.clear();
            return;
        }

        // We should've early exited above.
        debug_assert!(element_depth != 0,
                      "We should have already cleared the bloom filter");
        debug_assert!(!self.elements.is_empty(),
                      "How! We should've just rebuilt!");

        // Now the fun begins: We have the depth of the dom and the depth of the
        // last element inserted in the filter, let's try to find a common
        // parent.
        //
        // The current depth, that is, the depth of the last element inserted in
        // the bloom filter, is the number of elements _minus one_, that is: if
        // there's one element, it must be the root -> depth zero.
        let mut current_depth = self.elements.len() - 1;

        // If the filter represents an element too deep in the dom, we need to
        // pop ancestors.
        while current_depth > element_depth - 1 {
            self.pop().expect("Emilio is bad at math");
            current_depth -= 1;
        }

        // Now let's try to find a common parent in the bloom filter chain,
        // starting with traversal_parent.
        let mut common_parent = traversal_parent;
        let mut common_parent_depth = element_depth - 1;

        // Let's collect the parents we are going to need to insert once we've
        // found the common one.
        let mut parents_to_insert = SmallVec::<[E; 8]>::new();

        // If the bloom filter still doesn't have enough elements, the common
        // parent is up in the dom.
        while common_parent_depth > current_depth {
            // TODO(emilio): Seems like we could insert parents here, then
            // reverse the slice.
            parents_to_insert.push(common_parent);
            common_parent =
                common_parent.traversal_parent().expect("We were lied to");
            common_parent_depth -= 1;
        }

        // Now the two depths are the same.
        debug_assert_eq!(common_parent_depth, current_depth);

        // Happy case: The parents match, we only need to push the ancestors
        // we've collected and we'll never enter in this loop.
        //
        // Not-so-happy case: Parent's don't match, so we need to keep going up
        // until we find a common ancestor.
        //
        // Gecko currently models native anonymous content that conceptually
        // hangs off the document (such as scrollbars) as a separate subtree
        // from the document root.
        //
        // Thus it's possible with Gecko that we do not find any common
        // ancestor.
        while **self.elements.last().unwrap() != common_parent {
            parents_to_insert.push(common_parent);
            self.pop().unwrap();
            common_parent = match common_parent.traversal_parent() {
                Some(parent) => parent,
                None => {
                    debug_assert!(self.elements.is_empty());
                    if cfg!(feature = "gecko") {
                        break;
                    } else {
                        panic!("should have found a common ancestor");
                    }
                }
            }
        }

        // Now the parents match, so insert the stack of elements we have been
        // collecting so far.
        for parent in parents_to_insert.into_iter().rev() {
            self.push(parent);
        }

        debug_assert_eq!(self.elements.len(), element_depth);

        // We're done! Easy.
    }
}
<commit_msg>Push elements in order in StyleBloom::rebuilds.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! The style bloom filter is used as an optimization when matching deep
//! descendant selectors.

#![deny(missing_docs)]

use dom::{SendElement, TElement};
use selectors::bloom::BloomFilter;
use smallvec::SmallVec;

/// A struct that allows us to fast-reject deep descendant selectors avoiding
/// selector-matching.
///
/// This is implemented using a counting bloom filter, and it's a standard
/// optimization. See Gecko's `AncestorFilter`, and Blink's and WebKit's
/// `SelectorFilter`.
///
/// The constraints for Servo's style system are a bit different compared to
/// traditional style systems given Servo does a parallel breadth-first
/// traversal instead of a sequential depth-first traversal.
///
/// This implies that we need to track a bit more state than other browsers to
/// ensure we're doing the correct thing during the traversal, and being able to
/// apply this optimization effectively.
///
/// Concretely, we have a bloom filter instance per worker thread, and we track
/// the current DOM depth in order to find a common ancestor when it doesn't
/// match the previous element we've styled.
///
/// This is usually a pretty fast operation (we use to be one level deeper than
/// the previous one), but in the case of work-stealing, we may needed to push
/// and pop multiple elements.
///
/// See the `insert_parents_recovering`, where most of the magic happens.
///
/// Regarding thread-safety, this struct is safe because:
///
///  * We clear this after a restyle.
///  * The DOM shape and attributes (and every other thing we access here) are
///    immutable during a restyle.
///
pub struct StyleBloom<E: TElement> {
    /// The bloom filter per se.
    filter: Box<BloomFilter>,

    /// The stack of elements that this bloom filter contains.
    elements: Vec<SendElement<E>>,
}

fn each_relevant_element_hash<E, F>(element: E, mut f: F)
    where E: TElement,
          F: FnMut(u32),
{
    f(element.get_local_name().get_hash());
    f(element.get_namespace().get_hash());

    if let Some(id) = element.get_id() {
        f(id.get_hash());
    }

    // TODO: case-sensitivity depends on the document type and quirks mode.
    //
    // TODO(emilio): It's not clear whether that's relevant here though?
    // Classes and ids should be normalized already I think.
    element.each_class(|class| {
        f(class.get_hash())
    });
}

impl<E: TElement> StyleBloom<E> {
    /// Create an empty `StyleBloom`.
    pub fn new() -> Self {
        StyleBloom {
            filter: Box::new(BloomFilter::new()),
            elements: vec![],
        }
    }

    /// Return the bloom filter used properly by the `selectors` crate.
    pub fn filter(&self) -> &BloomFilter {
        &*self.filter
    }

    /// Push an element to the bloom filter, knowing that it's a child of the
    /// last element parent.
    pub fn push(&mut self, element: E) {
        if cfg!(debug_assertions) {
            if self.elements.is_empty() {
                assert!(element.traversal_parent().is_none());
            }
        }
        self.push_internal(element);
    }

    /// Same as `push`, but without asserting, in order to use it from
    /// `rebuild`.
    fn push_internal(&mut self, element: E) {
        each_relevant_element_hash(element, |hash| {
            self.filter.insert_hash(hash);
        });
        self.elements.push(unsafe { SendElement::new(element) });
    }

    /// Pop the last element in the bloom filter and return it.
    fn pop(&mut self) -> Option<E> {
        let popped = self.elements.pop().map(|el| *el);

        if let Some(popped) = popped {
            each_relevant_element_hash(popped, |hash| {
                self.filter.remove_hash(hash);
            })
        }

        popped
    }

    /// Returns true if the bloom filter is empty.
    pub fn is_empty(&self) -> bool {
        self.elements.is_empty()
    }

    /// Returns the DOM depth of elements that can be correctly
    /// matched against the bloom filter (that is, the number of
    /// elements in our list).
    pub fn matching_depth(&self) -> usize {
        self.elements.len()
    }

    /// Clears the bloom filter.
    pub fn clear(&mut self) {
        self.filter.clear();
        self.elements.clear();
    }

    /// Rebuilds the bloom filter up to the parent of the given element.
    pub fn rebuild(&mut self, mut element: E) {
        self.clear();

        let mut parents_to_insert = SmallVec::<[E; 16]>::new();
        while let Some(parent) = element.traversal_parent() {
            parents_to_insert.push(parent);
            element = parent;
        }

        for parent in parents_to_insert.drain().rev() {
            self.push(parent);
        }
    }

    /// In debug builds, asserts that all the parents of `element` are in the
    /// bloom filter.
    ///
    /// Goes away in release builds.
    pub fn assert_complete(&self, mut element: E) {
        if cfg!(debug_assertions) {
            let mut checked = 0;
            while let Some(parent) = element.traversal_parent() {
                assert_eq!(parent, *self.elements[self.elements.len() - 1 - checked]);
                element = parent;
                checked += 1;
            }
            assert_eq!(checked, self.elements.len());
        }
    }

    /// Get the element that represents the chain of things inserted
    /// into the filter right now.  That chain is the given element
    /// (if any) and its ancestors.
    #[inline]
    pub fn current_parent(&self) -> Option<E> {
        self.elements.last().map(|el| **el)
    }

    /// Insert the parents of an element in the bloom filter, trying to recover
    /// the filter if the last element inserted doesn't match.
    ///
    /// Gets the element depth in the dom, to make it efficient, or if not
    /// provided always rebuilds the filter from scratch.
    ///
    /// Returns the new bloom filter depth, that the traversal code is
    /// responsible to keep around if it wants to get an effective filter.
    pub fn insert_parents_recovering(&mut self,
                                     element: E,
                                     element_depth: usize)
    {
        // Easy case, we're in a different restyle, or we're empty.
        if self.elements.is_empty() {
            self.rebuild(element);
            return;
        }

        let traversal_parent = match element.traversal_parent() {
            Some(parent) => parent,
            None => {
                // Yay, another easy case.
                self.clear();
                return;
            }
        };

        if self.current_parent() == Some(traversal_parent) {
            // Ta da, cache hit, we're all done.
            return;
        }

        if element_depth == 0 {
            self.clear();
            return;
        }

        // We should've early exited above.
        debug_assert!(element_depth != 0,
                      "We should have already cleared the bloom filter");
        debug_assert!(!self.elements.is_empty(),
                      "How! We should've just rebuilt!");

        // Now the fun begins: We have the depth of the dom and the depth of the
        // last element inserted in the filter, let's try to find a common
        // parent.
        //
        // The current depth, that is, the depth of the last element inserted in
        // the bloom filter, is the number of elements _minus one_, that is: if
        // there's one element, it must be the root -> depth zero.
        let mut current_depth = self.elements.len() - 1;

        // If the filter represents an element too deep in the dom, we need to
        // pop ancestors.
        while current_depth > element_depth - 1 {
            self.pop().expect("Emilio is bad at math");
            current_depth -= 1;
        }

        // Now let's try to find a common parent in the bloom filter chain,
        // starting with traversal_parent.
        let mut common_parent = traversal_parent;
        let mut common_parent_depth = element_depth - 1;

        // Let's collect the parents we are going to need to insert once we've
        // found the common one.
        let mut parents_to_insert = SmallVec::<[E; 16]>::new();

        // If the bloom filter still doesn't have enough elements, the common
        // parent is up in the dom.
        while common_parent_depth > current_depth {
            // TODO(emilio): Seems like we could insert parents here, then
            // reverse the slice.
            parents_to_insert.push(common_parent);
            common_parent =
                common_parent.traversal_parent().expect("We were lied to");
            common_parent_depth -= 1;
        }

        // Now the two depths are the same.
        debug_assert_eq!(common_parent_depth, current_depth);

        // Happy case: The parents match, we only need to push the ancestors
        // we've collected and we'll never enter in this loop.
        //
        // Not-so-happy case: Parent's don't match, so we need to keep going up
        // until we find a common ancestor.
        //
        // Gecko currently models native anonymous content that conceptually
        // hangs off the document (such as scrollbars) as a separate subtree
        // from the document root.
        //
        // Thus it's possible with Gecko that we do not find any common
        // ancestor.
        while **self.elements.last().unwrap() != common_parent {
            parents_to_insert.push(common_parent);
            self.pop().unwrap();
            common_parent = match common_parent.traversal_parent() {
                Some(parent) => parent,
                None => {
                    debug_assert!(self.elements.is_empty());
                    if cfg!(feature = "gecko") {
                        break;
                    } else {
                        panic!("should have found a common ancestor");
                    }
                }
            }
        }

        // Now the parents match, so insert the stack of elements we have been
        // collecting so far.
        for parent in parents_to_insert.drain().rev() {
            self.push(parent);
        }

        debug_assert_eq!(self.elements.len(), element_depth);

        // We're done! Easy.
    }
}
<|endoftext|>


<commit_before>#[crate_id(name="truncate", vers="1.0.0", author="Arcterus")];

/*
 * This file is part of the uutils coreutils package.
 *
 * (c) Arcterus <arcterus@mail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

extern mod extra;

use std::io::{stderr, io_error, File, Open, ReadWrite, Writer, SeekEnd, SeekSet};
use std::os;
use std::u64;
use extra::getopts::groups;

#[deriving(Eq)]
enum TruncateMode {
    Reference,
    Extend,
    Reduce,
    AtMost,
    AtLeast,
    RoundDown,
    RoundUp
}

fn main() {
    let args = os::args();
    let program = args[0].clone();

    let opts = ~[
        groups::optflag("c", "no-create", "do not create files that do not exist"),
        groups::optflag("o", "io-blocks", "treat SIZE as the number of I/O blocks of the file rather than bytes (NOT IMPLEMENTED)"),
        groups::optopt("r", "reference", "base the size of each file on the size of RFILE", "RFILE"),
        groups::optopt("s", "size", "set or adjust the size of each file according to SIZE, which is in bytes unless --io-blocks is specified", "SIZE"),
        groups::optflag("h", "help", "display this help and exit"),
        groups::optflag("V", "version", "output version information and exit")
    ];
    let matches = match groups::getopts(args.tail(), opts) {
        Ok(m) => m,
        Err(f) => {
            writeln!(&mut stderr() as &mut Writer, "{}", f.to_err_msg());
            os::set_exit_status(1);
            return
        }
    };

    if matches.opt_present("help") {
        println!("truncate 1.0.0");
        println!("");
        println!("Usage:");
        println!("  {0:s} [OPTION]... FILE...", program);
        println!("");
        print!("{}", groups::usage("Shrink or extend the size of each file to the specified size.", opts));
        println!("");
        println!("SIZE is an integer with an optional prefix and optional unit.");
        println!("The available units (K, M, G, T, P, E, Z, and Y) use the following format:");
        println!("\t'KB' =>           1000 (kilobytes)");
        println!("\t'K'  =>           1024 (kibibytes)");
        println!("\t'MB' =>      1000*1000 (megabytes)");
        println!("\t'M'  =>      1024*1024 (mebibytes)");
        println!("\t'GB' => 1000*1000*1000 (gigabytes)");
        println!("\t'G'  => 1024*1024*1024 (gibibytes)");
        println!("SIZE may also be prefixed by one of the following to adjust the size of each");
        println!("file based on its current size:");
        println!("\t'+'  => extend by");
        println!("\t'-'  => reduce by");
        println!("\t'<'  => at most");
        println!("\t'>'  => at least");
        println!("\t'/'  => round down to multiple of");
        println!("\t'%'  => round up to multiple of");
    } else if matches.opt_present("version") {
        println!("truncate 1.0.0");
    } else if matches.free.is_empty() {
        writeln!(&mut stderr() as &mut Writer, "Missing an argument");
        writeln!(&mut stderr() as &mut Writer,
                 "For help, try '{0:s} --help'", program);
        os::set_exit_status(1);
    } else {
        let no_create = matches.opt_present("no-create");
        let io_blocks = matches.opt_present("io-blocks");
        let reference = matches.opt_str("reference");
        let size = matches.opt_str("size");
        if reference.is_none() && size.is_none() {
            writeln!(&mut stderr() as &mut Writer, "You must specify either --reference or --size.");
            os::set_exit_status(1);
        } else {
            truncate(no_create, io_blocks, reference, size, matches.free);
        }
    }
}

fn truncate(no_create: bool, io_blocks: bool, reference: Option<~str>, size: Option<~str>, filenames: ~[~str]) {
    let (refsize, mode) = match reference {
        Some(rfilename) => {
            io_error::cond.trap(|err| {
                writeln!(&mut stderr() as &mut Writer, "{}", err.to_str());
                os::set_exit_status(1);
            }).inside(|| {
                let mut rfile = File::open(&Path::new(rfilename.clone()));
                rfile.seek(0, SeekEnd);
                (rfile.tell(), Reference)
            })
        }
        None => {
            match parse_size(size.unwrap()) {
                Ok(szpair) => szpair,
                Err(()) => return
            }
        }
    };
    for filename in filenames.iter() {
        let filename: &str = *filename;
        let path = Path::new(filename);
        io_error::cond.trap(|err| {
            writeln!(&mut stderr() as &mut Writer, "{}", err.to_str());
        }).inside(|| {
            if path.exists() || !no_create {
                match File::open_mode(&path, Open, ReadWrite) {
                    Some(mut file) => {
                        file.seek(0, SeekEnd);
                        let fsize = file.tell();
                        file.seek(0, SeekSet);
                            let tsize = match mode {
                            Reference => refsize,
                            Extend => fsize + refsize,
                            Reduce => fsize - refsize,
                            AtMost => if fsize > refsize { refsize } else { fsize },
                            AtLeast => if fsize < refsize { refsize } else { fsize },
                            RoundDown => fsize - fsize % refsize,
                            RoundUp => fsize + fsize % refsize
                        };
                        file.truncate(tsize as i64);
                    }
                    None => {
                        writeln!(&mut stderr() as &mut Writer,
                                 "Failed to open the file '{}'", filename);
                        os::set_exit_status(1);
                    }
                }
            }
        });
    }
}

fn parse_size(size: ~str) -> Result<(u64, TruncateMode), ()> {
    let mut err = false;
    let mode = match size.char_at(0) {
        '+' => Extend,
        '-' => Reduce,
        '<' => AtMost,
        '>' => AtLeast,
        '/' => RoundDown,
        '*' => RoundUp,
        _ => Reference /* assume that the size is just a number */
    };
    let bytes = {
        let mut slice =
            if mode == Reference {
                let size: &str = size;
                size
            } else {
                size.slice_from(1)
            };
        if slice.char_at(slice.len() - 1).is_alphabetic() {
            slice = slice.slice_to(slice.len() - 1);
            if slice.len() > 0 && slice.char_at(slice.len() - 1).is_alphabetic() {
                slice = slice.slice_to(slice.len() - 1);
            }
        }
        slice
    }.bytes().to_owned_vec();
    let mut number = match u64::parse_bytes(bytes, 10) {
        Some(num) => num,
        None => {
            writeln!(&mut stderr() as &mut Writer,
                     "'{}' is not a valid number.", size);
            os::set_exit_status(1);
            err = true;
            0
        }
    };
    if !err && size.char_at(size.len() - 1).is_alphabetic() {
        number *= match size.char_at(size.len() - 1).to_ascii().to_upper().to_char() {
            'B' => match size.char_at(size.len() - 2).to_ascii().to_upper().to_char() {
                'K' => 1000,
                'M' => 1000 * 1000,
                'G' => 1000 * 1000 * 1000,
                'T' => 1000 * 1000 * 1000 * 1000,
                'P' => 1000 * 1000 * 1000 * 1000 * 1000,
                'E' => 1000 * 1000 * 1000 * 1000 * 1000 * 1000,
                'Z' => 1000 * 1000 * 1000 * 1000 * 1000 * 1000 * 1000,
                'Y' => 1000 * 1000 * 1000 * 1000 * 1000 * 1000 * 1000 * 1000,
                letter => {
                    writeln!(&mut stderr() as &mut Writer,
                             "'{}B' is not a valid suffix.", letter);
                    os::set_exit_status(1);
                    err = true;
                    1
                }
            },
            'K' => 1024,
            'M' => 1024 * 1024,
            'G' => 1024 * 1024 * 1024,
            'T' => 1024 * 1024 * 1024 * 1024,
            'P' => 1024 * 1024 * 1024 * 1024 * 1024,
            'E' => 1024 * 1024 * 1024 * 1024 * 1024 * 1024,
            'Z' => 1024 * 1024 * 1024 * 1024 * 1024 * 1024 * 1024,
            'Y' => 1024 * 1024 * 1024 * 1024 * 1024 * 1024 * 1024 * 1024,
            letter => {
                writeln!(&mut stderr() as &mut Writer,
                         "'{}' is not a valid suffix.", letter);
                os::set_exit_status(1);
                err = true;
                1
            }
        };
    }
    if err {
        Err(())
    } else {
        Ok((number, mode))
    }
}

<commit_msg>Changed truncate to use multi-line println!<commit_after>#[crate_id(name="truncate", vers="1.0.0", author="Arcterus")];

/*
 * This file is part of the uutils coreutils package.
 *
 * (c) Arcterus <arcterus@mail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

extern mod extra;

use std::io::{stderr, io_error, File, Open, ReadWrite, Writer, SeekEnd, SeekSet};
use std::os;
use std::u64;
use extra::getopts::groups;

#[deriving(Eq)]
enum TruncateMode {
    Reference,
    Extend,
    Reduce,
    AtMost,
    AtLeast,
    RoundDown,
    RoundUp
}

fn main() {
    let args = os::args();
    let program = args[0].clone();

    let opts = ~[
        groups::optflag("c", "no-create", "do not create files that do not exist"),
        groups::optflag("o", "io-blocks", "treat SIZE as the number of I/O blocks of the file rather than bytes (NOT IMPLEMENTED)"),
        groups::optopt("r", "reference", "base the size of each file on the size of RFILE", "RFILE"),
        groups::optopt("s", "size", "set or adjust the size of each file according to SIZE, which is in bytes unless --io-blocks is specified", "SIZE"),
        groups::optflag("h", "help", "display this help and exit"),
        groups::optflag("V", "version", "output version information and exit")
    ];
    let matches = match groups::getopts(args.tail(), opts) {
        Ok(m) => m,
        Err(f) => {
            writeln!(&mut stderr() as &mut Writer, "{}", f.to_err_msg());
            os::set_exit_status(1);
            return
        }
    };

    if matches.opt_present("help") {
        println!("truncate 1.0.0");
        println!("");
        println!("Usage:");
        println!("  {0:s} [OPTION]... FILE...", program);
        println!("");
        print!("{}", groups::usage("Shrink or extend the size of each file to the specified size.", opts));
        print!("
SIZE is an integer with an optional prefix and optional unit.
The available units (K, M, G, T, P, E, Z, and Y) use the following format:
	'KB' =>           1000 (kilobytes)
	'K'  =>           1024 (kibibytes)
	'MB' =>      1000*1000 (megabytes)
	'M'  =>      1024*1024 (mebibytes)
	'GB' => 1000*1000*1000 (gigabytes)
	'G'  => 1024*1024*1024 (gibibytes)
SIZE may also be prefixed by one of the following to adjust the size of each
file based on its current size:
	'+'  => extend by
	'-'  => reduce by
	'<'  => at most
	'>'  => at least
	'/'  => round down to multiple of
	'%'  => round up to multiple of
");
    } else if matches.opt_present("version") {
        println!("truncate 1.0.0");
    } else if matches.free.is_empty() {
        writeln!(&mut stderr() as &mut Writer, "Missing an argument");
        writeln!(&mut stderr() as &mut Writer,
                 "For help, try '{0:s} --help'", program);
        os::set_exit_status(1);
    } else {
        let no_create = matches.opt_present("no-create");
        let io_blocks = matches.opt_present("io-blocks");
        let reference = matches.opt_str("reference");
        let size = matches.opt_str("size");
        if reference.is_none() && size.is_none() {
            writeln!(&mut stderr() as &mut Writer, "You must specify either --reference or --size.");
            os::set_exit_status(1);
        } else {
            truncate(no_create, io_blocks, reference, size, matches.free);
        }
    }
}

fn truncate(no_create: bool, io_blocks: bool, reference: Option<~str>, size: Option<~str>, filenames: ~[~str]) {
    let (refsize, mode) = match reference {
        Some(rfilename) => {
            io_error::cond.trap(|err| {
                writeln!(&mut stderr() as &mut Writer, "{}", err.to_str());
                os::set_exit_status(1);
            }).inside(|| {
                let mut rfile = File::open(&Path::new(rfilename.clone()));
                rfile.seek(0, SeekEnd);
                (rfile.tell(), Reference)
            })
        }
        None => {
            match parse_size(size.unwrap()) {
                Ok(szpair) => szpair,
                Err(()) => return
            }
        }
    };
    for filename in filenames.iter() {
        let filename: &str = *filename;
        let path = Path::new(filename);
        io_error::cond.trap(|err| {
            writeln!(&mut stderr() as &mut Writer, "{}", err.to_str());
        }).inside(|| {
            if path.exists() || !no_create {
                match File::open_mode(&path, Open, ReadWrite) {
                    Some(mut file) => {
                        file.seek(0, SeekEnd);
                        let fsize = file.tell();
                        file.seek(0, SeekSet);
                            let tsize = match mode {
                            Reference => refsize,
                            Extend => fsize + refsize,
                            Reduce => fsize - refsize,
                            AtMost => if fsize > refsize { refsize } else { fsize },
                            AtLeast => if fsize < refsize { refsize } else { fsize },
                            RoundDown => fsize - fsize % refsize,
                            RoundUp => fsize + fsize % refsize
                        };
                        file.truncate(tsize as i64);
                    }
                    None => {
                        writeln!(&mut stderr() as &mut Writer,
                                 "Failed to open the file '{}'", filename);
                        os::set_exit_status(1);
                    }
                }
            }
        });
    }
}

fn parse_size(size: ~str) -> Result<(u64, TruncateMode), ()> {
    let mut err = false;
    let mode = match size.char_at(0) {
        '+' => Extend,
        '-' => Reduce,
        '<' => AtMost,
        '>' => AtLeast,
        '/' => RoundDown,
        '*' => RoundUp,
        _ => Reference /* assume that the size is just a number */
    };
    let bytes = {
        let mut slice =
            if mode == Reference {
                let size: &str = size;
                size
            } else {
                size.slice_from(1)
            };
        if slice.char_at(slice.len() - 1).is_alphabetic() {
            slice = slice.slice_to(slice.len() - 1);
            if slice.len() > 0 && slice.char_at(slice.len() - 1).is_alphabetic() {
                slice = slice.slice_to(slice.len() - 1);
            }
        }
        slice
    }.bytes().to_owned_vec();
    let mut number = match u64::parse_bytes(bytes, 10) {
        Some(num) => num,
        None => {
            writeln!(&mut stderr() as &mut Writer,
                     "'{}' is not a valid number.", size);
            os::set_exit_status(1);
            err = true;
            0
        }
    };
    if !err && size.char_at(size.len() - 1).is_alphabetic() {
        number *= match size.char_at(size.len() - 1).to_ascii().to_upper().to_char() {
            'B' => match size.char_at(size.len() - 2).to_ascii().to_upper().to_char() {
                'K' => 1000,
                'M' => 1000 * 1000,
                'G' => 1000 * 1000 * 1000,
                'T' => 1000 * 1000 * 1000 * 1000,
                'P' => 1000 * 1000 * 1000 * 1000 * 1000,
                'E' => 1000 * 1000 * 1000 * 1000 * 1000 * 1000,
                'Z' => 1000 * 1000 * 1000 * 1000 * 1000 * 1000 * 1000,
                'Y' => 1000 * 1000 * 1000 * 1000 * 1000 * 1000 * 1000 * 1000,
                letter => {
                    writeln!(&mut stderr() as &mut Writer,
                             "'{}B' is not a valid suffix.", letter);
                    os::set_exit_status(1);
                    err = true;
                    1
                }
            },
            'K' => 1024,
            'M' => 1024 * 1024,
            'G' => 1024 * 1024 * 1024,
            'T' => 1024 * 1024 * 1024 * 1024,
            'P' => 1024 * 1024 * 1024 * 1024 * 1024,
            'E' => 1024 * 1024 * 1024 * 1024 * 1024 * 1024,
            'Z' => 1024 * 1024 * 1024 * 1024 * 1024 * 1024 * 1024,
            'Y' => 1024 * 1024 * 1024 * 1024 * 1024 * 1024 * 1024 * 1024,
            letter => {
                writeln!(&mut stderr() as &mut Writer,
                         "'{}' is not a valid suffix.", letter);
                os::set_exit_status(1);
                err = true;
                1
            }
        };
    }
    if err {
        Err(())
    } else {
        Ok((number, mode))
    }
}

<|endoftext|>


<commit_before>#![allow(clippy::single_match_else)]
use rustc_tools_util::VersionInfo;

#[test]
fn check_that_clippy_lints_has_the_same_version_as_clippy() {
    let clippy_meta = cargo_metadata::MetadataCommand::new()
        .no_deps()
        .exec()
        .expect("could not obtain cargo metadata");
    std::env::set_current_dir(std::env::current_dir().unwrap().join("clippy_lints")).unwrap();
    let clippy_lints_meta = cargo_metadata::MetadataCommand::new()
        .no_deps()
        .exec()
        .expect("could not obtain cargo metadata");
    assert_eq!(clippy_lints_meta.packages[0].version, clippy_meta.packages[0].version);
    for package in &clippy_meta.packages[0].dependencies {
        if package.name == "clippy_lints" {
            assert!(package.req.matches(&clippy_lints_meta.packages[0].version));
            return;
        }
    }
}

#[test]
fn check_that_clippy_has_the_same_major_version_as_rustc() {
    let clippy_version = rustc_tools_util::get_version_info!();
    let clippy_major = clippy_version.major;
    let clippy_minor = clippy_version.minor;
    let clippy_patch = clippy_version.patch;

    // get the rustc version either from the rustc installed with the toolchain file or from
    // `RUSTC_REAL` if Clippy is build in the Rust repo with `./x.py`.
    let rustc = std::env::var("RUSTC_REAL").unwrap_or_else(|_| "rustc".to_string());
    let rustc_version = String::from_utf8(
        std::process::Command::new(&rustc)
            .arg("--version")
            .output()
            .expect("failed to run `rustc --version`")
            .stdout,
    )
    .unwrap();
    // extract "1 XX 0" from "rustc 1.XX.0-nightly (<commit> <date>)"
    let vsplit: Vec<&str> = rustc_version
        .split(' ')
        .nth(1)
        .unwrap()
        .split('-')
        .next()
        .unwrap()
        .split('.')
        .collect();
    match vsplit.as_slice() {
        [rustc_major, rustc_minor, _rustc_patch] => {
            // clippy 0.1.XX should correspond to rustc 1.XX.0
            assert_eq!(clippy_major, 0); // this will probably stay the same for a long time
            assert_eq!(
                clippy_minor.to_string(),
                *rustc_major,
                "clippy minor version does not equal rustc major version"
            );
            assert_eq!(
                clippy_patch.to_string(),
                *rustc_minor,
                "clippy patch version does not equal rustc minor version"
            );
            // do not check rustc_patch because when a stable-patch-release is made (like 1.50.2),
            // we don't want our tests failing suddenly
        },
        _ => {
            panic!("Failed to parse rustc version: {:?}", vsplit);
        },
    };
}
<commit_msg>tests: ignore check_that_clippy_has_the_same_major_version_as_rustc() inside the rustc repo.<commit_after>#![allow(clippy::single_match_else)]
use rustc_tools_util::VersionInfo;

#[test]
fn check_that_clippy_lints_has_the_same_version_as_clippy() {
    let clippy_meta = cargo_metadata::MetadataCommand::new()
        .no_deps()
        .exec()
        .expect("could not obtain cargo metadata");
    std::env::set_current_dir(std::env::current_dir().unwrap().join("clippy_lints")).unwrap();
    let clippy_lints_meta = cargo_metadata::MetadataCommand::new()
        .no_deps()
        .exec()
        .expect("could not obtain cargo metadata");
    assert_eq!(clippy_lints_meta.packages[0].version, clippy_meta.packages[0].version);
    for package in &clippy_meta.packages[0].dependencies {
        if package.name == "clippy_lints" {
            assert!(package.req.matches(&clippy_lints_meta.packages[0].version));
            return;
        }
    }
}

#[test]
fn check_that_clippy_has_the_same_major_version_as_rustc() {
    // do not run this test inside the upstream rustc repo:
    // https://github.com/rust-lang/rust-clippy/issues/6683
    if option_env!("RUSTC_TEST_SUITE").is_some() {
        return;
    }

    let clippy_version = rustc_tools_util::get_version_info!();
    let clippy_major = clippy_version.major;
    let clippy_minor = clippy_version.minor;
    let clippy_patch = clippy_version.patch;

    // get the rustc version either from the rustc installed with the toolchain file or from
    // `RUSTC_REAL` if Clippy is build in the Rust repo with `./x.py`.
    let rustc = std::env::var("RUSTC_REAL").unwrap_or_else(|_| "rustc".to_string());
    let rustc_version = String::from_utf8(
        std::process::Command::new(&rustc)
            .arg("--version")
            .output()
            .expect("failed to run `rustc --version`")
            .stdout,
    )
    .unwrap();
    // extract "1 XX 0" from "rustc 1.XX.0-nightly (<commit> <date>)"
    let vsplit: Vec<&str> = rustc_version
        .split(' ')
        .nth(1)
        .unwrap()
        .split('-')
        .next()
        .unwrap()
        .split('.')
        .collect();
    match vsplit.as_slice() {
        [rustc_major, rustc_minor, _rustc_patch] => {
            // clippy 0.1.XX should correspond to rustc 1.XX.0
            assert_eq!(clippy_major, 0); // this will probably stay the same for a long time
            assert_eq!(
                clippy_minor.to_string(),
                *rustc_major,
                "clippy minor version does not equal rustc major version"
            );
            assert_eq!(
                clippy_patch.to_string(),
                *rustc_minor,
                "clippy patch version does not equal rustc minor version"
            );
            // do not check rustc_patch because when a stable-patch-release is made (like 1.50.2),
            // we don't want our tests failing suddenly
        },
        _ => {
            panic!("Failed to parse rustc version: {:?}", vsplit);
        },
    };
}
<|endoftext|>


<commit_before>use std;

fn main(args: [str]) {
	std::io::println("Fuck you Mendez.");
}
<commit_msg>Updates to non-archaic Rust syntax<commit_after>fn main() {
	println!("Fuck you Mendez.");
}
<|endoftext|>