<commit_before>extern crate gstreamer as gst;

extern crate gtk;
use gtk::prelude::*;

use std::rc::{Rc, Weak};
use std::cell::RefCell;

use media::PlaybackContext;

use super::MainController;

const ALIGN_LEFT: f32 = 0f32;
const ALIGN_CENTER: f32 = 0.5f32;
const ALIGN_RIGHT: f32 = 1f32;

const STREAM_ID_COL: u32 = 0;
const STREAM_ID_DISPLAY_COL: u32 = 1;
const LANGUAGE_COL: u32 = 2;
const CODEC_COL: u32 = 3;
const COMMENT_COL: u32 = 4;

const VIDEO_WIDTH_COL: u32 = 5;
const VIDEO_HEIGHT_COL: u32 = 6;

const AUDIO_RATE_COL: u32 = 5;
const AUDIO_CHANNELS_COL: u32 = 6;

const TEXT_FORMAT_COL: u32 = 5;

pub struct StreamsController {
    streams_button: gtk::ToggleButton,
    display_streams_stack: gtk::Stack,

    video_treeview: gtk::TreeView,
    video_store: gtk::ListStore,
    audio_treeview: gtk::TreeView,
    audio_store: gtk::ListStore,
    text_treeview: gtk::TreeView,
    text_store: gtk::ListStore,

    main_ctrl: Option<Weak<RefCell<MainController>>>,
}

impl StreamsController {
    pub fn new(builder: &gtk::Builder) -> Rc<RefCell<Self>> {
        let this_rc = Rc::new(RefCell::new(StreamsController {
            streams_button: builder.get_object("streams-toggle").unwrap(),
            display_streams_stack: builder.get_object("display_streams-stack").unwrap(),

            video_treeview: builder.get_object("video_streams-treeview").unwrap(),
            video_store: builder.get_object("video_streams-liststore").unwrap(),
            audio_treeview: builder.get_object("audio_streams-treeview").unwrap(),
            audio_store: builder.get_object("audio_streams-liststore").unwrap(),
            text_treeview: builder.get_object("text_streams-treeview").unwrap(),
            text_store: builder.get_object("text_streams-liststore").unwrap(),

            main_ctrl: None,
        }));

        {
            let mut this = this_rc.borrow_mut();
            this.cleanup();
            this.init_treeviews();
        }

        this_rc
    }

    pub fn register_callbacks(
        this_rc: &Rc<RefCell<Self>>,
        main_ctrl: &Rc<RefCell<MainController>>,
    ) {
        let mut this = this_rc.borrow_mut();

        this.main_ctrl = Some(Rc::downgrade(main_ctrl));

        // streams button
        let this_clone = Rc::clone(this_rc);
        this.streams_button.connect_clicked(move |button| {
            let page_name = if button.get_active() {
                "streams".into()
            } else {
                "display".into()
            };
            this_clone.borrow_mut().display_streams_stack.set_visible_child_name(page_name);
        });
    }

    pub fn new_media(&mut self, context: &mut PlaybackContext) {
        self.streams_button.set_sensitive(true);

        let mut info = context
            .info
            .lock()
            .expect("StreamsController::new_media: failed to lock media info");

        // Video streams
        for (stream_id, &(ref caps, ref tags)) in &info.video_streams {
            let iter = self.add_stream(&self.video_store, &stream_id, caps, tags);
            let caps_structure = caps.get_structure(0).unwrap();
            if let Some(width) = caps_structure.get::<i32>("width") {
                self.video_store.set_value(&iter, VIDEO_WIDTH_COL, &gtk::Value::from(&width));
            }
            if let Some(height) = caps_structure.get::<i32>("height") {
                self.video_store.set_value(&iter, VIDEO_HEIGHT_COL, &gtk::Value::from(&height));
            }
        }

        info.video_selected = match self.video_store.get_iter_first() {
            Some(ref iter) => {
                self.video_treeview.get_selection().select_iter(iter);
                self.get_selected_stream(&self.video_store, iter)
            }
            None => None,
        };

        // Audio streams
        for (stream_id, &(ref caps, ref tags)) in &info.audio_streams {
            let iter = self.add_stream(&self.audio_store, &stream_id, caps, tags);
            let caps_structure = caps.get_structure(0).unwrap();
            if let Some(rate) = caps_structure.get::<i32>("rate") {
                self.audio_store.set_value(&iter, AUDIO_RATE_COL, &gtk::Value::from(&rate));
            }
            if let Some(channels) = caps_structure.get::<i32>("channels") {
                self.audio_store.set_value(&iter, AUDIO_CHANNELS_COL, &gtk::Value::from(&channels));
            }
        }

        info.audio_selected = match self.audio_store.get_iter_first() {
            Some(ref iter) => {
                self.audio_treeview.get_selection().select_iter(iter);
                self.get_selected_stream(&self.audio_store, iter)
            }
            None => None,
        };

        // Text streams
        for (stream_id, &(ref caps, ref tags)) in &info.text_streams {
            let iter = self.add_stream(&self.text_store, &stream_id, caps, tags);
            let caps_structure = caps.get_structure(0).unwrap();
            if let Some(format) = caps_structure.get::<&str>("format") {
                self.text_store.set_value(&iter, TEXT_FORMAT_COL, &gtk::Value::from(&format));
            }
        }

        info.text_selected = match self.text_store.get_iter_first() {
            Some(ref iter) => {
                self.text_treeview.get_selection().select_iter(iter);
                self.get_selected_stream(&self.text_store, iter)
            }
            None => None,
        };
    }

    fn add_stream(
        &self,
        store: &gtk::ListStore,
        stream_id: &str,
        caps: &gst::Caps,
        tags: &Option<gst::TagList>,
    ) -> gtk::TreeIter {
        let id_parts: Vec<&str> = stream_id.split('/').collect();
        let stream_id_display = if id_parts.len() == 2 {
            id_parts[1]
        } else {
            "unknown"
        };

        let iter = store.insert_with_values(
            None,
            &[STREAM_ID_COL, STREAM_ID_DISPLAY_COL],
            &[&stream_id, &stream_id_display],
        );

        let codec = match tags.as_ref() {
            Some(tags) => {
                let language = match tags.get_index::<gst::tags::LanguageName>(0).as_ref() {
                    Some(language) => language.get().unwrap(),
                    None => match tags.get_index::<gst::tags::LanguageCode>(0).as_ref() {
                        Some(language) => language.get().unwrap(),
                        None => "-",
                    }
                };
                store.set_value(&iter, LANGUAGE_COL, &gtk::Value::from(language));

                if let Some(comment) = tags.get_index::<gst::tags::Comment>(0).as_ref() {
                    store.set_value(&iter, COMMENT_COL, &gtk::Value::from(comment.get().unwrap()));
                }

                match tags.get_index::<gst::tags::VideoCodec>(0).as_ref() {
                    Some(codec) => codec.get(),
                    None => match tags.get_index::<gst::tags::AudioCodec>(0).as_ref() {
                        Some(codec) => codec.get(),
                        None => match tags.get_index::<gst::tags::SubtitleCodec>(0).as_ref() {
                            Some(codec) => codec.get(),
                            None => match tags.get_index::<gst::tags::Codec>(0).as_ref() {
                                Some(codec) => codec.get(),
                                None => None,
                            }
                        }
                    }
                }
            }
            None => None,
        };

        let codec = match codec {
            Some(codec) => codec,
            None => {
                // codec in caps in the form "streamtype/x-codec"
                let codec = caps.get_structure(0).unwrap().get_name();
                let id_parts: Vec<&str> = codec.split('/').collect();
                if id_parts.len() == 2 {
                    if id_parts[1].starts_with("x-") {
                        &id_parts[1][2..]
                    } else {
                        id_parts[1]
                    }
                } else {
                    codec
                }
            },
        };
        store.set_value(&iter, CODEC_COL, &gtk::Value::from(&codec));

        iter
    }

    fn get_selected_stream(
        &self,
        store: &gtk::ListStore,
        iter: &gtk::TreeIter,
    ) -> Option<(String, String)> {
        Some((
            store
                .get_value(iter, STREAM_ID_DISPLAY_COL as i32)
                .get::<String>()
                .unwrap(),
            store
                .get_value(iter, CODEC_COL as i32)
                .get::<String>()
                .unwrap(),
        ))
    }

    pub fn cleanup(&mut self) {
        self.streams_button.set_sensitive(false);
        self.video_store.clear();
        self.audio_store.clear();
        self.text_store.clear();
    }

    fn init_treeviews(&self) {
        self.video_treeview.set_model(Some(&self.video_store));
        self.add_column(
            &self.video_treeview,
            "Stream id",
            ALIGN_LEFT,
            STREAM_ID_DISPLAY_COL,
            Some(200),
        );
        self.add_column(&self.video_treeview, "Lang.", ALIGN_CENTER, LANGUAGE_COL, None);
        self.add_column(&self.video_treeview, "Codec", ALIGN_LEFT, CODEC_COL, None);
        self.add_column(&self.video_treeview, "Width", ALIGN_RIGHT, VIDEO_WIDTH_COL, None);
        self.add_column(&self.video_treeview, "Height", ALIGN_RIGHT, VIDEO_HEIGHT_COL, None);
        self.add_column(&self.video_treeview, "Comment", ALIGN_LEFT, COMMENT_COL, None);

        self.audio_treeview.set_model(Some(&self.audio_store));
        self.add_column(
            &self.audio_treeview,
            "Stream id",
            ALIGN_LEFT,
            STREAM_ID_DISPLAY_COL,
            Some(200),
        );
        self.add_column(&self.audio_treeview, "Lang.", ALIGN_CENTER, LANGUAGE_COL, None);
        self.add_column(&self.audio_treeview, "Codec", ALIGN_LEFT, CODEC_COL, None);
        self.add_column(&self.audio_treeview, "Rate", ALIGN_RIGHT, AUDIO_RATE_COL, None);
        self.add_column(&self.audio_treeview, "Channels", ALIGN_RIGHT, AUDIO_CHANNELS_COL, None);
        self.add_column(&self.audio_treeview, "Comment", ALIGN_LEFT, COMMENT_COL, None);

        self.text_treeview.set_model(Some(&self.text_store));
        self.add_column(
            &self.text_treeview,
            "Stream id",
            ALIGN_LEFT,
            STREAM_ID_DISPLAY_COL,
            Some(200),
        );
        self.add_column(&self.text_treeview, "Lang.", ALIGN_CENTER, LANGUAGE_COL, None);
        self.add_column(&self.text_treeview, "Codec", ALIGN_LEFT, CODEC_COL, None);
        self.add_column(&self.text_treeview, "Format", ALIGN_LEFT, TEXT_FORMAT_COL, None);
        self.add_column(&self.text_treeview, "Comment", ALIGN_LEFT, COMMENT_COL, None);
    }

    fn add_column(
        &self,
        treeview: &gtk::TreeView,
        title: &str,
        alignment: f32,
        col_id: u32,
        width: Option<i32>,
    ) {
        let col = gtk::TreeViewColumn::new();
        col.set_title(title);

        let renderer = gtk::CellRendererText::new();
        renderer.set_alignment(alignment, 0.5f32);
        col.pack_start(&renderer, true);
        col.add_attribute(&renderer, "text", col_id as i32);

        if let Some(width) = width {
            renderer.set_fixed_size(width, -1);
        }

        treeview.append_column(&col);
    }
}
<commit_msg>StreamsController: move cleanup for consistency with other controllers (#46)<commit_after>extern crate gstreamer as gst;

extern crate gtk;
use gtk::prelude::*;

use std::rc::{Rc, Weak};
use std::cell::RefCell;

use media::PlaybackContext;

use super::MainController;

const ALIGN_LEFT: f32 = 0f32;
const ALIGN_CENTER: f32 = 0.5f32;
const ALIGN_RIGHT: f32 = 1f32;

const STREAM_ID_COL: u32 = 0;
const STREAM_ID_DISPLAY_COL: u32 = 1;
const LANGUAGE_COL: u32 = 2;
const CODEC_COL: u32 = 3;
const COMMENT_COL: u32 = 4;

const VIDEO_WIDTH_COL: u32 = 5;
const VIDEO_HEIGHT_COL: u32 = 6;

const AUDIO_RATE_COL: u32 = 5;
const AUDIO_CHANNELS_COL: u32 = 6;

const TEXT_FORMAT_COL: u32 = 5;

pub struct StreamsController {
    streams_button: gtk::ToggleButton,
    display_streams_stack: gtk::Stack,

    video_treeview: gtk::TreeView,
    video_store: gtk::ListStore,
    audio_treeview: gtk::TreeView,
    audio_store: gtk::ListStore,
    text_treeview: gtk::TreeView,
    text_store: gtk::ListStore,

    main_ctrl: Option<Weak<RefCell<MainController>>>,
}

impl StreamsController {
    pub fn new(builder: &gtk::Builder) -> Rc<RefCell<Self>> {
        let this_rc = Rc::new(RefCell::new(StreamsController {
            streams_button: builder.get_object("streams-toggle").unwrap(),
            display_streams_stack: builder.get_object("display_streams-stack").unwrap(),

            video_treeview: builder.get_object("video_streams-treeview").unwrap(),
            video_store: builder.get_object("video_streams-liststore").unwrap(),
            audio_treeview: builder.get_object("audio_streams-treeview").unwrap(),
            audio_store: builder.get_object("audio_streams-liststore").unwrap(),
            text_treeview: builder.get_object("text_streams-treeview").unwrap(),
            text_store: builder.get_object("text_streams-liststore").unwrap(),

            main_ctrl: None,
        }));

        {
            let mut this = this_rc.borrow_mut();
            this.cleanup();
            this.init_treeviews();
        }

        this_rc
    }

    pub fn register_callbacks(
        this_rc: &Rc<RefCell<Self>>,
        main_ctrl: &Rc<RefCell<MainController>>,
    ) {
        let mut this = this_rc.borrow_mut();

        this.main_ctrl = Some(Rc::downgrade(main_ctrl));

        // streams button
        let this_clone = Rc::clone(this_rc);
        this.streams_button.connect_clicked(move |button| {
            let page_name = if button.get_active() {
                "streams".into()
            } else {
                "display".into()
            };
            this_clone.borrow_mut().display_streams_stack.set_visible_child_name(page_name);
        });
    }

    pub fn cleanup(&mut self) {
        self.streams_button.set_sensitive(false);
        self.video_store.clear();
        self.audio_store.clear();
        self.text_store.clear();
    }

    pub fn new_media(&mut self, context: &mut PlaybackContext) {
        self.streams_button.set_sensitive(true);

        let mut info = context
            .info
            .lock()
            .expect("StreamsController::new_media: failed to lock media info");

        // Video streams
        for (stream_id, &(ref caps, ref tags)) in &info.video_streams {
            let iter = self.add_stream(&self.video_store, &stream_id, caps, tags);
            let caps_structure = caps.get_structure(0).unwrap();
            if let Some(width) = caps_structure.get::<i32>("width") {
                self.video_store.set_value(&iter, VIDEO_WIDTH_COL, &gtk::Value::from(&width));
            }
            if let Some(height) = caps_structure.get::<i32>("height") {
                self.video_store.set_value(&iter, VIDEO_HEIGHT_COL, &gtk::Value::from(&height));
            }
        }

        info.video_selected = match self.video_store.get_iter_first() {
            Some(ref iter) => {
                self.video_treeview.get_selection().select_iter(iter);
                self.get_selected_stream(&self.video_store, iter)
            }
            None => None,
        };

        // Audio streams
        for (stream_id, &(ref caps, ref tags)) in &info.audio_streams {
            let iter = self.add_stream(&self.audio_store, &stream_id, caps, tags);
            let caps_structure = caps.get_structure(0).unwrap();
            if let Some(rate) = caps_structure.get::<i32>("rate") {
                self.audio_store.set_value(&iter, AUDIO_RATE_COL, &gtk::Value::from(&rate));
            }
            if let Some(channels) = caps_structure.get::<i32>("channels") {
                self.audio_store.set_value(&iter, AUDIO_CHANNELS_COL, &gtk::Value::from(&channels));
            }
        }

        info.audio_selected = match self.audio_store.get_iter_first() {
            Some(ref iter) => {
                self.audio_treeview.get_selection().select_iter(iter);
                self.get_selected_stream(&self.audio_store, iter)
            }
            None => None,
        };

        // Text streams
        for (stream_id, &(ref caps, ref tags)) in &info.text_streams {
            let iter = self.add_stream(&self.text_store, &stream_id, caps, tags);
            let caps_structure = caps.get_structure(0).unwrap();
            if let Some(format) = caps_structure.get::<&str>("format") {
                self.text_store.set_value(&iter, TEXT_FORMAT_COL, &gtk::Value::from(&format));
            }
        }

        info.text_selected = match self.text_store.get_iter_first() {
            Some(ref iter) => {
                self.text_treeview.get_selection().select_iter(iter);
                self.get_selected_stream(&self.text_store, iter)
            }
            None => None,
        };
    }

    fn add_stream(
        &self,
        store: &gtk::ListStore,
        stream_id: &str,
        caps: &gst::Caps,
        tags: &Option<gst::TagList>,
    ) -> gtk::TreeIter {
        let id_parts: Vec<&str> = stream_id.split('/').collect();
        let stream_id_display = if id_parts.len() == 2 {
            id_parts[1]
        } else {
            "unknown"
        };

        let iter = store.insert_with_values(
            None,
            &[STREAM_ID_COL, STREAM_ID_DISPLAY_COL],
            &[&stream_id, &stream_id_display],
        );

        let codec = match tags.as_ref() {
            Some(tags) => {
                let language = match tags.get_index::<gst::tags::LanguageName>(0).as_ref() {
                    Some(language) => language.get().unwrap(),
                    None => match tags.get_index::<gst::tags::LanguageCode>(0).as_ref() {
                        Some(language) => language.get().unwrap(),
                        None => "-",
                    }
                };
                store.set_value(&iter, LANGUAGE_COL, &gtk::Value::from(language));

                if let Some(comment) = tags.get_index::<gst::tags::Comment>(0).as_ref() {
                    store.set_value(&iter, COMMENT_COL, &gtk::Value::from(comment.get().unwrap()));
                }

                match tags.get_index::<gst::tags::VideoCodec>(0).as_ref() {
                    Some(codec) => codec.get(),
                    None => match tags.get_index::<gst::tags::AudioCodec>(0).as_ref() {
                        Some(codec) => codec.get(),
                        None => match tags.get_index::<gst::tags::SubtitleCodec>(0).as_ref() {
                            Some(codec) => codec.get(),
                            None => match tags.get_index::<gst::tags::Codec>(0).as_ref() {
                                Some(codec) => codec.get(),
                                None => None,
                            }
                        }
                    }
                }
            }
            None => None,
        };

        let codec = match codec {
            Some(codec) => codec,
            None => {
                // codec in caps in the form "streamtype/x-codec"
                let codec = caps.get_structure(0).unwrap().get_name();
                let id_parts: Vec<&str> = codec.split('/').collect();
                if id_parts.len() == 2 {
                    if id_parts[1].starts_with("x-") {
                        &id_parts[1][2..]
                    } else {
                        id_parts[1]
                    }
                } else {
                    codec
                }
            },
        };
        store.set_value(&iter, CODEC_COL, &gtk::Value::from(&codec));

        iter
    }

    fn get_selected_stream(
        &self,
        store: &gtk::ListStore,
        iter: &gtk::TreeIter,
    ) -> Option<(String, String)> {
        Some((
            store
                .get_value(iter, STREAM_ID_DISPLAY_COL as i32)
                .get::<String>()
                .unwrap(),
            store
                .get_value(iter, CODEC_COL as i32)
                .get::<String>()
                .unwrap(),
        ))
    }

    fn init_treeviews(&self) {
        self.video_treeview.set_model(Some(&self.video_store));
        self.add_column(
            &self.video_treeview,
            "Stream id",
            ALIGN_LEFT,
            STREAM_ID_DISPLAY_COL,
            Some(200),
        );
        self.add_column(&self.video_treeview, "Lang.", ALIGN_CENTER, LANGUAGE_COL, None);
        self.add_column(&self.video_treeview, "Codec", ALIGN_LEFT, CODEC_COL, None);
        self.add_column(&self.video_treeview, "Width", ALIGN_RIGHT, VIDEO_WIDTH_COL, None);
        self.add_column(&self.video_treeview, "Height", ALIGN_RIGHT, VIDEO_HEIGHT_COL, None);
        self.add_column(&self.video_treeview, "Comment", ALIGN_LEFT, COMMENT_COL, None);

        self.audio_treeview.set_model(Some(&self.audio_store));
        self.add_column(
            &self.audio_treeview,
            "Stream id",
            ALIGN_LEFT,
            STREAM_ID_DISPLAY_COL,
            Some(200),
        );
        self.add_column(&self.audio_treeview, "Lang.", ALIGN_CENTER, LANGUAGE_COL, None);
        self.add_column(&self.audio_treeview, "Codec", ALIGN_LEFT, CODEC_COL, None);
        self.add_column(&self.audio_treeview, "Rate", ALIGN_RIGHT, AUDIO_RATE_COL, None);
        self.add_column(&self.audio_treeview, "Channels", ALIGN_RIGHT, AUDIO_CHANNELS_COL, None);
        self.add_column(&self.audio_treeview, "Comment", ALIGN_LEFT, COMMENT_COL, None);

        self.text_treeview.set_model(Some(&self.text_store));
        self.add_column(
            &self.text_treeview,
            "Stream id",
            ALIGN_LEFT,
            STREAM_ID_DISPLAY_COL,
            Some(200),
        );
        self.add_column(&self.text_treeview, "Lang.", ALIGN_CENTER, LANGUAGE_COL, None);
        self.add_column(&self.text_treeview, "Codec", ALIGN_LEFT, CODEC_COL, None);
        self.add_column(&self.text_treeview, "Format", ALIGN_LEFT, TEXT_FORMAT_COL, None);
        self.add_column(&self.text_treeview, "Comment", ALIGN_LEFT, COMMENT_COL, None);
    }

    fn add_column(
        &self,
        treeview: &gtk::TreeView,
        title: &str,
        alignment: f32,
        col_id: u32,
        width: Option<i32>,
    ) {
        let col = gtk::TreeViewColumn::new();
        col.set_title(title);

        let renderer = gtk::CellRendererText::new();
        renderer.set_alignment(alignment, 0.5f32);
        col.pack_start(&renderer, true);
        col.add_attribute(&renderer, "text", col_id as i32);

        if let Some(width) = width {
            renderer.set_fixed_size(width, -1);
        }

        treeview.append_column(&col);
    }
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::ffi::CStr;

use cairo::prelude::SurfaceExt;
use cairo::{self, MatrixTrait};
use cairo_sys::cairo_surface_t;
use glib::translate::{from_glib_none, ToGlibPtr};
use glib_sys::*;

use bbox::BoundingBox;
use coord_units::CoordUnits;
use drawing_ctx::{self, RsvgDrawingCtx};
use filters::{IRect, RsvgFilterPrimitive};
use length::RsvgLength;

// Required by the C code until all filters are ported to Rust.
// Keep this in sync with
// ../../librsvg/librsvg/rsvg-filter.g:_RsvgFilter
#[repr(C)]
pub struct RsvgFilter {
    pub x: RsvgLength,
    pub y: RsvgLength,
    pub width: RsvgLength,
    pub height: RsvgLength,
    pub filterunits: CoordUnits,
    pub primitiveunits: CoordUnits,
}

pub type RsvgFilterContext = FilterContext;

// Required by the C code until all filters are ported to Rust.
// Keep this in sync with
// ../../librsvg/librsvg/filters/common.h:_RsvgFilterPrimitiveOutput
#[repr(C)]
pub struct RsvgFilterPrimitiveOutput {
    surface: *mut cairo_surface_t,
    bounds: IRect,
}

/// A filter primitive result.
#[derive(Debug, Clone)]
pub struct FilterResult {
    /// The surface after the filter primitive was applied.
    pub surface: cairo::ImageSurface,

    /// The filter primitive subregion.
    pub bounds: IRect,
}

/// The filter rendering context.
#[derive(Debug)]
pub struct FilterContext {
    /// The source graphic surface.
    source_surface: cairo::ImageSurface,
    /// Output of the last filter primitive.
    last_result: Option<FilterResult>,
    /// Surfaces of the previous filter primitives by name.
    previous_results: HashMap<String, FilterResult>,

    affine: cairo::Matrix,
    paffine: cairo::Matrix,
    filter: *mut RsvgFilter,
    drawing_ctx: *mut RsvgDrawingCtx,
    channelmap: [i32; 4],
}

impl FilterContext {
    /// Creates a new `FilterContext`.
    pub fn new(
        filter: *mut RsvgFilter,
        source_surface: cairo::ImageSurface,
        ctx: *mut RsvgDrawingCtx,
        channelmap: [i32; 4],
    ) -> Self {
        assert!(!filter.is_null());

        let state = drawing_ctx::get_current_state(ctx).unwrap();
        let bbox = drawing_ctx::get_bbox(ctx);
        let bbox_rect = bbox.rect.unwrap();

        let affine = match unsafe { (*filter).filterunits } {
            CoordUnits::UserSpaceOnUse => state.affine,
            CoordUnits::ObjectBoundingBox => {
                let affine = cairo::Matrix::new(
                    bbox_rect.width,
                    0f64,
                    0f64,
                    bbox_rect.height,
                    bbox_rect.x,
                    bbox_rect.y,
                );
                cairo::Matrix::multiply(&affine, &state.affine)
            }
        };

        let paffine = match unsafe { (*filter).primitiveunits } {
            CoordUnits::UserSpaceOnUse => state.affine,
            CoordUnits::ObjectBoundingBox => {
                let affine = cairo::Matrix::new(
                    bbox_rect.width,
                    0f64,
                    0f64,
                    bbox_rect.height,
                    bbox_rect.x,
                    bbox_rect.y,
                );
                cairo::Matrix::multiply(&affine, &state.affine)
            }
        };

        let mut rv = Self {
            source_surface,
            last_result: None,
            previous_results: HashMap::new(),
            affine,
            paffine,
            filter,
            drawing_ctx: ctx,
            channelmap,
        };

        let last_result = FilterResult {
            surface: rv.source_surface.clone(),
            bounds: rv.compute_bounds(None, None, None, None),
        };

        rv.last_result = Some(last_result);
        rv
    }

    /// Returns the surface corresponding to the last filter primitive's result.
    #[inline]
    pub fn last_result(&self) -> Option<&FilterResult> {
        self.last_result.as_ref()
    }

    /// Returns the surface corresponding to the source graphic.
    #[inline]
    pub fn source_graphic(&self) -> &cairo::ImageSurface {
        &self.source_surface
    }

    /// Returns the surface corresponding to the background image snapshot.
    #[inline]
    pub fn background_image(&self) -> &cairo::ImageSurface {
        unimplemented!()
    }

    /// Returns the surface corresponding to the result of the given filter primitive.
    #[inline]
    pub fn filter_result(&self, name: &str) -> Option<&FilterResult> {
        self.previous_results.get(name)
    }

    /// Converts this `FilterContext` into the surface corresponding to the result of the filter
    /// chain.
    #[inline]
    pub fn into_result(self) -> cairo::ImageSurface {
        self.last_result
            .map(|FilterResult { surface, .. }| surface)
            .unwrap_or(self.source_surface)
    }

    /// Stores a filter primitive result into the context.
    #[inline]
    pub fn store_result(&mut self, name: Option<String>, result: FilterResult) {
        if let Some(name) = name {
            self.previous_results.insert(name, result.clone());
        }

        self.last_result = Some(result);
    }

    /// Returns the drawing context for this filter context.
    #[inline]
    pub fn drawing_context(&self) -> *const RsvgDrawingCtx {
        self.drawing_ctx
    }

    /// Returns the paffine matrix.
    #[inline]
    pub fn paffine(&self) -> cairo::Matrix {
        self.paffine
    }

    /// Computes and returns the filter primitive bounds.
    pub fn compute_bounds(
        &self,
        x: Option<RsvgLength>,
        y: Option<RsvgLength>,
        width: Option<RsvgLength>,
        height: Option<RsvgLength>,
    ) -> IRect {
        let mut bbox = BoundingBox::new(&cairo::Matrix::identity());

        if unsafe { (*self.filter).filterunits } == CoordUnits::ObjectBoundingBox {
            drawing_ctx::push_view_box(self.drawing_ctx, 1f64, 1f64);
        }

        let rect = cairo::Rectangle {
            x: unsafe { (*self.filter).x.normalize(self.drawing_ctx) },
            y: unsafe { (*self.filter).y.normalize(self.drawing_ctx) },
            width: unsafe { (*self.filter).width.normalize(self.drawing_ctx) },
            height: unsafe { (*self.filter).height.normalize(self.drawing_ctx) },
        };

        if unsafe { (*self.filter).filterunits } == CoordUnits::ObjectBoundingBox {
            drawing_ctx::pop_view_box(self.drawing_ctx);
        }

        let other_bbox = BoundingBox::new(&self.affine).with_rect(Some(rect));
        bbox.insert(&other_bbox);

        if x.is_some() || y.is_some() || width.is_some() || height.is_some() {
            if unsafe { (*self.filter).primitiveunits } == CoordUnits::ObjectBoundingBox {
                drawing_ctx::push_view_box(self.drawing_ctx, 1f64, 1f64);
            }

            let mut rect = cairo::Rectangle {
                x: x.map(|x| x.normalize(self.drawing_ctx)).unwrap_or(0f64),
                y: y.map(|y| y.normalize(self.drawing_ctx)).unwrap_or(0f64),
                ..rect
            };

            if width.is_some() || height.is_some() {
                let (vbox_width, vbox_height) = drawing_ctx::get_view_box_size(self.drawing_ctx);

                rect.width = width
                    .map(|w| w.normalize(self.drawing_ctx))
                    .unwrap_or(vbox_width);
                rect.height = height
                    .map(|h| h.normalize(self.drawing_ctx))
                    .unwrap_or(vbox_height);
            }

            if unsafe { (*self.filter).primitiveunits } == CoordUnits::ObjectBoundingBox {
                drawing_ctx::pop_view_box(self.drawing_ctx);
            }

            let other_bbox = BoundingBox::new(&self.paffine).with_rect(Some(rect));
            bbox.clip(&other_bbox);
        }

        let rect = cairo::Rectangle {
            x: 0f64,
            y: 0f64,
            width: f64::from(self.source_surface.get_width()),
            height: f64::from(self.source_surface.get_height()),
        };
        let other_bbox = BoundingBox::new(&cairo::Matrix::identity()).with_rect(Some(rect));
        bbox.clip(&other_bbox);

        let bbox_rect = bbox.rect.unwrap();
        IRect {
            x0: bbox_rect.x.floor() as i32,
            y0: bbox_rect.y.floor() as i32,
            x1: (bbox_rect.x + bbox_rect.width).ceil() as i32,
            y1: (bbox_rect.y + bbox_rect.height).ceil() as i32,
        }
    }
}

#[no_mangle]
pub unsafe extern "C" fn rsvg_filter_context_get_affine(
    ctx: *const RsvgFilterContext,
) -> cairo::Matrix {
    assert!(!ctx.is_null());

    (*ctx).affine
}

#[no_mangle]
pub unsafe extern "C" fn rsvg_filter_context_get_paffine(
    ctx: *const RsvgFilterContext,
) -> cairo::Matrix {
    assert!(!ctx.is_null());

    (*ctx).paffine
}

#[no_mangle]
pub unsafe extern "C" fn rsvg_filter_context_get_filter(
    ctx: *const RsvgFilterContext,
) -> *const RsvgFilter {
    assert!(!ctx.is_null());

    (*ctx).filter
}

#[no_mangle]
pub unsafe extern "C" fn rsvg_filter_context_get_drawing_ctx(
    ctx: *mut RsvgFilterContext,
) -> *mut RsvgDrawingCtx {
    assert!(!ctx.is_null());

    (*ctx).drawing_ctx
}

#[no_mangle]
pub unsafe extern "C" fn rsvg_filter_context_get_width(ctx: *const RsvgFilterContext) -> i32 {
    assert!(!ctx.is_null());

    (*ctx).source_surface.get_width()
}

#[no_mangle]
pub unsafe extern "C" fn rsvg_filter_context_get_height(ctx: *const RsvgFilterContext) -> i32 {
    assert!(!ctx.is_null());

    (*ctx).source_surface.get_height()
}

#[no_mangle]
pub unsafe extern "C" fn rsvg_filter_context_get_channelmap(
    ctx: *const RsvgFilterContext,
) -> *const i32 {
    assert!(!ctx.is_null());

    (*ctx).channelmap.as_ptr()
}

#[no_mangle]
pub unsafe extern "C" fn rsvg_filter_context_get_source_surface(
    ctx: *mut RsvgFilterContext,
) -> *mut cairo_surface_t {
    assert!(!ctx.is_null());

    (*ctx).source_surface.to_glib_none().0
}

#[no_mangle]
pub unsafe extern "C" fn rsvg_filter_context_get_bg_surface(
    ctx: *mut RsvgFilterContext,
) -> *mut cairo_surface_t {
    assert!(!ctx.is_null());

    (*ctx).background_image().to_glib_none().0
}

#[no_mangle]
pub unsafe extern "C" fn rsvg_filter_context_get_lastresult(
    ctx: *mut RsvgFilterContext,
) -> RsvgFilterPrimitiveOutput {
    assert!(!ctx.is_null());

    match (*ctx).last_result {
        Some(FilterResult {
            ref surface,
            ref bounds,
        }) => RsvgFilterPrimitiveOutput {
            surface: surface.to_glib_none().0,
            bounds: *bounds,
        },
        None => RsvgFilterPrimitiveOutput {
            surface: (*ctx).source_surface.to_glib_none().0,
            bounds: (*ctx).compute_bounds(None, None, None, None),
        },
    }
}

#[no_mangle]
pub unsafe extern "C" fn rsvg_filter_context_get_previous_result(
    name: *mut GString,
    ctx: *mut RsvgFilterContext,
    output: *mut RsvgFilterPrimitiveOutput,
) -> i32 {
    assert!(!name.is_null());
    assert!(!ctx.is_null());
    assert!(!output.is_null());

    if let Some(&FilterResult {
        ref surface,
        ref bounds,
    }) = (*ctx).filter_result(&CStr::from_ptr((*name).str).to_string_lossy())
    {
        *output = RsvgFilterPrimitiveOutput {
            surface: surface.to_glib_none().0,
            bounds: *bounds,
        };
        1
    } else {
        0
    }
}

#[no_mangle]
pub unsafe extern "C" fn rsvg_filter_store_output(
    name: *mut GString,
    result: RsvgFilterPrimitiveOutput,
    ctx: *mut RsvgFilterContext,
) {
    assert!(!name.is_null());
    assert!(!result.surface.is_null());
    assert!(!ctx.is_null());

    let name = from_glib_none((*name).str);

    let surface: cairo::Surface = from_glib_none(result.surface);
    assert_eq!(surface.get_type(), cairo::SurfaceType::Image);
    let surface = cairo::ImageSurface::from(surface).unwrap();

    let result = FilterResult {
        surface,
        bounds: result.bounds,
    };

    (*ctx).store_result(Some(name), result);
}

#[no_mangle]
pub unsafe extern "C" fn rsvg_filter_primitive_get_bounds(
    primitive: *const RsvgFilterPrimitive,
    ctx: *const RsvgFilterContext,
) -> IRect {
    assert!(!ctx.is_null());

    let mut x = None;
    let mut y = None;
    let mut width = None;
    let mut height = None;

    if !primitive.is_null() {
        if (*primitive).x_specified != 0 {
            x = Some((*primitive).x)
        };

        if (*primitive).y_specified != 0 {
            y = Some((*primitive).y)
        };

        if (*primitive).width_specified != 0 {
            width = Some((*primitive).width)
        };

        if (*primitive).height_specified != 0 {
            height = Some((*primitive).height)
        };
    }

    (*ctx).compute_bounds(x, y, width, height)
}
<commit_msg>Clean up unsafe in FilterContext::compute_bounds()<commit_after>use std::collections::HashMap;
use std::ffi::CStr;

use cairo::prelude::SurfaceExt;
use cairo::{self, MatrixTrait};
use cairo_sys::cairo_surface_t;
use glib::translate::{from_glib_none, ToGlibPtr};
use glib_sys::*;

use bbox::BoundingBox;
use coord_units::CoordUnits;
use drawing_ctx::{self, RsvgDrawingCtx};
use filters::{IRect, RsvgFilterPrimitive};
use length::RsvgLength;

// Required by the C code until all filters are ported to Rust.
// Keep this in sync with
// ../../librsvg/librsvg/rsvg-filter.g:_RsvgFilter
#[repr(C)]
pub struct RsvgFilter {
    pub x: RsvgLength,
    pub y: RsvgLength,
    pub width: RsvgLength,
    pub height: RsvgLength,
    pub filterunits: CoordUnits,
    pub primitiveunits: CoordUnits,
}

pub type RsvgFilterContext = FilterContext;

// Required by the C code until all filters are ported to Rust.
// Keep this in sync with
// ../../librsvg/librsvg/filters/common.h:_RsvgFilterPrimitiveOutput
#[repr(C)]
pub struct RsvgFilterPrimitiveOutput {
    surface: *mut cairo_surface_t,
    bounds: IRect,
}

/// A filter primitive result.
#[derive(Debug, Clone)]
pub struct FilterResult {
    /// The surface after the filter primitive was applied.
    pub surface: cairo::ImageSurface,

    /// The filter primitive subregion.
    pub bounds: IRect,
}

/// The filter rendering context.
#[derive(Debug)]
pub struct FilterContext {
    /// The source graphic surface.
    source_surface: cairo::ImageSurface,
    /// Output of the last filter primitive.
    last_result: Option<FilterResult>,
    /// Surfaces of the previous filter primitives by name.
    previous_results: HashMap<String, FilterResult>,

    affine: cairo::Matrix,
    paffine: cairo::Matrix,
    filter: *mut RsvgFilter,
    drawing_ctx: *mut RsvgDrawingCtx,
    channelmap: [i32; 4],
}

impl FilterContext {
    /// Creates a new `FilterContext`.
    pub fn new(
        filter: *mut RsvgFilter,
        source_surface: cairo::ImageSurface,
        ctx: *mut RsvgDrawingCtx,
        channelmap: [i32; 4],
    ) -> Self {
        assert!(!filter.is_null());

        let state = drawing_ctx::get_current_state(ctx).unwrap();
        let bbox = drawing_ctx::get_bbox(ctx);
        let bbox_rect = bbox.rect.unwrap();

        let affine = match unsafe { (*filter).filterunits } {
            CoordUnits::UserSpaceOnUse => state.affine,
            CoordUnits::ObjectBoundingBox => {
                let affine = cairo::Matrix::new(
                    bbox_rect.width,
                    0f64,
                    0f64,
                    bbox_rect.height,
                    bbox_rect.x,
                    bbox_rect.y,
                );
                cairo::Matrix::multiply(&affine, &state.affine)
            }
        };

        let paffine = match unsafe { (*filter).primitiveunits } {
            CoordUnits::UserSpaceOnUse => state.affine,
            CoordUnits::ObjectBoundingBox => {
                let affine = cairo::Matrix::new(
                    bbox_rect.width,
                    0f64,
                    0f64,
                    bbox_rect.height,
                    bbox_rect.x,
                    bbox_rect.y,
                );
                cairo::Matrix::multiply(&affine, &state.affine)
            }
        };

        let mut rv = Self {
            source_surface,
            last_result: None,
            previous_results: HashMap::new(),
            affine,
            paffine,
            filter,
            drawing_ctx: ctx,
            channelmap,
        };

        let last_result = FilterResult {
            surface: rv.source_surface.clone(),
            bounds: rv.compute_bounds(None, None, None, None),
        };

        rv.last_result = Some(last_result);
        rv
    }

    /// Returns the surface corresponding to the last filter primitive's result.
    #[inline]
    pub fn last_result(&self) -> Option<&FilterResult> {
        self.last_result.as_ref()
    }

    /// Returns the surface corresponding to the source graphic.
    #[inline]
    pub fn source_graphic(&self) -> &cairo::ImageSurface {
        &self.source_surface
    }

    /// Returns the surface corresponding to the background image snapshot.
    #[inline]
    pub fn background_image(&self) -> &cairo::ImageSurface {
        unimplemented!()
    }

    /// Returns the surface corresponding to the result of the given filter primitive.
    #[inline]
    pub fn filter_result(&self, name: &str) -> Option<&FilterResult> {
        self.previous_results.get(name)
    }

    /// Converts this `FilterContext` into the surface corresponding to the result of the filter
    /// chain.
    #[inline]
    pub fn into_result(self) -> cairo::ImageSurface {
        self.last_result
            .map(|FilterResult { surface, .. }| surface)
            .unwrap_or(self.source_surface)
    }

    /// Stores a filter primitive result into the context.
    #[inline]
    pub fn store_result(&mut self, name: Option<String>, result: FilterResult) {
        if let Some(name) = name {
            self.previous_results.insert(name, result.clone());
        }

        self.last_result = Some(result);
    }

    /// Returns the drawing context for this filter context.
    #[inline]
    pub fn drawing_context(&self) -> *const RsvgDrawingCtx {
        self.drawing_ctx
    }

    /// Returns the paffine matrix.
    #[inline]
    pub fn paffine(&self) -> cairo::Matrix {
        self.paffine
    }

    /// Computes and returns the filter primitive bounds.
    pub fn compute_bounds(
        &self,
        x: Option<RsvgLength>,
        y: Option<RsvgLength>,
        width: Option<RsvgLength>,
        height: Option<RsvgLength>,
    ) -> IRect {
        let filter = unsafe { &*self.filter };
        let mut bbox = BoundingBox::new(&cairo::Matrix::identity());

        if filter.filterunits == CoordUnits::ObjectBoundingBox {
            drawing_ctx::push_view_box(self.drawing_ctx, 1f64, 1f64);
        }

        let rect = cairo::Rectangle {
            x: filter.x.normalize(self.drawing_ctx),
            y: filter.y.normalize(self.drawing_ctx),
            width: filter.width.normalize(self.drawing_ctx),
            height: filter.height.normalize(self.drawing_ctx),
        };

        if filter.filterunits == CoordUnits::ObjectBoundingBox {
            drawing_ctx::pop_view_box(self.drawing_ctx);
        }

        let other_bbox = BoundingBox::new(&self.affine).with_rect(Some(rect));
        bbox.insert(&other_bbox);

        if x.is_some() || y.is_some() || width.is_some() || height.is_some() {
            if filter.primitiveunits == CoordUnits::ObjectBoundingBox {
                drawing_ctx::push_view_box(self.drawing_ctx, 1f64, 1f64);
            }

            let mut rect = cairo::Rectangle {
                x: x.map(|x| x.normalize(self.drawing_ctx)).unwrap_or(0f64),
                y: y.map(|y| y.normalize(self.drawing_ctx)).unwrap_or(0f64),
                ..rect
            };

            if width.is_some() || height.is_some() {
                let (vbox_width, vbox_height) = drawing_ctx::get_view_box_size(self.drawing_ctx);

                rect.width = width
                    .map(|w| w.normalize(self.drawing_ctx))
                    .unwrap_or(vbox_width);
                rect.height = height
                    .map(|h| h.normalize(self.drawing_ctx))
                    .unwrap_or(vbox_height);
            }

            if filter.primitiveunits == CoordUnits::ObjectBoundingBox {
                drawing_ctx::pop_view_box(self.drawing_ctx);
            }

            let other_bbox = BoundingBox::new(&self.paffine).with_rect(Some(rect));
            bbox.clip(&other_bbox);
        }

        let rect = cairo::Rectangle {
            x: 0f64,
            y: 0f64,
            width: f64::from(self.source_surface.get_width()),
            height: f64::from(self.source_surface.get_height()),
        };
        let other_bbox = BoundingBox::new(&cairo::Matrix::identity()).with_rect(Some(rect));
        bbox.clip(&other_bbox);

        let bbox_rect = bbox.rect.unwrap();
        IRect {
            x0: bbox_rect.x.floor() as i32,
            y0: bbox_rect.y.floor() as i32,
            x1: (bbox_rect.x + bbox_rect.width).ceil() as i32,
            y1: (bbox_rect.y + bbox_rect.height).ceil() as i32,
        }
    }
}

#[no_mangle]
pub unsafe extern "C" fn rsvg_filter_context_get_affine(
    ctx: *const RsvgFilterContext,
) -> cairo::Matrix {
    assert!(!ctx.is_null());

    (*ctx).affine
}

#[no_mangle]
pub unsafe extern "C" fn rsvg_filter_context_get_paffine(
    ctx: *const RsvgFilterContext,
) -> cairo::Matrix {
    assert!(!ctx.is_null());

    (*ctx).paffine
}

#[no_mangle]
pub unsafe extern "C" fn rsvg_filter_context_get_filter(
    ctx: *const RsvgFilterContext,
) -> *const RsvgFilter {
    assert!(!ctx.is_null());

    (*ctx).filter
}

#[no_mangle]
pub unsafe extern "C" fn rsvg_filter_context_get_drawing_ctx(
    ctx: *mut RsvgFilterContext,
) -> *mut RsvgDrawingCtx {
    assert!(!ctx.is_null());

    (*ctx).drawing_ctx
}

#[no_mangle]
pub unsafe extern "C" fn rsvg_filter_context_get_width(ctx: *const RsvgFilterContext) -> i32 {
    assert!(!ctx.is_null());

    (*ctx).source_surface.get_width()
}

#[no_mangle]
pub unsafe extern "C" fn rsvg_filter_context_get_height(ctx: *const RsvgFilterContext) -> i32 {
    assert!(!ctx.is_null());

    (*ctx).source_surface.get_height()
}

#[no_mangle]
pub unsafe extern "C" fn rsvg_filter_context_get_channelmap(
    ctx: *const RsvgFilterContext,
) -> *const i32 {
    assert!(!ctx.is_null());

    (*ctx).channelmap.as_ptr()
}

#[no_mangle]
pub unsafe extern "C" fn rsvg_filter_context_get_source_surface(
    ctx: *mut RsvgFilterContext,
) -> *mut cairo_surface_t {
    assert!(!ctx.is_null());

    (*ctx).source_surface.to_glib_none().0
}

#[no_mangle]
pub unsafe extern "C" fn rsvg_filter_context_get_bg_surface(
    ctx: *mut RsvgFilterContext,
) -> *mut cairo_surface_t {
    assert!(!ctx.is_null());

    (*ctx).background_image().to_glib_none().0
}

#[no_mangle]
pub unsafe extern "C" fn rsvg_filter_context_get_lastresult(
    ctx: *mut RsvgFilterContext,
) -> RsvgFilterPrimitiveOutput {
    assert!(!ctx.is_null());

    match (*ctx).last_result {
        Some(FilterResult {
            ref surface,
            ref bounds,
        }) => RsvgFilterPrimitiveOutput {
            surface: surface.to_glib_none().0,
            bounds: *bounds,
        },
        None => RsvgFilterPrimitiveOutput {
            surface: (*ctx).source_surface.to_glib_none().0,
            bounds: (*ctx).compute_bounds(None, None, None, None),
        },
    }
}

#[no_mangle]
pub unsafe extern "C" fn rsvg_filter_context_get_previous_result(
    name: *mut GString,
    ctx: *mut RsvgFilterContext,
    output: *mut RsvgFilterPrimitiveOutput,
) -> i32 {
    assert!(!name.is_null());
    assert!(!ctx.is_null());
    assert!(!output.is_null());

    if let Some(&FilterResult {
        ref surface,
        ref bounds,
    }) = (*ctx).filter_result(&CStr::from_ptr((*name).str).to_string_lossy())
    {
        *output = RsvgFilterPrimitiveOutput {
            surface: surface.to_glib_none().0,
            bounds: *bounds,
        };
        1
    } else {
        0
    }
}

#[no_mangle]
pub unsafe extern "C" fn rsvg_filter_store_output(
    name: *mut GString,
    result: RsvgFilterPrimitiveOutput,
    ctx: *mut RsvgFilterContext,
) {
    assert!(!name.is_null());
    assert!(!result.surface.is_null());
    assert!(!ctx.is_null());

    let name = from_glib_none((*name).str);

    let surface: cairo::Surface = from_glib_none(result.surface);
    assert_eq!(surface.get_type(), cairo::SurfaceType::Image);
    let surface = cairo::ImageSurface::from(surface).unwrap();

    let result = FilterResult {
        surface,
        bounds: result.bounds,
    };

    (*ctx).store_result(Some(name), result);
}

#[no_mangle]
pub unsafe extern "C" fn rsvg_filter_primitive_get_bounds(
    primitive: *const RsvgFilterPrimitive,
    ctx: *const RsvgFilterContext,
) -> IRect {
    assert!(!ctx.is_null());

    let mut x = None;
    let mut y = None;
    let mut width = None;
    let mut height = None;

    if !primitive.is_null() {
        if (*primitive).x_specified != 0 {
            x = Some((*primitive).x)
        };

        if (*primitive).y_specified != 0 {
            y = Some((*primitive).y)
        };

        if (*primitive).width_specified != 0 {
            width = Some((*primitive).width)
        };

        if (*primitive).height_specified != 0 {
            height = Some((*primitive).height)
        };
    }

    (*ctx).compute_bounds(x, y, width, height)
}
<|endoftext|>


<commit_before><commit_msg>change/uucore ~ add `execution_phrase!()` macro for use with usage messages<commit_after><|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/. */

use context::{SharedStyleContext, StyleContext};
use dom::{OpaqueNode, TNode, TRestyleDamage, UnsafeNode};
use matching::{ApplicableDeclarations, ElementMatchMethods, MatchMethods, StyleSharingResult};
use selector_impl::SelectorImplExt;
use selectors::Element;
use selectors::bloom::BloomFilter;
use std::cell::RefCell;
use util::opts;
use util::tid::tid;

/// Every time we do another layout, the old bloom filters are invalid. This is
/// detected by ticking a generation number every layout.
pub type Generation = u32;

/// A pair of the bloom filter used for css selector matching, and the node to
/// which it applies. This is used to efficiently do `Descendant` selector
/// matches. Thanks to the bloom filter, we can avoid walking up the tree
/// looking for ancestors that aren't there in the majority of cases.
///
/// As we walk down the DOM tree a thread-local bloom filter is built of all the
/// CSS `SimpleSelector`s which are part of a `Descendant` compound selector
/// (i.e. paired with a `Descendant` combinator, in the `next` field of a
/// `CompoundSelector`.
///
/// Before a `Descendant` selector match is tried, it's compared against the
/// bloom filter. If the bloom filter can exclude it, the selector is quickly
/// rejected.
///
/// When done styling a node, all selectors previously inserted into the filter
/// are removed.
///
/// Since a work-stealing queue is used for styling, sometimes, the bloom filter
/// will no longer be the for the parent of the node we're currently on. When
/// this happens, the thread local bloom filter will be thrown away and rebuilt.
thread_local!(
    pub static STYLE_BLOOM: RefCell<Option<(Box<BloomFilter>, UnsafeNode, Generation)>> = RefCell::new(None));

/// Returns the thread local bloom filter.
///
/// If one does not exist, a new one will be made for you. If it is out of date,
/// it will be cleared and reused.
fn take_thread_local_bloom_filter<N, Impl: SelectorImplExt>(parent_node: Option<N>,
                                                            root: OpaqueNode,
                                                            context: &SharedStyleContext<Impl>)
                                                            -> Box<BloomFilter>
                                                            where N: TNode {
    STYLE_BLOOM.with(|style_bloom| {
        match (parent_node, style_bloom.borrow_mut().take()) {
            // Root node. Needs new bloom filter.
            (None,     _  ) => {
                debug!("[{}] No parent, but new bloom filter!", tid());
                box BloomFilter::new()
            }
            // No bloom filter for this thread yet.
            (Some(parent), None) => {
                let mut bloom_filter = box BloomFilter::new();
                insert_ancestors_into_bloom_filter(&mut bloom_filter, parent, root);
                bloom_filter
            }
            // Found cached bloom filter.
            (Some(parent), Some((mut bloom_filter, old_node, old_generation))) => {
                if old_node == parent.to_unsafe() &&
                    old_generation == context.generation {
                    // Hey, the cached parent is our parent! We can reuse the bloom filter.
                    debug!("[{}] Parent matches (={}). Reusing bloom filter.", tid(), old_node.0);
                } else {
                    // Oh no. the cached parent is stale. I guess we need a new one. Reuse the existing
                    // allocation to avoid malloc churn.
                    bloom_filter.clear();
                    insert_ancestors_into_bloom_filter(&mut bloom_filter, parent, root);
                }
                bloom_filter
            },
        }
    })
}

pub fn put_thread_local_bloom_filter<Impl: SelectorImplExt>(bf: Box<BloomFilter>,
                                                            unsafe_node: &UnsafeNode,
                                                            context: &SharedStyleContext<Impl>) {
    STYLE_BLOOM.with(move |style_bloom| {
        assert!(style_bloom.borrow().is_none(),
                "Putting into a never-taken thread-local bloom filter");
        *style_bloom.borrow_mut() = Some((bf, *unsafe_node, context.generation));
    })
}

/// "Ancestors" in this context is inclusive of ourselves.
fn insert_ancestors_into_bloom_filter<N>(bf: &mut Box<BloomFilter>,
                                         mut n: N,
                                         root: OpaqueNode)
                                         where N: TNode {
    debug!("[{}] Inserting ancestors.", tid());
    let mut ancestors = 0;
    loop {
        ancestors += 1;

        n.insert_into_bloom_filter(&mut **bf);
        n = match n.layout_parent_node(root) {
            None => break,
            Some(p) => p,
        };
    }
    debug!("[{}] Inserted {} ancestors.", tid(), ancestors);
}

pub trait DomTraversalContext<N: TNode>  {
    type SharedContext: Sync + 'static;
    fn new<'a>(&'a Self::SharedContext, OpaqueNode) -> Self;
    fn process_preorder(&self, node: N);
    fn process_postorder(&self, node: N);
}

/// The recalc-style-for-node traversal, which styles each node and must run before
/// layout computation. This computes the styles applied to each node.
#[inline]
#[allow(unsafe_code)]
pub fn recalc_style_at<'a, N, C>(context: &'a C,
                                 root: OpaqueNode,
                                 node: N)
    where N: TNode,
          C: StyleContext<'a, <N::ConcreteElement as Element>::Impl>,
          <N::ConcreteElement as Element>::Impl: SelectorImplExt<ComputedValues=N::ConcreteComputedValues> + 'a {
    // Initialize layout data.
    //
    // FIXME(pcwalton): Stop allocating here. Ideally this should just be done by the HTML
    // parser.
    node.initialize_data();

    // Get the parent node.
    let parent_opt = node.layout_parent_node(root);

    // Get the style bloom filter.
    let mut bf = take_thread_local_bloom_filter(parent_opt, root, context.shared_context());

    let nonincremental_layout = opts::get().nonincremental_layout;
    if nonincremental_layout || node.is_dirty() {
        // Remove existing CSS styles from nodes whose content has changed (e.g. text changed),
        // to force non-incremental reflow.
        if node.has_changed() {
            node.unstyle();
        }

        // Check to see whether we can share a style with someone.
        let style_sharing_candidate_cache =
            &mut context.local_context().style_sharing_candidate_cache.borrow_mut();

        let sharing_result = match node.as_element() {
            Some(element) => {
                unsafe {
                    element.share_style_if_possible(style_sharing_candidate_cache,
                                                    parent_opt.clone())
                }
            },
            None => StyleSharingResult::CannotShare,
        };

        // Otherwise, match and cascade selectors.
        match sharing_result {
            StyleSharingResult::CannotShare => {
                let mut applicable_declarations = ApplicableDeclarations::new();

                let shareable_element = match node.as_element() {
                    Some(element) => {
                        // Perform the CSS selector matching.
                        let stylist = &context.shared_context().stylist;

                        if element.match_element(&**stylist,
                                                 Some(&*bf),
                                                 &mut applicable_declarations) {
                            Some(element)
                        } else {
                            None
                        }
                    },
                    None => {
                        if node.has_changed() {
                            node.set_restyle_damage(N::ConcreteRestyleDamage::rebuild_and_reflow())
                        }
                        None
                    },
                };

                // Perform the CSS cascade.
                unsafe {
                    node.cascade_node(&context.shared_context(),
                                      parent_opt,
                                      &applicable_declarations,
                                      &mut context.local_context().applicable_declarations_cache.borrow_mut(),
                                      &context.shared_context().new_animations_sender);
                }

                // Add ourselves to the LRU cache.
                if let Some(element) = shareable_element {
                    style_sharing_candidate_cache.insert_if_possible::<'ln, N>(&element);
                }
            }
            StyleSharingResult::StyleWasShared(index, damage) => {
                style_sharing_candidate_cache.touch(index);
                node.set_restyle_damage(damage);
            }
        }
    }

    let unsafe_layout_node = node.to_unsafe();

    // Before running the children, we need to insert our nodes into the bloom
    // filter.
    debug!("[{}] + {:X}", tid(), unsafe_layout_node.0);
    node.insert_into_bloom_filter(&mut *bf);

    // NB: flow construction updates the bloom filter on the way up.
    put_thread_local_bloom_filter(bf, &unsafe_layout_node, context.shared_context());
}

<commit_msg>Auto merge of #11399 - heycam:parent-style, r=pcwalton<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/. */

use context::{SharedStyleContext, StyleContext};
use dom::{OpaqueNode, TNode, TRestyleDamage, UnsafeNode};
use matching::{ApplicableDeclarations, ElementMatchMethods, MatchMethods, StyleSharingResult};
use selector_impl::SelectorImplExt;
use selectors::Element;
use selectors::bloom::BloomFilter;
use std::cell::RefCell;
use util::opts;
use util::tid::tid;

/// Every time we do another layout, the old bloom filters are invalid. This is
/// detected by ticking a generation number every layout.
pub type Generation = u32;

/// A pair of the bloom filter used for css selector matching, and the node to
/// which it applies. This is used to efficiently do `Descendant` selector
/// matches. Thanks to the bloom filter, we can avoid walking up the tree
/// looking for ancestors that aren't there in the majority of cases.
///
/// As we walk down the DOM tree a thread-local bloom filter is built of all the
/// CSS `SimpleSelector`s which are part of a `Descendant` compound selector
/// (i.e. paired with a `Descendant` combinator, in the `next` field of a
/// `CompoundSelector`.
///
/// Before a `Descendant` selector match is tried, it's compared against the
/// bloom filter. If the bloom filter can exclude it, the selector is quickly
/// rejected.
///
/// When done styling a node, all selectors previously inserted into the filter
/// are removed.
///
/// Since a work-stealing queue is used for styling, sometimes, the bloom filter
/// will no longer be the for the parent of the node we're currently on. When
/// this happens, the thread local bloom filter will be thrown away and rebuilt.
thread_local!(
    pub static STYLE_BLOOM: RefCell<Option<(Box<BloomFilter>, UnsafeNode, Generation)>> = RefCell::new(None));

/// Returns the thread local bloom filter.
///
/// If one does not exist, a new one will be made for you. If it is out of date,
/// it will be cleared and reused.
fn take_thread_local_bloom_filter<N, Impl: SelectorImplExt>(parent_node: Option<N>,
                                                            root: OpaqueNode,
                                                            context: &SharedStyleContext<Impl>)
                                                            -> Box<BloomFilter>
                                                            where N: TNode {
    STYLE_BLOOM.with(|style_bloom| {
        match (parent_node, style_bloom.borrow_mut().take()) {
            // Root node. Needs new bloom filter.
            (None,     _  ) => {
                debug!("[{}] No parent, but new bloom filter!", tid());
                box BloomFilter::new()
            }
            // No bloom filter for this thread yet.
            (Some(parent), None) => {
                let mut bloom_filter = box BloomFilter::new();
                insert_ancestors_into_bloom_filter(&mut bloom_filter, parent, root);
                bloom_filter
            }
            // Found cached bloom filter.
            (Some(parent), Some((mut bloom_filter, old_node, old_generation))) => {
                if old_node == parent.to_unsafe() &&
                    old_generation == context.generation {
                    // Hey, the cached parent is our parent! We can reuse the bloom filter.
                    debug!("[{}] Parent matches (={}). Reusing bloom filter.", tid(), old_node.0);
                } else {
                    // Oh no. the cached parent is stale. I guess we need a new one. Reuse the existing
                    // allocation to avoid malloc churn.
                    bloom_filter.clear();
                    insert_ancestors_into_bloom_filter(&mut bloom_filter, parent, root);
                }
                bloom_filter
            },
        }
    })
}

pub fn put_thread_local_bloom_filter<Impl: SelectorImplExt>(bf: Box<BloomFilter>,
                                                            unsafe_node: &UnsafeNode,
                                                            context: &SharedStyleContext<Impl>) {
    STYLE_BLOOM.with(move |style_bloom| {
        assert!(style_bloom.borrow().is_none(),
                "Putting into a never-taken thread-local bloom filter");
        *style_bloom.borrow_mut() = Some((bf, *unsafe_node, context.generation));
    })
}

/// "Ancestors" in this context is inclusive of ourselves.
fn insert_ancestors_into_bloom_filter<N>(bf: &mut Box<BloomFilter>,
                                         mut n: N,
                                         root: OpaqueNode)
                                         where N: TNode {
    debug!("[{}] Inserting ancestors.", tid());
    let mut ancestors = 0;
    loop {
        ancestors += 1;

        n.insert_into_bloom_filter(&mut **bf);
        n = match n.layout_parent_node(root) {
            None => break,
            Some(p) => p,
        };
    }
    debug!("[{}] Inserted {} ancestors.", tid(), ancestors);
}

pub trait DomTraversalContext<N: TNode>  {
    type SharedContext: Sync + 'static;
    fn new<'a>(&'a Self::SharedContext, OpaqueNode) -> Self;
    fn process_preorder(&self, node: N);
    fn process_postorder(&self, node: N);
}

/// The recalc-style-for-node traversal, which styles each node and must run before
/// layout computation. This computes the styles applied to each node.
#[inline]
#[allow(unsafe_code)]
pub fn recalc_style_at<'a, N, C>(context: &'a C,
                                 root: OpaqueNode,
                                 node: N)
    where N: TNode,
          C: StyleContext<'a, <N::ConcreteElement as Element>::Impl>,
          <N::ConcreteElement as Element>::Impl: SelectorImplExt<ComputedValues=N::ConcreteComputedValues> + 'a {
    // Initialize layout data.
    //
    // FIXME(pcwalton): Stop allocating here. Ideally this should just be done by the HTML
    // parser.
    node.initialize_data();

    // Get the parent node.
    let parent_opt = match node.parent_node() {
        Some(parent) if parent.is_element() => Some(parent),
        _ => None,
    };

    // Get the style bloom filter.
    let mut bf = take_thread_local_bloom_filter(parent_opt, root, context.shared_context());

    let nonincremental_layout = opts::get().nonincremental_layout;
    if nonincremental_layout || node.is_dirty() {
        // Remove existing CSS styles from nodes whose content has changed (e.g. text changed),
        // to force non-incremental reflow.
        if node.has_changed() {
            node.unstyle();
        }

        // Check to see whether we can share a style with someone.
        let style_sharing_candidate_cache =
            &mut context.local_context().style_sharing_candidate_cache.borrow_mut();

        let sharing_result = match node.as_element() {
            Some(element) => {
                unsafe {
                    element.share_style_if_possible(style_sharing_candidate_cache,
                                                    parent_opt.clone())
                }
            },
            None => StyleSharingResult::CannotShare,
        };

        // Otherwise, match and cascade selectors.
        match sharing_result {
            StyleSharingResult::CannotShare => {
                let mut applicable_declarations = ApplicableDeclarations::new();

                let shareable_element = match node.as_element() {
                    Some(element) => {
                        // Perform the CSS selector matching.
                        let stylist = &context.shared_context().stylist;

                        if element.match_element(&**stylist,
                                                 Some(&*bf),
                                                 &mut applicable_declarations) {
                            Some(element)
                        } else {
                            None
                        }
                    },
                    None => {
                        if node.has_changed() {
                            node.set_restyle_damage(N::ConcreteRestyleDamage::rebuild_and_reflow())
                        }
                        None
                    },
                };

                // Perform the CSS cascade.
                unsafe {
                    node.cascade_node(&context.shared_context(),
                                      parent_opt,
                                      &applicable_declarations,
                                      &mut context.local_context().applicable_declarations_cache.borrow_mut(),
                                      &context.shared_context().new_animations_sender);
                }

                // Add ourselves to the LRU cache.
                if let Some(element) = shareable_element {
                    style_sharing_candidate_cache.insert_if_possible::<'ln, N>(&element);
                }
            }
            StyleSharingResult::StyleWasShared(index, damage) => {
                style_sharing_candidate_cache.touch(index);
                node.set_restyle_damage(damage);
            }
        }
    }

    let unsafe_layout_node = node.to_unsafe();

    // Before running the children, we need to insert our nodes into the bloom
    // filter.
    debug!("[{}] + {:X}", tid(), unsafe_layout_node.0);
    node.insert_into_bloom_filter(&mut *bf);

    // NB: flow construction updates the bloom filter on the way up.
    put_thread_local_bloom_filter(bf, &unsafe_layout_node, context.shared_context());
}

<|endoftext|>


<commit_before>extern mod std;

use std::{io, vec, ptr, os};
use std::libc::{c_uint};

mod crypto {
  use std::libc::{c_uint};
#[link_args = "-lcrypto"]
  extern {
    fn SHA1(src: *u8, sz: c_uint, out: *u8) -> *u8;
    fn MD5(src: *u8, sz: c_uint, out: *u8) -> *u8;
    fn SHA224(src: *u8, sz: c_uint, out: *u8) -> *u8;
    fn SHA256(src: *u8, sz: c_uint, out: *u8) -> *u8;
    fn SHA384(src: *u8, sz: c_uint, out: *u8) -> *u8;
    fn SHA512(src: *u8, sz: c_uint, out: *u8) -> *u8;
  }
}

enum HashMethod { MD5, SHA1, SHA224, SHA256, SHA384, SHA512 }

struct HashEngine {
  engine:      (extern "C" unsafe fn(*u8, c_uint, *u8) -> *u8),
  digest_size: uint,
  block_size:  uint
}

impl HashEngine {
  fn new(engine: HashMethod) -> ~HashEngine {
    match engine {
      MD5    => ~HashEngine{ engine: crypto::MD5,    digest_size: 16, block_size: 64  },
      SHA1   => ~HashEngine{ engine: crypto::SHA1,   digest_size: 20, block_size: 64  },
      SHA224 => ~HashEngine{ engine: crypto::SHA224, digest_size: 28, block_size: 64  },
      SHA256 => ~HashEngine{ engine: crypto::SHA256, digest_size: 32, block_size: 64  },
      SHA384 => ~HashEngine{ engine: crypto::SHA384, digest_size: 48, block_size: 64  },
      SHA512 => ~HashEngine{ engine: crypto::SHA512, digest_size: 64, block_size: 128 }
    }
  }

  fn hash(&self, data: ~[u8]) -> ~Digest {
    let hash_func = self.engine;
    Digest::new(
      unsafe {
        vec::from_buf(
          hash_func(
            vec::raw::to_ptr(data),
            data.len() as c_uint, ptr::null()
          ),
          self.digest_size
        )
      }
    )
  }

  fn hmac(&self, key: ~[u8], message: ~[u8]) -> ~Digest {
    let computed_key = match key.len() {
      _ if key.len() > self.block_size => self.hash(key).digest,
      _ if key.len() < self.block_size => key + vec::from_elem(self.block_size - key.len(), 0),
      _ => key
    };

    let o_key_pad = HashEngine::xor_with(computed_key, 0x5c);
    let i_key_pad = HashEngine::xor_with(computed_key, 0x36);

    self.hash(o_key_pad + self.hash(i_key_pad + message).digest)
  }

  priv fn xor_with(subject: &[u8], val: u8) -> ~[u8] {
    do subject.map |&c| { val ^ c }
  }
}

struct Digest { digest: ~[u8] }

impl Digest {
  fn new(digest: ~[u8]) -> ~Digest { return ~Digest{ digest: digest } }

  fn hexdigest(&self) -> ~str {
    let mut acc = ~"";
    for self.digest.iter().advance |&byte| { acc = acc.append(fmt!("%02x", byte as uint)); }
    acc
  }
}

fn sha1(data: ~[u8]) -> ~Digest {
  HashEngine::new(SHA1).hash(data)
}

fn sha224(data: ~[u8]) -> ~Digest {
  HashEngine::new(SHA224).hash(data)
}

fn sha256(data: ~[u8]) -> ~Digest {
  HashEngine::new(SHA256).hash(data)
}

fn sha384(data: ~[u8]) -> ~Digest {
  HashEngine::new(SHA384).hash(data)
}

fn sha512(data: ~[u8]) -> ~Digest {
  HashEngine::new(SHA512).hash(data)
}

fn md5(data: ~[u8]) -> ~Digest {
  HashEngine::new(MD5).hash(data)
}

fn hmac_md5(key: ~[u8], message: ~[u8]) -> ~Digest {
  HashEngine::new(MD5).hmac(key, message)
}

fn hmac_sha1(key: ~[u8], message: ~[u8]) -> ~Digest {
  HashEngine::new(SHA1).hmac(key, message)
}

fn hmac_sha256(key: ~[u8], message: ~[u8]) -> ~Digest {
  HashEngine::new(SHA256).hmac(key, message)
}

fn hmac_sha512(key: ~[u8], message: ~[u8]) -> ~Digest {
  HashEngine::new(SHA512).hmac(key, message)
}

fn main() {
  if os::args().len() < 2 { fail!(~"Nothing to hash: supply an argument") }
  let args = ~os::args();
  io::println(sha1(args[1].as_bytes().to_owned()).hexdigest());
}

#[test]
fn test_sha1() {
  assert_eq!(sha1("testing SHA1".as_bytes().to_owned()).hexdigest(), ~"a07bb2f7f56b7b47e54fd9e29b4629c8967dde4c");
}

#[test]
fn test_md5() {
  assert_eq!(md5("testing MD5".as_bytes().to_owned()).hexdigest(), ~"da35df95d34f166890fa759f5e00e94c");
}

#[test]
fn test_hmac_sha1_with_a_64_byte_key() {
  assert_eq!(
    hmac_sha1("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx".as_bytes().to_owned(), "some data to hmac".as_bytes().to_owned()).hexdigest(),
    ~"d6264ce636b593e6c5b43c90ec4ca3550032c99f"
  )
}

#[test]
fn test_hmac_sha1_with_a_too_long_key() {
  assert_eq!(
    hmac_sha1("keyxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx".as_bytes().to_owned(), "some data to hmac".as_bytes().to_owned()).hexdigest(),
    ~"4361f0a10ae7fc1ce33d2795e998d0e526a38815"
  )
}

#[test]
fn test_hmac_sha1_with_a_too_short_key() {
  assert_eq!(
    hmac_sha1("my key".as_bytes().to_owned(), "some data to hmac".as_bytes().to_owned()).hexdigest(),
    ~"331c9bf4d7dc8ad7e9bab2f566c8612042a9f4e2"
  )
}

#[test]
fn test_hmac_sha256() {
  assert_eq!(
    hmac_sha256("my key".as_bytes().to_owned(), "some data to hmac".as_bytes().to_owned()).hexdigest(),
    ~"17764a8a2e8c23ae3b9e1781c5ebeb5754c7920806021032e9133051e10118be"
  );
}

#[test]
fn test_hmac_sha512() {
  assert_eq!(
    hmac_sha512("my key".as_bytes().to_owned(), "some data to hmac".as_bytes().to_owned()).hexdigest(),
    ~"b54973feba2e436c4f0911855c80e7320d72edd37b359d6474d714718c52775f163832b79214e9fa9d400208d21372a465b2cda2a1d5ab488bbc02b0daea9626"
  );
}

#[test]
fn test_hmac_md5() {
  assert_eq!(hmac_md5("my key".as_bytes().to_owned(), "some data to hmac".as_bytes().to_owned()).hexdigest(), ~"befd13e00ee07549520ade402c664a3a");
}
<commit_msg>Refactor a bit, fix HMAC for very long keys to match OpenSSL.<commit_after>extern mod std;

use std::{io, vec, ptr, os};
use std::libc::{c_uint};

mod crypto {
  use std::libc::{c_uint};
#[link_args = "-lcrypto"]
  extern {
    fn SHA1(src: *u8, sz: c_uint, out: *u8) -> *u8;
    fn MD5(src: *u8, sz: c_uint, out: *u8) -> *u8;
    fn SHA224(src: *u8, sz: c_uint, out: *u8) -> *u8;
    fn SHA256(src: *u8, sz: c_uint, out: *u8) -> *u8;
    fn SHA384(src: *u8, sz: c_uint, out: *u8) -> *u8;
    fn SHA512(src: *u8, sz: c_uint, out: *u8) -> *u8;
  }
}

enum HashMethod { MD5, SHA1, SHA224, SHA256, SHA384, SHA512 }

struct HashEngine {
  engine:      (extern "C" unsafe fn(*u8, c_uint, *u8) -> *u8),
  digest_size: uint,
  block_size:  uint
}

impl HashEngine {
  fn new(engine: HashMethod) -> ~HashEngine {
    match engine {
      MD5    => ~HashEngine{ engine: crypto::MD5,    digest_size: 16, block_size: 64  },
      SHA1   => ~HashEngine{ engine: crypto::SHA1,   digest_size: 20, block_size: 64  },
      SHA224 => ~HashEngine{ engine: crypto::SHA224, digest_size: 28, block_size: 64  },
      SHA256 => ~HashEngine{ engine: crypto::SHA256, digest_size: 32, block_size: 64  },
      SHA384 => ~HashEngine{ engine: crypto::SHA384, digest_size: 48, block_size: 64  },
      SHA512 => ~HashEngine{ engine: crypto::SHA512, digest_size: 64, block_size: 128 }
    }
  }

  fn hash(&self, data: ~[u8]) -> ~Digest {
    let hash_func = self.engine;
    Digest::new(
      unsafe {
        vec::from_buf(
          hash_func(
            vec::raw::to_ptr(data),
            data.len() as c_uint, ptr::null()
          ),
          self.digest_size
        )
      }
    )
  }

  fn hmac(&self, key: ~[u8], message: ~[u8]) -> ~Digest {
    let computed_key = match key.len() {
      _ if key.len() > self.block_size => self.zero_pad(self.hash(key).digest),
      _ if key.len() < self.block_size => self.zero_pad(key),
      _ => key
    };

    let o_key_pad = HashEngine::xor_with(computed_key, 0x5c);
    let i_key_pad = HashEngine::xor_with(computed_key, 0x36);

    self.hash(o_key_pad + self.hash(i_key_pad + message).digest)
  }

  priv fn xor_with(subject: &[u8], val: u8) -> ~[u8] {
    do subject.map |&c| { val ^ c }
  }

  priv fn zero_pad(&self, subject: ~[u8]) -> ~[u8] {
  	subject + vec::from_elem(self.block_size - subject.len(), 0)
  }
}

struct Digest { digest: ~[u8] }

impl Digest {
  fn new(digest: ~[u8]) -> ~Digest { return ~Digest{ digest: digest } }

  fn hexdigest(&self) -> ~str {
    let mut acc = ~"";
    for self.digest.iter().advance |&byte| { acc = acc.append(fmt!("%02x", byte as uint)); }
    acc
  }
}

fn sha1(data: ~[u8]) -> ~Digest {
  HashEngine::new(SHA1).hash(data)
}

fn sha224(data: ~[u8]) -> ~Digest {
  HashEngine::new(SHA224).hash(data)
}

fn sha256(data: ~[u8]) -> ~Digest {
  HashEngine::new(SHA256).hash(data)
}

fn sha384(data: ~[u8]) -> ~Digest {
  HashEngine::new(SHA384).hash(data)
}

fn sha512(data: ~[u8]) -> ~Digest {
  HashEngine::new(SHA512).hash(data)
}

fn md5(data: ~[u8]) -> ~Digest {
  HashEngine::new(MD5).hash(data)
}

fn hmac_md5(key: ~[u8], message: ~[u8]) -> ~Digest {
  HashEngine::new(MD5).hmac(key, message)
}

fn hmac_sha1(key: ~[u8], message: ~[u8]) -> ~Digest {
  HashEngine::new(SHA1).hmac(key, message)
}

fn hmac_sha256(key: ~[u8], message: ~[u8]) -> ~Digest {
  HashEngine::new(SHA256).hmac(key, message)
}

fn hmac_sha512(key: ~[u8], message: ~[u8]) -> ~Digest {
  HashEngine::new(SHA512).hmac(key, message)
}

fn main() {
  if os::args().len() < 2 { fail!(~"Nothing to hash: supply an argument") }
  let args = ~os::args();
  io::println(sha1(args[1].as_bytes().to_owned()).hexdigest());
}

#[test]
fn test_sha1() {
  assert_eq!(sha1("testing SHA1".as_bytes().to_owned()).hexdigest(), ~"a07bb2f7f56b7b47e54fd9e29b4629c8967dde4c");
}

#[test]
fn test_md5() {
  assert_eq!(md5("testing MD5".as_bytes().to_owned()).hexdigest(), ~"da35df95d34f166890fa759f5e00e94c");
}

#[test]
fn test_hmac_sha1_with_a_64_byte_key() {
  assert_eq!(
    hmac_sha1("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx".as_bytes().to_owned(), "some data to hmac".as_bytes().to_owned()).hexdigest(),
    ~"d6264ce636b593e6c5b43c90ec4ca3550032c99f"
  )
}

#[test]
fn test_hmac_sha1_with_a_too_long_key() {
  assert_eq!(
    hmac_sha1("keyxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx".as_bytes().to_owned(), "some data to hmac".as_bytes().to_owned()).hexdigest(),
    ~"4361f0a10ae7fc1ce33d2795e998d0e526a38815"
  )
}

#[test]
fn test_hmac_sha1_with_a_too_short_key() {
  assert_eq!(
    hmac_sha1("my key".as_bytes().to_owned(), "some data to hmac".as_bytes().to_owned()).hexdigest(),
    ~"331c9bf4d7dc8ad7e9bab2f566c8612042a9f4e2"
  )
}

#[test]
fn test_hmac_sha256() {
  assert_eq!(
    hmac_sha256("my key".as_bytes().to_owned(), "some data to hmac".as_bytes().to_owned()).hexdigest(),
    ~"17764a8a2e8c23ae3b9e1781c5ebeb5754c7920806021032e9133051e10118be"
  );
}

#[test]
fn test_hmac_sha512() {
  assert_eq!(
    hmac_sha512("my key".as_bytes().to_owned(), "some data to hmac".as_bytes().to_owned()).hexdigest(),
    ~"b54973feba2e436c4f0911855c80e7320d72edd37b359d6474d714718c52775f163832b79214e9fa9d400208d21372a465b2cda2a1d5ab488bbc02b0daea9626"
  );
}

#[test]
fn test_hmac_md5() {
  assert_eq!(hmac_md5("my key".as_bytes().to_owned(), "some data to hmac".as_bytes().to_owned()).hexdigest(), ~"befd13e00ee07549520ade402c664a3a");
}
<|endoftext|>


<commit_before>//! System Mutexes
//!
//! The Windows implementation of mutexes is a little odd and it may not be
//! immediately obvious what's going on. The primary oddness is that SRWLock is
//! used instead of CriticalSection, and this is done because:
//!
//! 1. SRWLock is several times faster than CriticalSection according to
//!    benchmarks performed on both Windows 8 and Windows 7.
//!
//! 2. CriticalSection allows recursive locking while SRWLock deadlocks. The
//!    Unix implementation deadlocks so consistency is preferred. See #19962 for
//!    more details.
//!
//! 3. While CriticalSection is fair and SRWLock is not, the current Rust policy
//!    is that there are no guarantees of fairness.
//!
//! The downside of this approach, however, is that SRWLock is not available on
//! Windows XP, so we continue to have a fallback implementation where
//! CriticalSection is used and we keep track of who's holding the mutex to
//! detect recursive locks.

use crate::cell::UnsafeCell;
use crate::mem::{self, MaybeUninit};
use crate::sync::atomic::{AtomicUsize, Ordering};
use crate::sys::c;
use crate::sys::compat;

pub struct Mutex {
    lock: AtomicUsize,
    held: UnsafeCell<bool>,
}

unsafe impl Send for Mutex {}
unsafe impl Sync for Mutex {}

#[derive(Clone, Copy)]
enum Kind {
    SRWLock = 1,
    CriticalSection = 2,
}

#[inline]
pub unsafe fn raw(m: &Mutex) -> c::PSRWLOCK {
    debug_assert!(mem::size_of::<c::SRWLOCK>() <= mem::size_of_val(&m.lock));
    &m.lock as *const _ as *mut _
}

impl Mutex {
    pub const fn new() -> Mutex {
        Mutex {
            // This works because SRWLOCK_INIT is 0 (wrapped in a struct), so we are also properly
            // initializing an SRWLOCK here.
            lock: AtomicUsize::new(0),
            held: UnsafeCell::new(false),
        }
    }
    #[inline]
    pub unsafe fn init(&mut self) {}
    pub unsafe fn lock(&self) {
        match kind() {
            Kind::SRWLock => c::AcquireSRWLockExclusive(raw(self)),
            Kind::CriticalSection => {
                let re = self.remutex();
                (*re).lock();
                if !self.flag_locked() {
                    (*re).unlock();
                    panic!("cannot recursively lock a mutex");
                }
            }
        }
    }
    pub unsafe fn try_lock(&self) -> bool {
        match kind() {
            Kind::SRWLock => c::TryAcquireSRWLockExclusive(raw(self)) != 0,
            Kind::CriticalSection => {
                let re = self.remutex();
                if !(*re).try_lock() {
                    false
                } else if self.flag_locked() {
                    true
                } else {
                    (*re).unlock();
                    false
                }
            }
        }
    }
    pub unsafe fn unlock(&self) {
        *self.held.get() = false;
        match kind() {
            Kind::SRWLock => c::ReleaseSRWLockExclusive(raw(self)),
            Kind::CriticalSection => (*self.remutex()).unlock(),
        }
    }
    pub unsafe fn destroy(&self) {
        match kind() {
            Kind::SRWLock => {}
            Kind::CriticalSection => match self.lock.load(Ordering::SeqCst) {
                0 => {}
                n => {
                    Box::from_raw(n as *mut ReentrantMutex).destroy();
                }
            },
        }
    }

    unsafe fn remutex(&self) -> *mut ReentrantMutex {
        match self.lock.load(Ordering::SeqCst) {
            0 => {}
            n => return n as *mut _,
        }
        let re = box ReentrantMutex::uninitialized();
        re.init();
        let re = Box::into_raw(re);
        match self.lock.compare_and_swap(0, re as usize, Ordering::SeqCst) {
            0 => re,
            n => {
                Box::from_raw(re).destroy();
                n as *mut _
            }
        }
    }

    unsafe fn flag_locked(&self) -> bool {
        if *self.held.get() {
            false
        } else {
            *self.held.get() = true;
            true
        }
    }
}

fn kind() -> Kind {
    static KIND: AtomicUsize = AtomicUsize::new(0);

    let val = KIND.load(Ordering::SeqCst);
    if val == Kind::SRWLock as usize {
        return Kind::SRWLock;
    } else if val == Kind::CriticalSection as usize {
        return Kind::CriticalSection;
    }

    let ret = match compat::lookup("kernel32", "AcquireSRWLockExclusive") {
        None => Kind::CriticalSection,
        Some(..) => Kind::SRWLock,
    };
    KIND.store(ret as usize, Ordering::SeqCst);
    ret
}

pub struct ReentrantMutex {
    inner: UnsafeCell<MaybeUninit<c::CRITICAL_SECTION>>,
}

unsafe impl Send for ReentrantMutex {}
unsafe impl Sync for ReentrantMutex {}

impl ReentrantMutex {
    pub const fn uninitialized() -> ReentrantMutex {
        ReentrantMutex { inner: UnsafeCell::new(MaybeUninit::uninit()) }
    }

    pub unsafe fn init(&self) {
        c::InitializeCriticalSection((&mut *self.inner.get()).as_mut_ptr());
    }

    pub unsafe fn lock(&self) {
        c::EnterCriticalSection((&mut *self.inner.get()).as_mut_ptr());
    }

    #[inline]
    pub unsafe fn try_lock(&self) -> bool {
        c::TryEnterCriticalSection((&mut *self.inner.get()).as_mut_ptr()) != 0
    }

    pub unsafe fn unlock(&self) {
        c::LeaveCriticalSection((&mut *self.inner.get()).as_mut_ptr());
    }

    pub unsafe fn destroy(&self) {
        c::DeleteCriticalSection((&mut *self.inner.get()).as_mut_ptr());
    }
}
<commit_msg>Small cleanups in Windows Mutex.<commit_after>//! System Mutexes
//!
//! The Windows implementation of mutexes is a little odd and it may not be
//! immediately obvious what's going on. The primary oddness is that SRWLock is
//! used instead of CriticalSection, and this is done because:
//!
//! 1. SRWLock is several times faster than CriticalSection according to
//!    benchmarks performed on both Windows 8 and Windows 7.
//!
//! 2. CriticalSection allows recursive locking while SRWLock deadlocks. The
//!    Unix implementation deadlocks so consistency is preferred. See #19962 for
//!    more details.
//!
//! 3. While CriticalSection is fair and SRWLock is not, the current Rust policy
//!    is that there are no guarantees of fairness.
//!
//! The downside of this approach, however, is that SRWLock is not available on
//! Windows XP, so we continue to have a fallback implementation where
//! CriticalSection is used and we keep track of who's holding the mutex to
//! detect recursive locks.

use crate::cell::{Cell, UnsafeCell};
use crate::mem::{self, MaybeUninit};
use crate::sync::atomic::{AtomicUsize, Ordering};
use crate::sys::c;
use crate::sys::compat;

pub struct Mutex {
    // This is either directly an SRWLOCK (if supported), or a Box<Inner> otherwise.
    lock: AtomicUsize,
}

unsafe impl Send for Mutex {}
unsafe impl Sync for Mutex {}

struct Inner {
    remutex: ReentrantMutex,
    held: Cell<bool>,
}

#[derive(Clone, Copy)]
enum Kind {
    SRWLock = 1,
    CriticalSection = 2,
}

#[inline]
pub unsafe fn raw(m: &Mutex) -> c::PSRWLOCK {
    debug_assert!(mem::size_of::<c::SRWLOCK>() <= mem::size_of_val(&m.lock));
    &m.lock as *const _ as *mut _
}

impl Mutex {
    pub const fn new() -> Mutex {
        Mutex {
            // This works because SRWLOCK_INIT is 0 (wrapped in a struct), so we are also properly
            // initializing an SRWLOCK here.
            lock: AtomicUsize::new(0),
        }
    }
    #[inline]
    pub unsafe fn init(&mut self) {}
    pub unsafe fn lock(&self) {
        match kind() {
            Kind::SRWLock => c::AcquireSRWLockExclusive(raw(self)),
            Kind::CriticalSection => {
                let inner = &mut *self.inner();
                inner.remutex.lock();
                if inner.held.replace(true) {
                    // It was already locked, so we got a recursive lock which we do not want.
                    inner.remutex.unlock();
                    panic!("cannot recursively lock a mutex");
                }
            }
        }
    }
    pub unsafe fn try_lock(&self) -> bool {
        match kind() {
            Kind::SRWLock => c::TryAcquireSRWLockExclusive(raw(self)) != 0,
            Kind::CriticalSection => {
                let inner = &mut *self.inner();
                if !inner.remutex.try_lock() {
                    false
                } else if inner.held.replace(true) {
                    // It was already locked, so we got a recursive lock which we do not want.
                    inner.remutex.unlock();
                    false
                } else {
                    true
                }
            }
        }
    }
    pub unsafe fn unlock(&self) {
        match kind() {
            Kind::SRWLock => c::ReleaseSRWLockExclusive(raw(self)),
            Kind::CriticalSection => {
                let inner = &mut *(self.lock.load(Ordering::SeqCst) as *mut Inner);
                inner.held.set(false);
                inner.remutex.unlock();
            }
        }
    }
    pub unsafe fn destroy(&self) {
        match kind() {
            Kind::SRWLock => {}
            Kind::CriticalSection => match self.lock.load(Ordering::SeqCst) {
                0 => {}
                n => {
                    Box::from_raw(n as *mut Inner).remutex.destroy();
                }
            },
        }
    }

    unsafe fn inner(&self) -> *mut Inner {
        match self.lock.load(Ordering::SeqCst) {
            0 => {}
            n => return n as *mut _,
        }
        let inner = box Inner { remutex: ReentrantMutex::uninitialized(), held: Cell::new(false) };
        inner.remutex.init();
        let inner = Box::into_raw(inner);
        match self.lock.compare_and_swap(0, inner as usize, Ordering::SeqCst) {
            0 => inner,
            n => {
                Box::from_raw(inner).remutex.destroy();
                n as *mut _
            }
        }
    }
}

fn kind() -> Kind {
    static KIND: AtomicUsize = AtomicUsize::new(0);

    let val = KIND.load(Ordering::SeqCst);
    if val == Kind::SRWLock as usize {
        return Kind::SRWLock;
    } else if val == Kind::CriticalSection as usize {
        return Kind::CriticalSection;
    }

    let ret = match compat::lookup("kernel32", "AcquireSRWLockExclusive") {
        None => Kind::CriticalSection,
        Some(..) => Kind::SRWLock,
    };
    KIND.store(ret as usize, Ordering::SeqCst);
    ret
}

pub struct ReentrantMutex {
    inner: UnsafeCell<MaybeUninit<c::CRITICAL_SECTION>>,
}

unsafe impl Send for ReentrantMutex {}
unsafe impl Sync for ReentrantMutex {}

impl ReentrantMutex {
    pub const fn uninitialized() -> ReentrantMutex {
        ReentrantMutex { inner: UnsafeCell::new(MaybeUninit::uninit()) }
    }

    pub unsafe fn init(&self) {
        c::InitializeCriticalSection((&mut *self.inner.get()).as_mut_ptr());
    }

    pub unsafe fn lock(&self) {
        c::EnterCriticalSection((&mut *self.inner.get()).as_mut_ptr());
    }

    #[inline]
    pub unsafe fn try_lock(&self) -> bool {
        c::TryEnterCriticalSection((&mut *self.inner.get()).as_mut_ptr()) != 0
    }

    pub unsafe fn unlock(&self) {
        c::LeaveCriticalSection((&mut *self.inner.get()).as_mut_ptr());
    }

    pub unsafe fn destroy(&self) {
        c::DeleteCriticalSection((&mut *self.inner.get()).as_mut_ptr());
    }
}
<|endoftext|>


<commit_before>use core::raw::Slice;
use core::mem::transmute;

pub struct VBE {
    control_info: u32,
    mode_info   : u32,
    mode        : u16,
    iface_seg   : u16,
    iface_off   : u16,
    iface_len   : u16,
}

pub struct FB {
    addr        : u64,
    pitch       : u32,
    width       : u32,
    height      : u32,
    bpp         : u8,
    fbtype      : u8,
    colors      : [u8, ..6],
}

#[allow(dead_code)]
pub enum FBType {
    Indexed = 0,
    RGB = 1,
    Text = 2,
}

#[allow(dead_code)]
pub struct FBPalette {
    addr        : u32,
    count       : u16,
}

#[allow(dead_code)]
pub struct FBPixelFormat {
    red_shift   : u8,
    red_mask    : u8,
    green_shift : u8,
    green_mask  : u8,
    blue_shift  : u8,
    blue_mask   : u8,
}

pub enum InfoFlags {
    MemorySize = 1,
    BootDevice = 2,
    CommandLine = 4,
    Modules = 8,
    // There are two formats for the symbol bit of the info struct, not sure
    // what they are though.
    Symbols = 16 | 32,
    Symbols1 = 16,
    Symbols2 = 32,
    MemoryMap = 64,
    Drives = 128,
    ConfigTable = 256,
    LoaderName = 512,
    APMTable = 1024,
    VBEInfo = 2048
}

pub struct Info {
    pub flags   : u32,
// if has(MemorySize)
    pub mem_lower   : u32,
    pub mem_upper   : u32,
// if flags[1]:
    pub boot_devices: u32,

// if has(CommandLine)
    pub cmdline     : u32,

// if has(Modules)
    pub mods_count  : u32,
    pub mods_addr   : u32,

//
    syms        : [u32, ..4],

// if has(MMap)
    pub mmap_length : u32,
    pub mmap_addr   : u32,

    drives_length: u32,
    drives_addr : u32,

    config_table: u32,

    boot_loader : u32,
    apm_table   : u32,

    vbe         : VBE,
    fb          : FB,
}

impl Info {
    pub fn has(&self, flag : InfoFlags) -> bool {
        self.flags & (flag as u32) != 0
    }

    pub fn modules(&self, make_ptr : |uint| -> *const Module) -> &[Module] {
        unsafe { transmute(Slice {
            data : make_ptr(self.mods_addr as uint),
            len : self.mods_count as uint
        }) }
    }
}

#[allow(dead_code)] // Spurious dead-code warning?
pub struct Module {
    pub start   : u32,
    pub end     : u32,
    pub string  : u32,
    reserved    : u32,
}

#[packed]
pub struct MemoryMapItem {
    // Size of item (bytes), *not* including the item_size field
    pub item_size   : u32,
    pub start       : u64,
    pub length      : u64,
    // See values from MemoryTypes
    pub item_type   : u32,
}

pub enum MemoryTypes {
    MemoryTypeMemory = 1,
    MemoryTypeReserved = 2,
    MemoryTypeACPIRCL = 3,
    MemoryTypeACPISomething = 4,
}
<commit_msg>Fix memory init - another #[packed]<commit_after>use core::raw::Slice;
use core::mem::transmute;

pub struct VBE {
    control_info: u32,
    mode_info   : u32,
    mode        : u16,
    iface_seg   : u16,
    iface_off   : u16,
    iface_len   : u16,
}

pub struct FB {
    addr        : u64,
    pitch       : u32,
    width       : u32,
    height      : u32,
    bpp         : u8,
    fbtype      : u8,
    colors      : [u8, ..6],
}

#[allow(dead_code)]
pub enum FBType {
    Indexed = 0,
    RGB = 1,
    Text = 2,
}

#[allow(dead_code)]
pub struct FBPalette {
    addr        : u32,
    count       : u16,
}

#[allow(dead_code)]
pub struct FBPixelFormat {
    red_shift   : u8,
    red_mask    : u8,
    green_shift : u8,
    green_mask  : u8,
    blue_shift  : u8,
    blue_mask   : u8,
}

pub enum InfoFlags {
    MemorySize = 1,
    BootDevice = 2,
    CommandLine = 4,
    Modules = 8,
    // There are two formats for the symbol bit of the info struct, not sure
    // what they are though.
    Symbols = 16 | 32,
    Symbols1 = 16,
    Symbols2 = 32,
    MemoryMap = 64,
    Drives = 128,
    ConfigTable = 256,
    LoaderName = 512,
    APMTable = 1024,
    VBEInfo = 2048
}

pub struct Info {
    pub flags   : u32,
// if has(MemorySize)
    pub mem_lower   : u32,
    pub mem_upper   : u32,
// if flags[1]:
    pub boot_devices: u32,

// if has(CommandLine)
    pub cmdline     : u32,

// if has(Modules)
    pub mods_count  : u32,
    pub mods_addr   : u32,

//
    syms        : [u32, ..4],

// if has(MMap)
    pub mmap_length : u32,
    pub mmap_addr   : u32,

    drives_length: u32,
    drives_addr : u32,

    config_table: u32,

    boot_loader : u32,
    apm_table   : u32,

    vbe         : VBE,
    fb          : FB,
}

impl Info {
    pub fn has(&self, flag : InfoFlags) -> bool {
        self.flags & (flag as u32) != 0
    }

    pub fn modules(&self, make_ptr : |uint| -> *const Module) -> &[Module] {
        unsafe { transmute(Slice {
            data : make_ptr(self.mods_addr as uint),
            len : self.mods_count as uint
        }) }
    }
}

#[allow(dead_code)] // Spurious dead-code warning?
pub struct Module {
    pub start   : u32,
    pub end     : u32,
    pub string  : u32,
    reserved    : u32,
}

#[repr(packed)]
pub struct MemoryMapItem {
    // Size of item (bytes), *not* including the item_size field
    pub item_size   : u32,
    pub start       : u64,
    pub length      : u64,
    // See values from MemoryTypes
    pub item_type   : u32,
}

pub enum MemoryTypes {
    MemoryTypeMemory = 1,
    MemoryTypeReserved = 2,
    MemoryTypeACPIRCL = 3,
    MemoryTypeACPISomething = 4,
}
<|endoftext|>


<commit_before><commit_msg>Add Vector Multiply Sum Saturated<commit_after><|endoftext|>