<commit_before>use std::cell::UnsafeCell;
use std::cmp;
use std::fmt;
use std::mem;
use std::mem::ManuallyDrop;
use std::sync::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT};

use errors::InvalidThreadAccess;

fn next_thread_id() -> usize {
    static mut COUNTER: AtomicUsize = ATOMIC_USIZE_INIT;
    unsafe { COUNTER.fetch_add(1, Ordering::SeqCst) }
}

pub(crate) fn get_thread_id() -> usize {
    thread_local!(static THREAD_ID: usize = next_thread_id());
    THREAD_ID.with(|&x| x)
}

/// A `Fragile<T>` wraps a non sendable `T` to be safely send to other threads.
///
/// Once the value has been wrapped it can be sent to other threads but access
/// to the value on those threads will fail.
///
/// If the value needs destruction and the fragile wrapper is on another thread
/// the destructor will panic.  Alternatively you can use `Sticky<T>` which is
/// not going to panic but might temporarily leak the value.
pub struct Fragile<T> {
    value: ManuallyDrop<UnsafeCell<T>>,
    thread_id: usize,
}

impl<T> Fragile<T> {
    /// Creates a new `Fragile` wrapping a `value`.
    ///
    /// The value that is moved into the `Fragile` can be non `Send` and
    /// will be anchored to the thread that created the object.  If the
    /// fragile wrapper type ends up being send from thread to thread
    /// only the original thread can interact with the value.
    pub fn new(value: T) -> Self {
        Fragile {
            value: ManuallyDrop::new(UnsafeCell::new(value)),
            thread_id: get_thread_id(),
        }
    }

    #[inline(always)]
    fn assert_thread(&self) {
        if get_thread_id() != self.thread_id {
            panic!("trying to access wrapped value in fragile container from incorrect thread.");
        }
    }

    /// Consumes the `Fragile`, returning the wrapped value.
    ///
    /// # Panics
    ///
    /// Panics if called from a different thread than the one where the
    /// original value was created.
    pub fn into_inner(mut self) -> T {
        self.assert_thread();
        unsafe {
            let value = mem::replace(&mut self.value, mem::uninitialized());
            mem::forget(self);
            ManuallyDrop::into_inner(value).into_inner()
        }
    }

    /// Consumes the `Fragile`, returning the wrapped value if successful.
    ///
    /// The wrapped value is returned if this is called from the same thread
    /// as the one where the original value was created, otherwise the
    /// `Fragile` is returned as `Err(self)`.
    pub fn try_into_inner(self) -> Result<T, Self> {
        if get_thread_id() == self.thread_id {
            Ok(self.into_inner())
        } else {
            Err(self)
        }
    }

    /// Immutably borrows the wrapped value.
    ///
    /// # Panics
    ///
    /// Panics if the calling thread is not the one that wrapped the value.
    /// For a non-panicking variant, use [`try_get`](#method.try_get`).
    pub fn get(&self) -> &T {
        self.assert_thread();
        unsafe { &*self.value.get() }
    }

    /// Mutably borrows the wrapped value.
    ///
    /// # Panics
    ///
    /// Panics if the calling thread is not the one that wrapped the value.
    /// For a non-panicking variant, use [`try_get_mut`](#method.try_get_mut`).
    pub fn get_mut(&mut self) -> &mut T {
        self.assert_thread();
        unsafe { &mut *self.value.get() }
    }

    /// Tries to immutably borrow the wrapped value.
    ///
    /// Returns `None` if the calling thread is not the one that wrapped the value.
    pub fn try_get(&self) -> Result<&T, InvalidThreadAccess> {
        if get_thread_id() == self.thread_id {
            unsafe { Ok(&*self.value.get()) }
        } else {
            Err(InvalidThreadAccess)
        }
    }

    /// Tries to mutably borrow the wrapped value.
    ///
    /// Returns `None` if the calling thread is not the one that wrapped the value.
    pub fn try_get_mut(&mut self) -> Result<&mut T, InvalidThreadAccess> {
        if get_thread_id() == self.thread_id {
            unsafe { Ok(&mut *self.value.get()) }
        } else {
            Err(InvalidThreadAccess)
        }
    }
}

impl<T> Drop for Fragile<T> {
    fn drop(&mut self) {
        if mem::needs_drop::<T>() {
            if get_thread_id() == self.thread_id {
                unsafe { ManuallyDrop::drop(&mut self.value) }
            } else {
                panic!("destructor of fragile object ran on wrong thread");
            }
        }
    }
}

impl<T> From<T> for Fragile<T> {
    #[inline]
    fn from(t: T) -> Fragile<T> {
        Fragile::new(t)
    }
}

impl<T: Clone> Clone for Fragile<T> {
    #[inline]
    fn clone(&self) -> Fragile<T> {
        Fragile::new(self.get().clone())
    }
}

impl<T: Default> Default for Fragile<T> {
    #[inline]
    fn default() -> Fragile<T> {
        Fragile::new(T::default())
    }
}

impl<T: PartialEq> PartialEq for Fragile<T> {
    #[inline]
    fn eq(&self, other: &Fragile<T>) -> bool {
        *self.get() == *other.get()
    }
}

impl<T: Eq> Eq for Fragile<T> {}

impl<T: PartialOrd> PartialOrd for Fragile<T> {
    #[inline]
    fn partial_cmp(&self, other: &Fragile<T>) -> Option<cmp::Ordering> {
        self.get().partial_cmp(&*other.get())
    }

    #[inline]
    fn lt(&self, other: &Fragile<T>) -> bool {
        *self.get() < *other.get()
    }

    #[inline]
    fn le(&self, other: &Fragile<T>) -> bool {
        *self.get() <= *other.get()
    }

    #[inline]
    fn gt(&self, other: &Fragile<T>) -> bool {
        *self.get() > *other.get()
    }

    #[inline]
    fn ge(&self, other: &Fragile<T>) -> bool {
        *self.get() >= *other.get()
    }
}

impl<T: Ord> Ord for Fragile<T> {
    #[inline]
    fn cmp(&self, other: &Fragile<T>) -> cmp::Ordering {
        self.get().cmp(&*other.get())
    }
}

impl<T: fmt::Display> fmt::Display for Fragile<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        fmt::Display::fmt(self.get(), f)
    }
}

impl<T: fmt::Debug> fmt::Debug for Fragile<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        match self.try_get() {
            Ok(value) => f.debug_struct("Fragile").field("value", value).finish(),
            Err(..) => {
                struct InvalidPlaceholder;
                impl fmt::Debug for InvalidPlaceholder {
                    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
                        f.write_str("<invalid thread>")
                    }
                }

                f.debug_struct("Fragile")
                    .field("value", &InvalidPlaceholder)
                    .finish()
            }
        }
    }
}

unsafe impl<T> Send for Fragile<T> {}

#[test]
fn test_basic() {
    use std::thread;
    let val = Fragile::new(true);
    assert_eq!(val.to_string(), "true");
    assert_eq!(val.get(), &true);
    assert!(val.try_get().is_ok());
    thread::spawn(move || {
        assert!(val.try_get().is_err());
    }).join()
        .unwrap();
}

#[test]
fn test_mut() {
    let mut val = Fragile::new(true);
    *val.get_mut() = false;
    assert_eq!(val.to_string(), "false");
    assert_eq!(val.get(), &false);
}

#[test]
#[should_panic]
fn test_access_other_thread() {
    use std::thread;
    let val = Fragile::new(true);
    thread::spawn(move || {
        val.get();
    }).join()
        .unwrap();
}

#[test]
fn test_noop_drop_elsewhere() {
    use std::thread;
    let val = Fragile::new(true);
    thread::spawn(move || {
        // force the move
        val.try_get().ok();
    }).join()
        .unwrap();
}

#[test]
fn test_panic_on_drop_elsewhere() {
    use std::sync::atomic::{AtomicBool, Ordering};
    use std::sync::Arc;
    use std::thread;
    let was_called = Arc::new(AtomicBool::new(false));
    struct X(Arc<AtomicBool>);
    impl Drop for X {
        fn drop(&mut self) {
            self.0.store(true, Ordering::SeqCst);
        }
    }
    let val = Fragile::new(X(was_called.clone()));
    assert!(
        thread::spawn(move || {
            val.try_get().ok();
        }).join()
            .is_err()
    );
    assert_eq!(was_called.load(Ordering::SeqCst), false);
}
<commit_msg>Box always in Fragile<T><commit_after>use std::cell::UnsafeCell;
use std::cmp;
use std::fmt;
use std::mem;
use std::mem::ManuallyDrop;
use std::sync::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT};

use errors::InvalidThreadAccess;

fn next_thread_id() -> usize {
    static mut COUNTER: AtomicUsize = ATOMIC_USIZE_INIT;
    unsafe { COUNTER.fetch_add(1, Ordering::SeqCst) }
}

pub(crate) fn get_thread_id() -> usize {
    thread_local!(static THREAD_ID: usize = next_thread_id());
    THREAD_ID.with(|&x| x)
}

/// A `Fragile<T>` wraps a non sendable `T` to be safely send to other threads.
///
/// Once the value has been wrapped it can be sent to other threads but access
/// to the value on those threads will fail.
///
/// If the value needs destruction and the fragile wrapper is on another thread
/// the destructor will panic.  Alternatively you can use `Sticky<T>` which is
/// not going to panic but might temporarily leak the value.
pub struct Fragile<T> {
    value: ManuallyDrop<UnsafeCell<Box<T>>>,
    thread_id: usize,
}

unsafe impl<T> Sync for Fragile<T> {}

impl<T> Fragile<T> {
    /// Creates a new `Fragile` wrapping a `value`.
    ///
    /// The value that is moved into the `Fragile` can be non `Send` and
    /// will be anchored to the thread that created the object.  If the
    /// fragile wrapper type ends up being send from thread to thread
    /// only the original thread can interact with the value.
    pub fn new(value: T) -> Self {
        Fragile {
            value: ManuallyDrop::new(UnsafeCell::new(Box::new(value))),
            thread_id: get_thread_id(),
        }
    }

    pub(crate) fn is_same_thread(&self) -> bool {
        get_thread_id() == self.thread_id
    }

    #[inline(always)]
    fn assert_thread(&self) {
        if !self.is_same_thread() {
            panic!("trying to access wrapped value in fragile container from incorrect thread.");
        }
    }

    /// Consumes the `Fragile`, returning the wrapped value.
    ///
    /// # Panics
    ///
    /// Panics if called from a different thread than the one where the
    /// original value was created.
    pub fn into_inner(mut self) -> T {
        self.assert_thread();
        unsafe {
            let value = mem::replace(&mut self.value, mem::uninitialized());
            mem::forget(self);
            *ManuallyDrop::into_inner(value).into_inner()
        }
    }

    /// Consumes the `Fragile`, returning the wrapped value if successful.
    ///
    /// The wrapped value is returned if this is called from the same thread
    /// as the one where the original value was created, otherwise the
    /// `Fragile` is returned as `Err(self)`.
    pub fn try_into_inner(self) -> Result<T, Self> {
        if get_thread_id() == self.thread_id {
            Ok(self.into_inner())
        } else {
            Err(self)
        }
    }

    /// Immutably borrows the wrapped value.
    ///
    /// # Panics
    ///
    /// Panics if the calling thread is not the one that wrapped the value.
    /// For a non-panicking variant, use [`try_get`](#method.try_get`).
    pub fn get(&self) -> &T {
        self.assert_thread();
        unsafe { &*self.value.get() }
    }

    /// Mutably borrows the wrapped value.
    ///
    /// # Panics
    ///
    /// Panics if the calling thread is not the one that wrapped the value.
    /// For a non-panicking variant, use [`try_get_mut`](#method.try_get_mut`).
    pub fn get_mut(&mut self) -> &mut T {
        self.assert_thread();
        unsafe { &mut *self.value.get() }
    }

    /// Tries to immutably borrow the wrapped value.
    ///
    /// Returns `None` if the calling thread is not the one that wrapped the value.
    pub fn try_get(&self) -> Result<&T, InvalidThreadAccess> {
        if get_thread_id() == self.thread_id {
            unsafe { Ok(&*self.value.get()) }
        } else {
            Err(InvalidThreadAccess)
        }
    }

    /// Tries to mutably borrow the wrapped value.
    ///
    /// Returns `None` if the calling thread is not the one that wrapped the value.
    pub fn try_get_mut(&mut self) -> Result<&mut T, InvalidThreadAccess> {
        if get_thread_id() == self.thread_id {
            unsafe { Ok(&mut *self.value.get()) }
        } else {
            Err(InvalidThreadAccess)
        }
    }
}

impl<T> Drop for Fragile<T> {
    fn drop(&mut self) {
        if mem::needs_drop::<T>() {
            if get_thread_id() == self.thread_id {
                unsafe { ManuallyDrop::drop(&mut self.value) }
            } else {
                panic!("destructor of fragile object ran on wrong thread");
            }
        }
    }
}

impl<T> From<T> for Fragile<T> {
    #[inline]
    fn from(t: T) -> Fragile<T> {
        Fragile::new(t)
    }
}

impl<T: Clone> Clone for Fragile<T> {
    #[inline]
    fn clone(&self) -> Fragile<T> {
        Fragile::new(self.get().clone())
    }
}

impl<T: Default> Default for Fragile<T> {
    #[inline]
    fn default() -> Fragile<T> {
        Fragile::new(T::default())
    }
}

impl<T: PartialEq> PartialEq for Fragile<T> {
    #[inline]
    fn eq(&self, other: &Fragile<T>) -> bool {
        *self.get() == *other.get()
    }
}

impl<T: Eq> Eq for Fragile<T> {}

impl<T: PartialOrd> PartialOrd for Fragile<T> {
    #[inline]
    fn partial_cmp(&self, other: &Fragile<T>) -> Option<cmp::Ordering> {
        self.get().partial_cmp(&*other.get())
    }

    #[inline]
    fn lt(&self, other: &Fragile<T>) -> bool {
        *self.get() < *other.get()
    }

    #[inline]
    fn le(&self, other: &Fragile<T>) -> bool {
        *self.get() <= *other.get()
    }

    #[inline]
    fn gt(&self, other: &Fragile<T>) -> bool {
        *self.get() > *other.get()
    }

    #[inline]
    fn ge(&self, other: &Fragile<T>) -> bool {
        *self.get() >= *other.get()
    }
}

impl<T: Ord> Ord for Fragile<T> {
    #[inline]
    fn cmp(&self, other: &Fragile<T>) -> cmp::Ordering {
        self.get().cmp(&*other.get())
    }
}

impl<T: fmt::Display> fmt::Display for Fragile<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        fmt::Display::fmt(self.get(), f)
    }
}

impl<T: fmt::Debug> fmt::Debug for Fragile<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        match self.try_get() {
            Ok(value) => f.debug_struct("Fragile").field("value", value).finish(),
            Err(..) => {
                struct InvalidPlaceholder;
                impl fmt::Debug for InvalidPlaceholder {
                    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
                        f.write_str("<invalid thread>")
                    }
                }

                f.debug_struct("Fragile")
                    .field("value", &InvalidPlaceholder)
                    .finish()
            }
        }
    }
}

unsafe impl<T> Send for Fragile<T> {}

#[test]
fn test_basic() {
    use std::thread;
    let val = Fragile::new(true);
    assert_eq!(val.to_string(), "true");
    assert_eq!(val.get(), &true);
    assert!(val.try_get().is_ok());
    thread::spawn(move || {
        assert!(val.try_get().is_err());
    }).join()
        .unwrap();
}

#[test]
fn test_mut() {
    let mut val = Fragile::new(true);
    *val.get_mut() = false;
    assert_eq!(val.to_string(), "false");
    assert_eq!(val.get(), &false);
}

#[test]
#[should_panic]
fn test_access_other_thread() {
    use std::thread;
    let val = Fragile::new(true);
    thread::spawn(move || {
        val.get();
    }).join()
        .unwrap();
}

#[test]
fn test_noop_drop_elsewhere() {
    use std::thread;
    let val = Fragile::new(true);
    thread::spawn(move || {
        // force the move
        val.try_get().ok();
    }).join()
        .unwrap();
}

#[test]
fn test_panic_on_drop_elsewhere() {
    use std::sync::atomic::{AtomicBool, Ordering};
    use std::sync::Arc;
    use std::thread;
    let was_called = Arc::new(AtomicBool::new(false));
    struct X(Arc<AtomicBool>);
    impl Drop for X {
        fn drop(&mut self) {
            self.0.store(true, Ordering::SeqCst);
        }
    }
    let val = Fragile::new(X(was_called.clone()));
    assert!(
        thread::spawn(move || {
            val.try_get().ok();
        }).join()
            .is_err()
    );
    assert_eq!(was_called.load(Ordering::SeqCst), false);
}
<|endoftext|>


<commit_before>use {Field, Borrowed, Value, Matrix2d, RoundRectangle};
use vecmath::{margin_round_rectangle,
relative_round_rectangle, translate,
rotate_radians, scale, shear, multiply,
identity};
use {Transform2d};
use {RelativeRectangle};
use {RoundRectangleColorContext};
use {AddColor, View};

/// A round rectangle context.
pub struct RoundRectangleContext<'a> {
    /// Base/origin transform.
    pub base: Field<'a, Matrix2d>,
    /// Current transform.
    pub transform: Field<'a, Matrix2d>,
    /// Current round rectangle.
    pub round_rect: Field<'a, RoundRectangle>,
}

impl<'a> Transform2d<'a> for RoundRectangleContext<'a> {
    #[inline(always)]
    fn trans(&'a self, x: f64, y: f64) -> RoundRectangleContext<'a> {
        RoundRectangleContext {
            base: Borrowed(self.base.get()),
            transform: {
                let trans = translate(x, y);
                Value(multiply(&trans, self.transform.get()))
            },
            round_rect: Borrowed(self.round_rect.get()),
        }
    }

    #[inline(always)]
    fn trans_local(&'a self, x: f64, y: f64) -> RoundRectangleContext<'a> {
        RoundRectangleContext {
            base: Borrowed(self.base.get()),
            transform: {
                let trans = translate(x, y);
                Value(multiply(self.transform.get(), &trans))
            },
            round_rect: Borrowed(self.round_rect.get()),
        }
    }

    #[inline(always)]
    fn rot_rad(&'a self, angle: f64) -> RoundRectangleContext<'a> {
        RoundRectangleContext {
            base: Borrowed(self.base.get()),
            transform: {
                let rot = rotate_radians(angle);
                Value(multiply(&rot, self.transform.get()))
            },
            round_rect: Borrowed(self.round_rect.get()),
        }
    }

    #[inline(always)]
    fn rot_rad_local(&'a self, angle: f64) -> RoundRectangleContext<'a> {
        RoundRectangleContext {
            base: Borrowed(self.base.get()),
            transform: {
                let rot = rotate_radians(angle);
                Value(multiply(self.transform.get(), &rot))
            },
            round_rect: Borrowed(self.round_rect.get()),
        }
    }

    #[inline(always)]
    fn scale(&'a self, sx: f64, sy: f64) -> RoundRectangleContext<'a> {
        RoundRectangleContext {
            base: Borrowed(self.base.get()),
            transform: {
                let scale = scale(sx, sy);
                Value(multiply(&scale, self.transform.get()))
            },
            round_rect: Borrowed(self.round_rect.get()),
        }
    }

    #[inline(always)]
    fn scale_local(&'a self, sx: f64, sy: f64) -> RoundRectangleContext<'a> {
        RoundRectangleContext {
            base: Borrowed(self.base.get()),
            transform: {
                let scale = scale(sx, sy);
                Value(multiply(self.transform.get(), &scale))
            },
            round_rect: Borrowed(self.round_rect.get()),
        }
    }

    #[inline(always)]
    fn shear(&'a self, sx: f64, sy: f64) -> RoundRectangleContext<'a> {
        RoundRectangleContext {
            base: Borrowed(self.base.get()),
            transform: {
                let shear = shear(sx, sy);
                Value(multiply(&shear, self.transform.get()))
            },
            round_rect: Borrowed(self.round_rect.get()),
        }
    }

    #[inline(always)]
    fn shear_local(&'a self, sx: f64, sy: f64) -> RoundRectangleContext<'a> {
        RoundRectangleContext {
            base: Borrowed(self.base.get()),
            transform: {
                let shear = shear(sx, sy);
                Value(multiply(self.transform.get(), &shear))
            },
            round_rect: Borrowed(self.round_rect.get()),
        }
    }
}

impl<'a> RelativeRectangle<'a> for RoundRectangleContext<'a> {
    #[inline(always)]
    fn margin(&'a self, m: f64) -> RoundRectangleContext<'a> {
        RoundRectangleContext {
            base: Borrowed(self.base.get()),
            transform: Borrowed(self.transform.get()),
            round_rect: Value(margin_round_rectangle(self.round_rect.get(), m)),
        }
    }

    #[inline(always)]
    fn rel(&'a self, x: f64, y: f64) -> RoundRectangleContext<'a> {
        RoundRectangleContext {
            base: Borrowed(self.base.get()),
            transform: Borrowed(self.transform.get()),
            round_rect: Value(relative_round_rectangle(self.round_rect.get(), x, y)),
        }
    }
}

impl<'a> AddColor<'a, RoundRectangleColorContext<'a>> for RoundRectangleContext<'a> {
    /// Creates a RectangleColorContext.
    #[inline(always)]
    fn rgba(&'a self, r: f32, g: f32, b: f32, a: f32) -> RoundRectangleColorContext<'a> {
        RoundRectangleColorContext {
            base: Borrowed(self.base.get()),
            transform: Borrowed(self.transform.get()),
            color: Value([r, g, b, a]),
            round_rect: Borrowed(self.round_rect.get()),
        }
    }
}

impl<'a> View<'a> for RoundRectangleContext<'a> {
    #[inline(always)]
    fn view(&'a self) -> RoundRectangleContext<'a> {
        RoundRectangleContext {
            base: Borrowed(self.base.get()),
            transform: Borrowed(self.base.get()),
            round_rect: Borrowed(self.round_rect.get()),
        }
    }

    #[inline(always)]
    fn reset(&'a self) -> RoundRectangleContext<'a> {
        RoundRectangleContext {
            base: Borrowed(self.base.get()),
            transform: Value(identity()),
            round_rect: Borrowed(self.round_rect.get()),
        }
    }

    #[inline(always)]
    fn store_view(&'a self) -> RoundRectangleContext<'a> {
        RoundRectangleContext {
            base: Borrowed(self.transform.get()),
            transform: Borrowed(self.transform.get()),
            round_rect: Borrowed(self.round_rect.get()),
        }
    }
}

<commit_msg>Sorted use alphabetically<commit_after>use {
    AddColor,
    Borrowed,
    Field,
    Matrix2d,
    RelativeRectangle,
    RoundRectangle,
    RoundRectangleColorContext,
    Transform2d,
    Value,
    View,
};
use vecmath::{
    identity,
    margin_round_rectangle,
    multiply,
    relative_round_rectangle,
    rotate_radians,
    scale,
    shear,
    translate,
};

/// A round rectangle context.
pub struct RoundRectangleContext<'a> {
    /// Base/origin transform.
    pub base: Field<'a, Matrix2d>,
    /// Current transform.
    pub transform: Field<'a, Matrix2d>,
    /// Current round rectangle.
    pub round_rect: Field<'a, RoundRectangle>,
}

impl<'a> Transform2d<'a> for RoundRectangleContext<'a> {
    #[inline(always)]
    fn trans(&'a self, x: f64, y: f64) -> RoundRectangleContext<'a> {
        RoundRectangleContext {
            base: Borrowed(self.base.get()),
            transform: {
                let trans = translate(x, y);
                Value(multiply(&trans, self.transform.get()))
            },
            round_rect: Borrowed(self.round_rect.get()),
        }
    }

    #[inline(always)]
    fn trans_local(&'a self, x: f64, y: f64) -> RoundRectangleContext<'a> {
        RoundRectangleContext {
            base: Borrowed(self.base.get()),
            transform: {
                let trans = translate(x, y);
                Value(multiply(self.transform.get(), &trans))
            },
            round_rect: Borrowed(self.round_rect.get()),
        }
    }

    #[inline(always)]
    fn rot_rad(&'a self, angle: f64) -> RoundRectangleContext<'a> {
        RoundRectangleContext {
            base: Borrowed(self.base.get()),
            transform: {
                let rot = rotate_radians(angle);
                Value(multiply(&rot, self.transform.get()))
            },
            round_rect: Borrowed(self.round_rect.get()),
        }
    }

    #[inline(always)]
    fn rot_rad_local(&'a self, angle: f64) -> RoundRectangleContext<'a> {
        RoundRectangleContext {
            base: Borrowed(self.base.get()),
            transform: {
                let rot = rotate_radians(angle);
                Value(multiply(self.transform.get(), &rot))
            },
            round_rect: Borrowed(self.round_rect.get()),
        }
    }

    #[inline(always)]
    fn scale(&'a self, sx: f64, sy: f64) -> RoundRectangleContext<'a> {
        RoundRectangleContext {
            base: Borrowed(self.base.get()),
            transform: {
                let scale = scale(sx, sy);
                Value(multiply(&scale, self.transform.get()))
            },
            round_rect: Borrowed(self.round_rect.get()),
        }
    }

    #[inline(always)]
    fn scale_local(&'a self, sx: f64, sy: f64) -> RoundRectangleContext<'a> {
        RoundRectangleContext {
            base: Borrowed(self.base.get()),
            transform: {
                let scale = scale(sx, sy);
                Value(multiply(self.transform.get(), &scale))
            },
            round_rect: Borrowed(self.round_rect.get()),
        }
    }

    #[inline(always)]
    fn shear(&'a self, sx: f64, sy: f64) -> RoundRectangleContext<'a> {
        RoundRectangleContext {
            base: Borrowed(self.base.get()),
            transform: {
                let shear = shear(sx, sy);
                Value(multiply(&shear, self.transform.get()))
            },
            round_rect: Borrowed(self.round_rect.get()),
        }
    }

    #[inline(always)]
    fn shear_local(&'a self, sx: f64, sy: f64) -> RoundRectangleContext<'a> {
        RoundRectangleContext {
            base: Borrowed(self.base.get()),
            transform: {
                let shear = shear(sx, sy);
                Value(multiply(self.transform.get(), &shear))
            },
            round_rect: Borrowed(self.round_rect.get()),
        }
    }
}

impl<'a> RelativeRectangle<'a> for RoundRectangleContext<'a> {
    #[inline(always)]
    fn margin(&'a self, m: f64) -> RoundRectangleContext<'a> {
        RoundRectangleContext {
            base: Borrowed(self.base.get()),
            transform: Borrowed(self.transform.get()),
            round_rect: Value(margin_round_rectangle(self.round_rect.get(), m)),
        }
    }

    #[inline(always)]
    fn rel(&'a self, x: f64, y: f64) -> RoundRectangleContext<'a> {
        RoundRectangleContext {
            base: Borrowed(self.base.get()),
            transform: Borrowed(self.transform.get()),
            round_rect: Value(relative_round_rectangle(self.round_rect.get(), x, y)),
        }
    }
}

impl<'a> AddColor<'a, RoundRectangleColorContext<'a>> for RoundRectangleContext<'a> {
    /// Creates a RectangleColorContext.
    #[inline(always)]
    fn rgba(&'a self, r: f32, g: f32, b: f32, a: f32) -> RoundRectangleColorContext<'a> {
        RoundRectangleColorContext {
            base: Borrowed(self.base.get()),
            transform: Borrowed(self.transform.get()),
            color: Value([r, g, b, a]),
            round_rect: Borrowed(self.round_rect.get()),
        }
    }
}

impl<'a> View<'a> for RoundRectangleContext<'a> {
    #[inline(always)]
    fn view(&'a self) -> RoundRectangleContext<'a> {
        RoundRectangleContext {
            base: Borrowed(self.base.get()),
            transform: Borrowed(self.base.get()),
            round_rect: Borrowed(self.round_rect.get()),
        }
    }

    #[inline(always)]
    fn reset(&'a self) -> RoundRectangleContext<'a> {
        RoundRectangleContext {
            base: Borrowed(self.base.get()),
            transform: Value(identity()),
            round_rect: Borrowed(self.round_rect.get()),
        }
    }

    #[inline(always)]
    fn store_view(&'a self) -> RoundRectangleContext<'a> {
        RoundRectangleContext {
            base: Borrowed(self.transform.get()),
            transform: Borrowed(self.transform.get()),
            round_rect: Borrowed(self.round_rect.get()),
        }
    }
}

<|endoftext|>


<commit_before>// compile-flags: -C no-prepopulate-passes

#![crate_type = "lib"]
#![feature(c_variadic)]
#![no_std]
use core::ffi::VaList;

extern "C" {
    fn foreign_c_variadic_0(_: i32, ...);
    fn foreign_c_variadic_1(_: VaList, ...);
}

pub unsafe extern "C" fn use_foreign_c_variadic_0() {
    // Ensure that we correctly call foreign C-variadic functions.
    // CHECK: invoke void (i32, ...) @foreign_c_variadic_0(i32 0)
    foreign_c_variadic_0(0);
    // CHECK: invoke void (i32, ...) @foreign_c_variadic_0(i32 0, i32 42)
    foreign_c_variadic_0(0, 42i32);
    // CHECK: invoke void (i32, ...) @foreign_c_variadic_0(i32 0, i32 42, i32 1024)
    foreign_c_variadic_0(0, 42i32, 1024i32);
    // CHECK: invoke void (i32, ...) @foreign_c_variadic_0(i32 0, i32 42, i32 1024, i32 0)
    foreign_c_variadic_0(0, 42i32, 1024i32, 0i32);
}

// Ensure that we do not remove the `va_list` passed to the foreign function when
// removing the "spoofed" `VaListImpl` that is used by Rust defined C-variadics.
pub unsafe extern "C" fn use_foreign_c_variadic_1_0(ap: VaList) {
    // CHECK: invoke void ({{.*}}*, ...) @foreign_c_variadic_1({{.*}} %ap)
    foreign_c_variadic_1(ap);
}

pub unsafe extern "C" fn use_foreign_c_variadic_1_1(ap: VaList) {
    // CHECK: invoke void ({{.*}}*, ...) @foreign_c_variadic_1({{.*}} %ap, i32 42)
    foreign_c_variadic_1(ap, 42i32);
}
pub unsafe extern "C" fn use_foreign_c_variadic_1_2(ap: VaList) {
    // CHECK: invoke void ({{.*}}*, ...) @foreign_c_variadic_1({{.*}} %ap, i32 2, i32 42)
    foreign_c_variadic_1(ap, 2i32, 42i32);
}

pub unsafe extern "C" fn use_foreign_c_variadic_1_3(ap: VaList) {
    // CHECK: invoke void ({{.*}}*, ...) @foreign_c_variadic_1({{.*}} %ap, i32 2, i32 42, i32 0)
    foreign_c_variadic_1(ap, 2i32, 42i32, 0i32);
}

// Ensure that `va_start` and `va_end` are properly injected.
#[no_mangle]
pub unsafe extern "C" fn c_variadic(n: i32, mut ap: ...) -> i32 {
    // CHECK: call void @llvm.va_start
    let mut sum = 0;
    for _ in 0..n {
        sum += ap.arg::<i32>();
    }
    sum
    // CHECK: call void @llvm.va_end
}

// Ensure that we generate the correct `call` signature when calling a Rust
// defined C-variadic.
pub unsafe fn test_c_variadic_call() {
    // CHECK: call i32 (i32, ...) @c_variadic(i32 0)
    c_variadic(0);
    // CHECK: call i32 (i32, ...) @c_variadic(i32 0, i32 42)
    c_variadic(0, 42i32);
    // CHECK: call i32 (i32, ...) @c_variadic(i32 0, i32 42, i32 1024)
    c_variadic(0, 42i32, 1024i32);
    // CHECK: call i32 (i32, ...) @c_variadic(i32 0, i32 42, i32 1024, i32 0)
    c_variadic(0, 42i32, 1024i32, 0i32);
}
<commit_msg>Force #[unwind(aborts)] in test/codegen/c-variadic.rs<commit_after>// compile-flags: -C no-prepopulate-passes

#![crate_type = "lib"]
#![feature(c_variadic)]
#![feature(unwind_attributes)]
#![no_std]
use core::ffi::VaList;

extern "C" {
    fn foreign_c_variadic_0(_: i32, ...);
    fn foreign_c_variadic_1(_: VaList, ...);
}

#[unwind(aborts)] // FIXME(#58794)
pub unsafe extern "C" fn use_foreign_c_variadic_0() {
    // Ensure that we correctly call foreign C-variadic functions.
    // CHECK: invoke void (i32, ...) @foreign_c_variadic_0(i32 0)
    foreign_c_variadic_0(0);
    // CHECK: invoke void (i32, ...) @foreign_c_variadic_0(i32 0, i32 42)
    foreign_c_variadic_0(0, 42i32);
    // CHECK: invoke void (i32, ...) @foreign_c_variadic_0(i32 0, i32 42, i32 1024)
    foreign_c_variadic_0(0, 42i32, 1024i32);
    // CHECK: invoke void (i32, ...) @foreign_c_variadic_0(i32 0, i32 42, i32 1024, i32 0)
    foreign_c_variadic_0(0, 42i32, 1024i32, 0i32);
}

// Ensure that we do not remove the `va_list` passed to the foreign function when
// removing the "spoofed" `VaListImpl` that is used by Rust defined C-variadics.
#[unwind(aborts)] // FIXME(#58794)
pub unsafe extern "C" fn use_foreign_c_variadic_1_0(ap: VaList) {
    // CHECK: invoke void ({{.*}}*, ...) @foreign_c_variadic_1({{.*}} %ap)
    foreign_c_variadic_1(ap);
}

#[unwind(aborts)] // FIXME(#58794)
pub unsafe extern "C" fn use_foreign_c_variadic_1_1(ap: VaList) {
    // CHECK: invoke void ({{.*}}*, ...) @foreign_c_variadic_1({{.*}} %ap, i32 42)
    foreign_c_variadic_1(ap, 42i32);
}
#[unwind(aborts)] // FIXME(#58794)
pub unsafe extern "C" fn use_foreign_c_variadic_1_2(ap: VaList) {
    // CHECK: invoke void ({{.*}}*, ...) @foreign_c_variadic_1({{.*}} %ap, i32 2, i32 42)
    foreign_c_variadic_1(ap, 2i32, 42i32);
}

#[unwind(aborts)] // FIXME(#58794)
pub unsafe extern "C" fn use_foreign_c_variadic_1_3(ap: VaList) {
    // CHECK: invoke void ({{.*}}*, ...) @foreign_c_variadic_1({{.*}} %ap, i32 2, i32 42, i32 0)
    foreign_c_variadic_1(ap, 2i32, 42i32, 0i32);
}

// Ensure that `va_start` and `va_end` are properly injected.
#[no_mangle]
pub unsafe extern "C" fn c_variadic(n: i32, mut ap: ...) -> i32 {
    // CHECK: call void @llvm.va_start
    let mut sum = 0;
    for _ in 0..n {
        sum += ap.arg::<i32>();
    }
    sum
    // CHECK: call void @llvm.va_end
}

// Ensure that we generate the correct `call` signature when calling a Rust
// defined C-variadic.
pub unsafe fn test_c_variadic_call() {
    // CHECK: call i32 (i32, ...) @c_variadic(i32 0)
    c_variadic(0);
    // CHECK: call i32 (i32, ...) @c_variadic(i32 0, i32 42)
    c_variadic(0, 42i32);
    // CHECK: call i32 (i32, ...) @c_variadic(i32 0, i32 42, i32 1024)
    c_variadic(0, 42i32, 1024i32);
    // CHECK: call i32 (i32, ...) @c_variadic(i32 0, i32 42, i32 1024, i32 0)
    c_variadic(0, 42i32, 1024i32, 0i32);
}
<|endoftext|>


<commit_before>#![crate_name = "foo"]

// @has foo/fn.f.html '//*[@class="docblock attributes"]' '#[no_mangle]'
#[no_mangle]
pub extern "C" fn f() {}

// @has foo/fn.g.html '//*[@class="docblock attributes"]' '#[export_name = "bar"]'
#[export_name = "bar"]
pub extern "C" fn g() {}

// @has foo/enum.Foo.html '//*[@class="docblock attributes"]' '#[repr(i64)]'
// @has foo/enum.Foo.html '//*[@class="docblock attributes"]' '#[must_use]'
#[repr(i64)]
#[must_use]
pub enum Foo {
    Bar,
}
<commit_msg>Update test<commit_after>#![crate_name = "foo"]

// @has foo/fn.f.html '//*[@class="docblock attributes"]' '#[no_mangle]'
#[no_mangle]
pub extern "C" fn f() {}

// @has foo/fn.g.html '//*[@class="docblock attributes"]' '#[export_name = "bar"]'
#[export_name = "bar"]
pub extern "C" fn g() {}

// @has foo/enum.Foo.html '//*[@class="docblock attributes top-attr"]' '#[repr(i64)]'
// @has foo/enum.Foo.html '//*[@class="docblock attributes top-attr"]' '#[must_use]'
#[repr(i64)]
#[must_use]
pub enum Foo {
    Bar,
}
<|endoftext|>


<commit_before>use super::LinesIterator;
use crate::theme::{Effect, Style};
use crate::utils::markup::StyledString;
use crate::utils::span::Span;

fn input() -> StyledString {
    let mut text = StyledString::plain("I ");
    text.append(StyledString::styled("didn't", Effect::Bold));
    text.append(StyledString::plain(" say "));
    text.append(StyledString::styled("half", Effect::Italic));
    text.append(StyledString::plain(" the things people say I did."));
    text.append(StyledString::plain("\n"));
    text.append(StyledString::plain("\n"));
    text.append(StyledString::plain("    - A. Einstein"));

    text
}

#[test]
fn test_line_breaks() {
    let input = input();

    let iter = LinesIterator::new(&input, 17);

    let rows: Vec<_> = iter.map(|row| row.resolve(&input)).collect();

    // println!("{:?}", rows);

    assert_eq!(
        &rows[..],
        &[
            vec![
                Span {
                    content: "I ",
                    attr: &Style::none(),
                },
                Span {
                    content: "didn't",
                    attr: &Style::from(Effect::Bold),
                },
                Span {
                    content: " say ",
                    attr: &Style::none(),
                },
                Span {
                    content: "half",
                    attr: &Style::from(Effect::Italic),
                },
            ],
            vec![Span {
                content: "the things people",
                attr: &Style::none(),
            }],
            vec![Span {
                content: "say I did.",
                attr: &Style::none(),
            }],
            vec![],
            vec![Span {
                content: "    - A. Einstein",
                attr: &Style::none(),
            }],
        ]
    );
}
<commit_msg>Fix spans tests<commit_after>use super::LinesIterator;
use crate::theme::{Effect, Style};
use crate::utils::markup::StyledString;
use crate::utils::span::Span;

fn input() -> StyledString {
    let mut text = StyledString::plain("I ");
    text.append(StyledString::styled("didn't", Effect::Bold));
    text.append(StyledString::plain(" say "));
    text.append(StyledString::styled("half", Effect::Italic));
    text.append(StyledString::plain(" the things people say I did."));
    text.append(StyledString::plain("\n"));
    text.append(StyledString::plain("\n"));
    text.append(StyledString::plain("    - A. Einstein"));

    text
}

#[test]
fn test_line_breaks() {
    let input = input();

    let iter = LinesIterator::new(&input, 17);

    let rows: Vec<_> = iter.map(|row| row.resolve(&input)).collect();

    // println!("{:?}", rows);

    assert_eq!(
        &rows[..],
        &[
            vec![
                Span {
                    content: "I ",
                    attr: &Style::none(),
                    width: 2,
                },
                Span {
                    content: "didn't",
                    attr: &Style::from(Effect::Bold),
                    width: 6,
                },
                Span {
                    content: " say ",
                    attr: &Style::none(),
                    width: 5,
                },
                Span {
                    content: "half",
                    attr: &Style::from(Effect::Italic),
                    width: 4,
                },
            ],
            vec![Span {
                content: "the things people",
                attr: &Style::none(),
                width: 17,
            }],
            vec![Span {
                content: "say I did.",
                attr: &Style::none(),
                width: 10,
            }],
            vec![],
            vec![Span {
                content: "    - A. Einstein",
                attr: &Style::none(),
                width: 17
            }],
        ]
    );
}
<|endoftext|>


<commit_before>use futures::{Async, BoxFuture};
use tokio_service::Service;
use tokio_core::reactor::Handle;
use minihttp::request::Request;
use minihttp::Error;
use tokio_curl::Session;

use config::ConfigCell;
use response::DebugInfo;
use routing::{parse_host, route};
use serializer::{Response, Serializer};
use config::Handler;
use handlers::{files, proxy};
use websocket;

#[derive(Clone)]
pub struct Main {
    pub config: ConfigCell,
    pub handle: Handle,
    pub curl_session: Session,
}

impl Service for Main {
    type Request = Request;
    type Response = Serializer;
    type Error = Error;
    type Future = BoxFuture<Self::Response, Error>;

    fn call(&self, req: Request) -> Self::Future {
        // We must store configuration for specific request for the case
        // it changes in runtime. Config changes in the middle of request
        // can create undesirable effects
        let cfg = self.config.get();
        let mut debug = DebugInfo::new(&req);

        let matched_route = req.host().map(parse_host)
            .and_then(|host| route(host, &req.path, &cfg.routing));
        let response = if let Some((route, suffix)) = matched_route {
            debug.set_route(route);
            match cfg.handlers.get(route) {
                Some(&Handler::EmptyGif) => {
                    Response::EmptyGif
                }
                Some(&Handler::Static(ref settings)) => {
                    if let Ok(path) = files::path(settings, suffix, &req) {
                        Response::Static {
                            path: path,
                            settings: settings.clone(),
                        }
                    } else {
                        Response::ErrorPage(403)
                    }
                }
                Some(&Handler::WebsocketEcho) => {
                    match websocket::prepare(&req) {
                        Ok(init) => {
                            Response::WebsocketEcho(init)
                        }
                        Err(status) => {
                            // TODO(tailhook) use real status
                            Response::ErrorPage(
                                ::minihttp::enums::HttpStatus::code(&status)
                            )
                        }
                    }
                }
                Some(&Handler::Proxy(ref settings)) => {
                    if let Some(dest) = cfg.http_destinations.get(&settings.destination.upstream) {
                        match proxy::prepare(&req, &dest, settings.clone()) {
                            Ok(call) => {
                                Response::Proxy {
                                    session: self.curl_session.clone(),
                                    call: call,
                                }.serve(cfg2, debug)
                            }
                            Err(_) => {
                                Response::ErrorPage(500).serve(cfg2, debug)
                            }
                        }
                    } else {
                        Response::ErrorPage(404).serve(cfg2, debug)
                    }
                }
                // TODO(tailhook) make better error code for None
                _ => {
                    // Not implemented
                    Response::ErrorPage(501)
                }
            }
        } else {
            Response::ErrorPage(404)
        };
        response.serve(cfg.clone(), debug, &self.handle)
    }

    fn poll_ready(&self) -> Async<()> {
        Async::Ready(())
    }
}
<commit_msg>Fix merge errors<commit_after>use futures::{Async, BoxFuture};
use tokio_service::Service;
use tokio_core::reactor::Handle;
use minihttp::request::Request;
use minihttp::Error;
use tokio_curl::Session;

use config::ConfigCell;
use response::DebugInfo;
use routing::{parse_host, route};
use serializer::{Response, Serializer};
use config::Handler;
use handlers::{files, proxy};
use websocket;

#[derive(Clone)]
pub struct Main {
    pub config: ConfigCell,
    pub handle: Handle,
    pub curl_session: Session,
}

impl Service for Main {
    type Request = Request;
    type Response = Serializer;
    type Error = Error;
    type Future = BoxFuture<Self::Response, Error>;

    fn call(&self, req: Request) -> Self::Future {
        // We must store configuration for specific request for the case
        // it changes in runtime. Config changes in the middle of request
        // can create undesirable effects
        let cfg = self.config.get();
        let mut debug = DebugInfo::new(&req);

        let matched_route = req.host().map(parse_host)
            .and_then(|host| route(host, &req.path, &cfg.routing));
        let response = if let Some((route, suffix)) = matched_route {
            debug.set_route(route);
            match cfg.handlers.get(route) {
                Some(&Handler::EmptyGif) => {
                    Response::EmptyGif
                }
                Some(&Handler::Static(ref settings)) => {
                    if let Ok(path) = files::path(settings, suffix, &req) {
                        Response::Static {
                            path: path,
                            settings: settings.clone(),
                        }
                    } else {
                        Response::ErrorPage(403)
                    }
                }
                Some(&Handler::WebsocketEcho) => {
                    match websocket::prepare(&req) {
                        Ok(init) => {
                            Response::WebsocketEcho(init)
                        }
                        Err(status) => {
                            // TODO(tailhook) use real status
                            Response::ErrorPage(
                                ::minihttp::enums::HttpStatus::code(&status)
                            )
                        }
                    }
                }
                Some(&Handler::Proxy(ref settings)) => {
                    if let Some(dest) = cfg.http_destinations.get(&settings.destination.upstream) {
                        match proxy::prepare(&req, &dest, settings.clone()) {
                            Ok(call) => {
                                Response::Proxy {
                                    session: self.curl_session.clone(),
                                    call: call,
                                }
                            }
                            Err(_) => {
                                Response::ErrorPage(500)
                            }
                        }
                    } else {
                        Response::ErrorPage(404)
                    }
                }
                // TODO(tailhook) make better error code for None
                _ => {
                    // Not implemented
                    Response::ErrorPage(501)
                }
            }
        } else {
            Response::ErrorPage(404)
        };
        response.serve(cfg.clone(), debug, &self.handle)
    }

    fn poll_ready(&self) -> Async<()> {
        Async::Ready(())
    }
}
<|endoftext|>


<commit_before>use super::*;

use std::collections::VecDeque;
use std::io::{BufReader, BufRead, Error, ErrorKind};
use std::fs::{File, OpenOptions};
use std::io::{self, Read, Seek, SeekFrom, Write};
use std::ops::Index;
use std::ops::IndexMut;

const DEFAULT_MAX_SIZE: usize = 1000;

/// Structure encapsulating command history
pub struct History {
    // TODO: this should eventually be private
    /// Vector of buffers to store history in
    pub buffers: VecDeque<Buffer>,
    // TODO: Do we need this here? Ion can take care of this.
    // pub previous_status: i32,
    /// Store a filename to save history into; if None don't save history
    file_name: Option<String>,
    /// Maximal number of buffers stored in the memory
    max_size: usize,
    /// Maximal number of lines stored in the file
    max_file_size: usize,
}

impl History {
    /// Create new History structure.
    pub fn new() -> History {
        History {
            buffers: VecDeque::with_capacity(DEFAULT_MAX_SIZE),
            file_name: None,
            max_size: DEFAULT_MAX_SIZE,
            max_file_size: DEFAULT_MAX_SIZE,
        }
    }

    /// Number of items in history.
    pub fn len(&self) -> usize {
        self.buffers.len()
    }

    /// Add a command to the history buffer and remove the oldest commands when the max history
    /// size has been met. If writing to the disk is enabled, this function will be used for
    /// logging history to the designated history file.
    pub fn push(&mut self, new_item: Buffer) -> io::Result<()> {

        let mut ret = Ok(());
        self.file_name.as_ref().map(|name| {
            ret = self.write_to_disk(&new_item, name);
        });

        // buffers[0] is the oldest entry
        // the new entry goes to the end
        self.buffers.push_back(new_item);
        while self.buffers.len() > self.max_size {
            self.buffers.pop_front();
        }
        ret
    }

    /// Go through the history and try to find a buffer which starts the same as the new buffer
    /// given to this function as argument.
    pub fn get_newest_match<'a, 'b>(&'a self, curr_position: Option<usize>, new_buff: &'b Buffer) -> Option<&'a Buffer> {
        let pos = curr_position.unwrap_or(self.buffers.len());
        for iter in (0..pos).rev() {
            if let Some(tested) = self.buffers.get(iter) {
                if tested.starts_with(new_buff) {
                    return self.buffers.get(iter)
                }
            }
        }
        None
    }

    /// Get the history file name.
    pub fn file_name(&self) -> Option<&str> {
        match self.file_name {
            Some(ref s) => Some(&s[..]),
            None => None,
        }
    }

    /// Set history file name. At the same time enable history.
    pub fn set_file_name(&mut self, name: Option<String>) {
        self.file_name = name;
    }

    /// Set maximal number of buffers stored in memory
    pub fn set_max_size(&mut self, size: usize) {
        self.max_size = size;
    }

    /// Set maximal number of entries in history file
    pub fn set_max_file_size(&mut self, size: usize) {
        self.max_file_size = size;
    }

    /// Load history from given file name
    pub fn load_history(&mut self) -> io::Result<()> {
        let file_name = match self.file_name.clone() {
            Some(name) => name,
            None => return Err(Error::new(ErrorKind::Other, "Liner: file name not specified")),
        };
        let file = try!(OpenOptions::new().read(true).open(file_name));
        let reader = BufReader::new(file);
        for line in reader.lines() {
            match line {
                Ok(line) => self.buffers.push_back(Buffer::from(line)),
                Err(_) => break,
            }
        }
        Ok(())
    }

    /// Perform write operation. If the history file does not exist, it will be created.
    /// This function is not part of the public interface.
    /// XXX: include more information in the file (like fish does)
    fn write_to_disk(&self, new_item: &Buffer, file_name: &String) -> io::Result<()> {
        let ret = match OpenOptions::new().read(true).write(true).create(true).open(file_name) {
            Ok(mut file) => {
                // Determine the number of commands stored and the file length.
                let (file_length, commands_stored) = {
                    let mut commands_stored = 0;
                    let mut file_length = 0;
                    let file = File::open(file_name).unwrap();
                    for byte in file.bytes() {
                        if byte.unwrap_or(b' ') == b'\n' {
                            commands_stored += 1;
                        }
                        file_length += 1;
                    }
                    (file_length, commands_stored)
                };

                // If the max history file size has been reached, truncate the file so that only
                // N amount of commands are listed. To truncate the file, the seek point will be
                // discovered by counting the number of bytes until N newlines have been found and
                // then the file will be seeked to that point, copying all data after and rewriting
                // the file with the first N lines removed.
                if commands_stored >= self.max_file_size {
                    let seek_point = {
                        let commands_to_delete = commands_stored - self.max_file_size + 1;
                        let mut matched = 0;
                        let mut bytes = 0;
                        let file = File::open(file_name).unwrap();
                        for byte in file.bytes() {
                            if byte.unwrap_or(b' ') == b'\n' {
                                matched += 1;
                            }
                            bytes += 1;
                            if matched == commands_to_delete {
                                break;
                            }
                        }
                        bytes as u64
                    };

                    try!(file.seek(SeekFrom::Start(seek_point)));
                    let mut buffer: Vec<u8> = Vec::with_capacity(file_length - seek_point as usize);
                    try!(file.read_to_end(&mut buffer));
                    try!(file.set_len(0));
                    try!(io::copy(&mut buffer.as_slice(), &mut file));

                }

                // Seek to end for appending
                try!(file.seek(SeekFrom::End(0)));
                // Write the command to the history file.
                try!(file.write_all(String::from(new_item.clone()).as_bytes()));
                try!(file.write(b"\n"));

                Ok(())
            }
            Err(message) => Err(message),
        };
        ret
    }
}

impl Index<usize> for History {
    type Output = Buffer;

    fn index(&self, index: usize) -> &Buffer {
        &self.buffers[index]
    }
}

impl IndexMut<usize> for History {
    fn index_mut(&mut self, index: usize) -> &mut Buffer {
        &mut self.buffers[index]
    }
}
<commit_msg>add option to filter dup history entries, closes #57<commit_after>use super::*;

use std::collections::VecDeque;
use std::io::{BufReader, BufRead, Error, ErrorKind};
use std::fs::{File, OpenOptions};
use std::io::{self, Read, Seek, SeekFrom, Write};
use std::ops::Index;
use std::ops::IndexMut;

const DEFAULT_MAX_SIZE: usize = 1000;

/// Structure encapsulating command history
pub struct History {
    // TODO: this should eventually be private
    /// Vector of buffers to store history in
    pub buffers: VecDeque<Buffer>,
    // TODO: Do we need this here? Ion can take care of this.
    // pub previous_status: i32,
    /// Store a filename to save history into; if None don't save history
    file_name: Option<String>,
    /// Maximal number of buffers stored in the memory
    /// TODO: just make this public?
    max_size: usize,
    /// Maximal number of lines stored in the file
    // TODO: just make this public?
    max_file_size: usize,

    // TODO set from environment variable?
    pub append_duplicate_entries: bool,
}

impl History {
    /// Create new History structure.
    pub fn new() -> History {
        History {
            buffers: VecDeque::with_capacity(DEFAULT_MAX_SIZE),
            file_name: None,
            max_size: DEFAULT_MAX_SIZE,
            max_file_size: DEFAULT_MAX_SIZE,
            append_duplicate_entries: false,
        }
    }

    /// Number of items in history.
    pub fn len(&self) -> usize {
        self.buffers.len()
    }

    /// Add a command to the history buffer and remove the oldest commands when the max history
    /// size has been met. If writing to the disk is enabled, this function will be used for
    /// logging history to the designated history file.
    pub fn push(&mut self, new_item: Buffer) -> io::Result<()> {
        let mut ret = Ok(());
        self.file_name
            .as_ref()
            .map(|name| { ret = self.write_to_disk(&new_item, name); });

        // buffers[0] is the oldest entry
        // the new entry goes to the end
        if !self.append_duplicate_entries &&
            self.buffers.back().map(|b| b.to_string()) == Some(new_item.to_string())
        {
            return Ok(());
        }
        self.buffers.push_back(new_item);
        while self.buffers.len() > self.max_size {
            self.buffers.pop_front();
        }
        ret
    }

    /// Go through the history and try to find a buffer which starts the same as the new buffer
    /// given to this function as argument.
    pub fn get_newest_match<'a, 'b>(
        &'a self,
        curr_position: Option<usize>,
        new_buff: &'b Buffer,
    ) -> Option<&'a Buffer> {
        let pos = curr_position.unwrap_or(self.buffers.len());
        for iter in (0..pos).rev() {
            if let Some(tested) = self.buffers.get(iter) {
                if tested.starts_with(new_buff) {
                    return self.buffers.get(iter);
                }
            }
        }
        None
    }

    /// Get the history file name.
    pub fn file_name(&self) -> Option<&str> {
        match self.file_name {
            Some(ref s) => Some(&s[..]),
            None => None,
        }
    }

    /// Set history file name. At the same time enable history.
    pub fn set_file_name(&mut self, name: Option<String>) {
        self.file_name = name;
    }

    /// Set maximal number of buffers stored in memory
    pub fn set_max_size(&mut self, size: usize) {
        self.max_size = size;
    }

    /// Set maximal number of entries in history file
    pub fn set_max_file_size(&mut self, size: usize) {
        self.max_file_size = size;
    }

    /// Load history from given file name
    pub fn load_history(&mut self) -> io::Result<()> {
        let file_name = match self.file_name.clone() {
            Some(name) => name,
            None => {
                return Err(Error::new(
                    ErrorKind::Other,
                    "Liner: file name not specified",
                ))
            }
        };
        let file = try!(OpenOptions::new().read(true).open(file_name));
        let reader = BufReader::new(file);
        for line in reader.lines() {
            match line {
                Ok(line) => self.buffers.push_back(Buffer::from(line)),
                Err(_) => break,
            }
        }
        Ok(())
    }

    /// Perform write operation. If the history file does not exist, it will be created.
    /// This function is not part of the public interface.
    /// XXX: include more information in the file (like fish does)
    fn write_to_disk(&self, new_item: &Buffer, file_name: &String) -> io::Result<()> {
        let ret = match OpenOptions::new()
            .read(true)
            .write(true)
            .create(true)
            .open(file_name) {
            Ok(mut file) => {
                // Determine the number of commands stored and the file length.
                let (file_length, commands_stored) = {
                    let mut commands_stored = 0;
                    let mut file_length = 0;
                    let file = File::open(file_name).unwrap();
                    for byte in file.bytes() {
                        if byte.unwrap_or(b' ') == b'\n' {
                            commands_stored += 1;
                        }
                        file_length += 1;
                    }
                    (file_length, commands_stored)
                };

                // If the max history file size has been reached, truncate the file so that only
                // N amount of commands are listed. To truncate the file, the seek point will be
                // discovered by counting the number of bytes until N newlines have been found and
                // then the file will be seeked to that point, copying all data after and rewriting
                // the file with the first N lines removed.
                if commands_stored >= self.max_file_size {
                    let seek_point = {
                        let commands_to_delete = commands_stored - self.max_file_size + 1;
                        let mut matched = 0;
                        let mut bytes = 0;
                        let file = File::open(file_name).unwrap();
                        for byte in file.bytes() {
                            if byte.unwrap_or(b' ') == b'\n' {
                                matched += 1;
                            }
                            bytes += 1;
                            if matched == commands_to_delete {
                                break;
                            }
                        }
                        bytes as u64
                    };

                    try!(file.seek(SeekFrom::Start(seek_point)));
                    let mut buffer: Vec<u8> = Vec::with_capacity(file_length - seek_point as usize);
                    try!(file.read_to_end(&mut buffer));
                    try!(file.set_len(0));
                    try!(io::copy(&mut buffer.as_slice(), &mut file));

                }

                // Seek to end for appending
                try!(file.seek(SeekFrom::End(0)));
                // Write the command to the history file.
                try!(file.write_all(String::from(new_item.clone()).as_bytes()));
                try!(file.write(b"\n"));

                Ok(())
            }
            Err(message) => Err(message),
        };
        ret
    }
}

impl Index<usize> for History {
    type Output = Buffer;

    fn index(&self, index: usize) -> &Buffer {
        &self.buffers[index]
    }
}

impl IndexMut<usize> for History {
    fn index_mut(&mut self, index: usize) -> &mut Buffer {
        &mut self.buffers[index]
    }
}
<|endoftext|>


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

//! The layout task. Performs layout on the DOM, builds display lists and sends them to be
/// rendered.

use css::matching::MatchMethods;
use css::select::new_css_select_ctx;
use layout::aux::{LayoutData, LayoutAuxMethods};
use layout::box::RenderBox;
use layout::box_builder::LayoutTreeBuilder;
use layout::context::LayoutContext;
use layout::display_list_builder::{DisplayListBuilder};
use layout::flow::FlowContext;

use std::cast::transmute;
use std::cell::Cell;
use std::comm::{Port};
use geom::point::Point2D;
use geom::rect::Rect;
use geom::size::Size2D;
use gfx::display_list::DisplayList;
use gfx::font_context::FontContext;
use gfx::geometry::Au;
use gfx::opts::Opts;
use gfx::render_task::{RenderMsg, RenderChan, RenderLayer};
use newcss::select::SelectCtx;
use newcss::stylesheet::Stylesheet;
use newcss::types::OriginAuthor;
use script::dom::event::ReflowEvent;
use script::dom::node::{AbstractNode, LayoutView};
use script::layout_interface::{AddStylesheetMsg, ContentBoxQuery};
use script::layout_interface::{HitTestQuery, ContentBoxResponse, HitTestResponse};
use script::layout_interface::{ContentBoxesQuery, ContentBoxesResponse, ExitMsg, LayoutQuery};
use script::layout_interface::{MatchSelectorsDocumentDamage, Msg};
use script::layout_interface::{QueryMsg, RouteScriptMsg, Reflow, ReflowDocumentDamage};
use script::layout_interface::{ReflowForDisplay, ReflowMsg};
use script::script_task::{ReflowCompleteMsg, ScriptChan, ScriptMsg, SendEventMsg};
use servo_net::image_cache_task::{ImageCacheTask, ImageResponseMsg};
use servo_net::local_image_cache::LocalImageCache;
use servo_util::tree::{TreeNodeRef, TreeUtils};
use servo_util::time::{ProfilerChan, profile};
use servo_util::time;
use extra::net::url::Url;

struct LayoutTask {
    port: Port<Msg>,
    script_chan: ScriptChan,
    render_chan: RenderChan,
    image_cache_task: ImageCacheTask,
    local_image_cache: @mut LocalImageCache,
    font_ctx: @mut FontContext,
    doc_url: Option<Url>,
    screen_size: Option<Size2D<Au>>,

    /// This is used to root reader data.
    layout_refs: ~[@mut LayoutData],

    css_select_ctx: @mut SelectCtx,
    profiler_chan: ProfilerChan,
}

impl LayoutTask {
    pub fn create(port: Port<Msg>,
                              script_chan: ScriptChan,
                              render_chan: RenderChan,
                              img_cache_task: ImageCacheTask,
                              opts: Opts,
                              profiler_chan: ProfilerChan) {
        let port = Cell::new(port);
        do spawn {
            let mut layout = LayoutTask::new(port.take(),
                                         script_chan.clone(),
                                         render_chan.clone(),
                                         img_cache_task.clone(),
                                         &opts,
                                         profiler_chan.clone());
            layout.start();
        };
    }

    fn new(port: Port<Msg>,
           script_chan: ScriptChan,
           render_chan: RenderChan,
           image_cache_task: ImageCacheTask,
           opts: &Opts,
           profiler_chan: ProfilerChan)
           -> LayoutTask {
        let fctx = @mut FontContext::new(opts.render_backend, true, profiler_chan.clone());

        LayoutTask {
            port: port,
            script_chan: script_chan,
            render_chan: render_chan,
            image_cache_task: image_cache_task.clone(),
            local_image_cache: @mut LocalImageCache(image_cache_task),
            font_ctx: fctx,
            doc_url: None,
            screen_size: None,

            layout_refs: ~[],
            css_select_ctx: @mut new_css_select_ctx(),
            profiler_chan: profiler_chan,
        }
    }

    fn start(&mut self) {
        while self.handle_request() {
            // Loop indefinitely.
        }
    }

    // Create a layout context for use in building display lists, hit testing, &c.
    fn build_layout_context(&self) -> LayoutContext {
        let image_cache = self.local_image_cache;
        let font_ctx = self.font_ctx;
        let screen_size = self.screen_size.unwrap();

        LayoutContext {
            image_cache: image_cache,
            font_ctx: font_ctx,
            screen_size: Rect(Point2D(Au(0), Au(0)), screen_size),
        }
    }

    fn handle_request(&mut self) -> bool {
        match self.port.recv() {
            AddStylesheetMsg(sheet) => self.handle_add_stylesheet(sheet),
            ReflowMsg(data) => {
                let data = Cell::new(data);

                do profile(time::LayoutPerformCategory, self.profiler_chan.clone()) {
                    self.handle_reflow(data.take());
                }
            }
            QueryMsg(query) => {
                let query = Cell::new(query);
                do profile(time::LayoutQueryCategory, self.profiler_chan.clone()) {
                    self.handle_query(query.take());
                }
            }
            RouteScriptMsg(script_msg) => {
                debug!("layout: routing %? to script task", script_msg);
                self.route_script_msg(script_msg);
            }
            ExitMsg => {
                debug!("layout: ExitMsg received");
                return false
            }
        }

        true
    }

    fn handle_add_stylesheet(&self, sheet: Stylesheet) {
        let sheet = Cell::new(sheet);
        self.css_select_ctx.append_sheet(sheet.take(), OriginAuthor);
    }

    /// The high-level routine that performs layout tasks.
    fn handle_reflow(&mut self, data: &Reflow) {
        // FIXME: Isolate this transmutation into a "bridge" module.
        let node: &AbstractNode<LayoutView> = unsafe {
            transmute(&data.document_root)
        };

        // FIXME: Bad copy!
        let doc_url = copy data.url;
        let script_chan = data.script_chan.clone();

        debug!("layout: received layout request for: %s", doc_url.to_str());
        debug!("layout: damage is %?", data.damage);
        debug!("layout: parsed Node tree");
        debug!("%?", node.dump());
        // Reset the image cache.
        self.local_image_cache.next_round(self.make_on_image_available_cb(script_chan));

        self.doc_url = Some(doc_url);
        let screen_size = Size2D(Au::from_px(data.window_size.width as int),
                                 Au::from_px(data.window_size.height as int));
        self.screen_size = Some(screen_size);

        // Create a layout context for use throughout the following passes.
        let mut layout_ctx = self.build_layout_context();

        // Initialize layout data for each node.
        //
        // FIXME: This is inefficient. We don't need an entire traversal to do this!
        do profile(time::LayoutAuxInitCategory, self.profiler_chan.clone()) {
            node.initialize_style_for_subtree(&mut self.layout_refs);
        }

        // Perform CSS selector matching if necessary.
        match data.damage.level {
            ReflowDocumentDamage => {}
            MatchSelectorsDocumentDamage => {
                do profile(time::LayoutSelectorMatchCategory, self.profiler_chan.clone()) {
                    node.restyle_subtree(self.css_select_ctx);
                }
            }
        }

        // Construct the flow tree.
        let layout_root: FlowContext = do profile(time::LayoutTreeBuilderCategory,
                                                  self.profiler_chan.clone()) {
            let mut builder = LayoutTreeBuilder::new();
            let layout_root: FlowContext = match builder.construct_trees(&layout_ctx, *node) {
                Ok(root) => root,
                Err(*) => fail!(~"Root flow should always exist")
            };

            debug!("layout: constructed Flow tree");
            debug!("%?", layout_root.dump());

            layout_root
        };

        // Perform the primary layout passes over the flow tree to compute the locations of all
        // the boxes.
        do profile(time::LayoutMainCategory, self.profiler_chan.clone()) {
            for layout_root.traverse_postorder |flow| {
                flow.bubble_widths(&mut layout_ctx);
            };
            for layout_root.traverse_preorder |flow| {
                flow.assign_widths(&mut layout_ctx);
            };

            // For now, this is an inorder traversal
            layout_root.assign_height(&mut layout_ctx);
        }

        // Build the display list if necessary, and send it to the renderer.
        if data.goal == ReflowForDisplay {
            do profile(time::LayoutDispListBuildCategory, self.profiler_chan.clone()) {
                let builder = DisplayListBuilder {
                    ctx: &layout_ctx,
                };

                let display_list = @Cell::new(DisplayList::new());

                // TODO: Set options on the builder before building.
                // TODO: Be smarter about what needs painting.
                for layout_root.traverse_preorder |flow| {
                    flow.build_display_list(&builder, &layout_root.position(), display_list);
                }

                let root_size = do layout_root.with_base |base| {
                    base.position.size
                };

                let render_layer = RenderLayer {
                    display_list: display_list.take(),
                    size: Size2D(root_size.width.to_px() as uint, root_size.height.to_px() as uint)
                };

                self.render_chan.send(RenderMsg(render_layer));
            } // time(layout: display list building)
        }

        debug!("%?", layout_root.dump());

        // Tell script that we're done.
        //
        // FIXME(pcwalton): This should probably be *one* channel, but we can't fix this without
        // either select or a filtered recv() that only looks for messages of a given type.
        data.script_join_chan.send(());
        data.script_chan.send(ReflowCompleteMsg);
    }

    /// Handles a query from the script task. This is the main routine that DOM functions like
    /// `getClientRects()` or `getBoundingClientRect()` ultimately invoke.
    fn handle_query(&self, query: LayoutQuery) {
        match query {
            ContentBoxQuery(node, reply_chan) => {
                // FIXME: Isolate this transmutation into a single "bridge" module.
                let node: AbstractNode<LayoutView> = unsafe {
                    transmute(node)
                };

                let response = match node.layout_data().flow {
                    None => {
                        error!("no flow present");
                        Err(())
                    }
                    Some(flow) => {
                        let start_val: Option<Rect<Au>> = None;
                        let rect = do flow.foldl_boxes_for_node(node, start_val) |acc, box| {
                            match acc {
                                Some(acc) => Some(acc.union(&box.content_box())),
                                None => Some(box.content_box())
                            }
                        };

                        match rect {
                            None => {
                                error!("no boxes for node");
                                Err(())
                            }
                            Some(rect) => Ok(ContentBoxResponse(rect))
                        }
                    }
                };

                reply_chan.send(response)
            }
            ContentBoxesQuery(node, reply_chan) => {
                // FIXME: Isolate this transmutation into a single "bridge" module.
                let node: AbstractNode<LayoutView> = unsafe {
                    transmute(node)
                };

                let response = match node.layout_data().flow {
                    None => Err(()),
                    Some(flow) => {
                        let mut boxes = ~[];
                        for flow.iter_boxes_for_node(node) |box| {
                            boxes.push(box.content_box());
                        }

                        Ok(ContentBoxesResponse(boxes))
                    }
                };

                reply_chan.send(response)
            }
            HitTestQuery(node, point, reply_chan) => {
                // FIXME: Isolate this transmutation into a single "bridge" module.
                let node: AbstractNode<LayoutView> = unsafe {
                    transmute(node)
                };
                let mut flow_node: AbstractNode<LayoutView> = node;
                for node.traverse_preorder |node| {
                    if node.layout_data().flow.is_some() {
                        flow_node = node;
                        break;
                    }
                };

                let response = match flow_node.layout_data().flow {
                    None => {
                        debug!("HitTestQuery: flow is None");
                        Err(())
                    }
                    Some(flow) => {
                        let layout_ctx = self.build_layout_context();
                        let builder = DisplayListBuilder {
                            ctx: &layout_ctx,
                        };
                        let display_list: @Cell<DisplayList<RenderBox>> =
                            @Cell::new(DisplayList::new());
                        flow.build_display_list(&builder,
                                                &flow.position(),
                                                display_list);
                        let (x, y) = (Au::from_frac_px(point.x as float),
                                      Au::from_frac_px(point.y as float));
                        let mut resp = Err(());
                        let display_list = &display_list.take().list;
                        // iterate in reverse to ensure we have the most recently painted render box
                        for display_list.rev_iter().advance |display_item| {
                            let bounds = display_item.bounds();
                            // TODO this check should really be performed by a method of DisplayItem
                            if x <= bounds.origin.x + bounds.size.width &&
                               bounds.origin.x <= x &&
                               y < bounds.origin.y + bounds.size.height &&
                               bounds.origin.y <  y {
                                resp = Ok(HitTestResponse(display_item.base().extra.node()));
                                break;
                            }
                        }
                        resp
                    }
                };

                reply_chan.send(response)
            }
        }
    }

    // TODO(tkuehn): once there are multiple script tasks, this is where the layout task will
    // determine which script task should receive the message. The prototype will need to change
    fn route_script_msg(&self, script_msg: ScriptMsg) {
        self.script_chan.send(script_msg);
    }

    // When images can't be loaded in time to display they trigger
    // this callback in some task somewhere. This will send a message
    // to the script task, and ultimately cause the image to be
    // re-requested. We probably don't need to go all the way back to
    // the script task for this.
    fn make_on_image_available_cb(&self, script_chan: ScriptChan)
                                  -> @fn() -> ~fn(ImageResponseMsg) {
        // This has a crazy signature because the image cache needs to
        // make multiple copies of the callback, and the dom event
        // channel is not a copyable type, so this is actually a
        // little factory to produce callbacks
        let f: @fn() -> ~fn(ImageResponseMsg) = || {
            let script_chan = script_chan.clone();
            let f: ~fn(ImageResponseMsg) = |_| {
                script_chan.send(SendEventMsg(ReflowEvent))
            };
            f
        };
        return f;
    }
}

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

//! The layout task. Performs layout on the DOM, builds display lists and sends them to be
/// rendered.

use css::matching::MatchMethods;
use css::select::new_css_select_ctx;
use layout::aux::{LayoutData, LayoutAuxMethods};
use layout::box::RenderBox;
use layout::box_builder::LayoutTreeBuilder;
use layout::context::LayoutContext;
use layout::display_list_builder::{DisplayListBuilder};
use layout::flow::FlowContext;

use std::cast::transmute;
use std::cell::Cell;
use std::comm::{Port};
use geom::point::Point2D;
use geom::rect::Rect;
use geom::size::Size2D;
use gfx::display_list::DisplayList;
use gfx::font_context::FontContext;
use gfx::geometry::Au;
use gfx::opts::Opts;
use gfx::render_task::{RenderMsg, RenderChan, RenderLayer};
use newcss::select::SelectCtx;
use newcss::stylesheet::Stylesheet;
use newcss::types::OriginAuthor;
use script::dom::event::ReflowEvent;
use script::dom::node::{AbstractNode, LayoutView};
use script::layout_interface::{AddStylesheetMsg, ContentBoxQuery};
use script::layout_interface::{HitTestQuery, ContentBoxResponse, HitTestResponse};
use script::layout_interface::{ContentBoxesQuery, ContentBoxesResponse, ExitMsg, LayoutQuery};
use script::layout_interface::{MatchSelectorsDocumentDamage, Msg};
use script::layout_interface::{QueryMsg, RouteScriptMsg, Reflow, ReflowDocumentDamage};
use script::layout_interface::{ReflowForDisplay, ReflowMsg};
use script::script_task::{ReflowCompleteMsg, ScriptChan, ScriptMsg, SendEventMsg};
use servo_net::image_cache_task::{ImageCacheTask, ImageResponseMsg};
use servo_net::local_image_cache::LocalImageCache;
use servo_util::tree::{TreeNodeRef, TreeUtils};
use servo_util::time::{ProfilerChan, profile};
use servo_util::time;
use extra::net::url::Url;

struct LayoutTask {
    port: Port<Msg>,
    script_chan: ScriptChan,
    render_chan: RenderChan,
    image_cache_task: ImageCacheTask,
    local_image_cache: @mut LocalImageCache,
    font_ctx: @mut FontContext,
    doc_url: Option<Url>,
    screen_size: Option<Size2D<Au>>,

    /// This is used to root reader data.
    layout_refs: ~[@mut LayoutData],

    css_select_ctx: @mut SelectCtx,
    profiler_chan: ProfilerChan,
}

impl LayoutTask {
    pub fn create(port: Port<Msg>,
                              script_chan: ScriptChan,
                              render_chan: RenderChan,
                              img_cache_task: ImageCacheTask,
                              opts: Opts,
                              profiler_chan: ProfilerChan) {
        let port = Cell::new(port);
        do spawn {
            let mut layout = LayoutTask::new(port.take(),
                                         script_chan.clone(),
                                         render_chan.clone(),
                                         img_cache_task.clone(),
                                         &opts,
                                         profiler_chan.clone());
            layout.start();
        };
    }

    fn new(port: Port<Msg>,
           script_chan: ScriptChan,
           render_chan: RenderChan,
           image_cache_task: ImageCacheTask,
           opts: &Opts,
           profiler_chan: ProfilerChan)
           -> LayoutTask {
        let fctx = @mut FontContext::new(opts.render_backend, true, profiler_chan.clone());

        LayoutTask {
            port: port,
            script_chan: script_chan,
            render_chan: render_chan,
            image_cache_task: image_cache_task.clone(),
            local_image_cache: @mut LocalImageCache(image_cache_task),
            font_ctx: fctx,
            doc_url: None,
            screen_size: None,

            layout_refs: ~[],
            css_select_ctx: @mut new_css_select_ctx(),
            profiler_chan: profiler_chan,
        }
    }

    fn start(&mut self) {
        while self.handle_request() {
            // Loop indefinitely.
        }
    }

    // Create a layout context for use in building display lists, hit testing, &c.
    fn build_layout_context(&self) -> LayoutContext {
        let image_cache = self.local_image_cache;
        let font_ctx = self.font_ctx;
        let screen_size = self.screen_size.unwrap();

        LayoutContext {
            image_cache: image_cache,
            font_ctx: font_ctx,
            screen_size: Rect(Point2D(Au(0), Au(0)), screen_size),
        }
    }

    fn handle_request(&mut self) -> bool {
        match self.port.recv() {
            AddStylesheetMsg(sheet) => self.handle_add_stylesheet(sheet),
            ReflowMsg(data) => {
                let data = Cell::new(data);

                do profile(time::LayoutPerformCategory, self.profiler_chan.clone()) {
                    self.handle_reflow(data.take());
                }
            }
            QueryMsg(query) => {
                let query = Cell::new(query);
                do profile(time::LayoutQueryCategory, self.profiler_chan.clone()) {
                    self.handle_query(query.take());
                }
            }
            RouteScriptMsg(script_msg) => {
                debug!("layout: routing %? to script task", script_msg);
                self.route_script_msg(script_msg);
            }
            ExitMsg => {
                debug!("layout: ExitMsg received");
                return false
            }
        }

        true
    }

    fn handle_add_stylesheet(&self, sheet: Stylesheet) {
        let sheet = Cell::new(sheet);
        self.css_select_ctx.append_sheet(sheet.take(), OriginAuthor);
    }

    /// The high-level routine that performs layout tasks.
    fn handle_reflow(&mut self, data: &Reflow) {
        // FIXME: Isolate this transmutation into a "bridge" module.
        let node: &AbstractNode<LayoutView> = unsafe {
            transmute(&data.document_root)
        };

        // FIXME: Bad copy!
        let doc_url = copy data.url;
        let script_chan = data.script_chan.clone();

        debug!("layout: received layout request for: %s", doc_url.to_str());
        debug!("layout: damage is %?", data.damage);
        debug!("layout: parsed Node tree");
        debug!("%?", node.dump());
        // Reset the image cache.
        self.local_image_cache.next_round(self.make_on_image_available_cb(script_chan));

        self.doc_url = Some(doc_url);
        let screen_size = Size2D(Au::from_px(data.window_size.width as int),
                                 Au::from_px(data.window_size.height as int));
        self.screen_size = Some(screen_size);

        // Create a layout context for use throughout the following passes.
        let mut layout_ctx = self.build_layout_context();

        // Initialize layout data for each node.
        //
        // FIXME: This is inefficient. We don't need an entire traversal to do this!
        do profile(time::LayoutAuxInitCategory, self.profiler_chan.clone()) {
            node.initialize_style_for_subtree(&mut self.layout_refs);
        }

        // Perform CSS selector matching if necessary.
        match data.damage.level {
            ReflowDocumentDamage => {}
            MatchSelectorsDocumentDamage => {
                do profile(time::LayoutSelectorMatchCategory, self.profiler_chan.clone()) {
                    node.restyle_subtree(self.css_select_ctx);
                }
            }
        }

        // Construct the flow tree.
        let layout_root: FlowContext = do profile(time::LayoutTreeBuilderCategory,
                                                  self.profiler_chan.clone()) {
            let mut builder = LayoutTreeBuilder::new();
            let layout_root: FlowContext = match builder.construct_trees(&layout_ctx, *node) {
                Ok(root) => root,
                Err(*) => fail!(~"Root flow should always exist")
            };

            debug!("layout: constructed Flow tree");
            debug!("%?", layout_root.dump());

            layout_root
        };

        // Perform the primary layout passes over the flow tree to compute the locations of all
        // the boxes.
        do profile(time::LayoutMainCategory, self.profiler_chan.clone()) {
            for layout_root.traverse_postorder |flow| {
                flow.bubble_widths(&mut layout_ctx);
            };
            for layout_root.traverse_preorder |flow| {
                flow.assign_widths(&mut layout_ctx);
            };

            // For now, this is an inorder traversal
            layout_root.assign_height(&mut layout_ctx);
        }

        // Build the display list if necessary, and send it to the renderer.
        if data.goal == ReflowForDisplay {
            do profile(time::LayoutDispListBuildCategory, self.profiler_chan.clone()) {
                let builder = DisplayListBuilder {
                    ctx: &layout_ctx,
                };

                let display_list = @Cell::new(DisplayList::new());

                // TODO: Set options on the builder before building.
                // TODO: Be smarter about what needs painting.
                do layout_root.partially_traverse_preorder |flow| {
                    flow.build_display_list(&builder, &layout_root.position(), display_list)
                }

                let root_size = do layout_root.with_base |base| {
                    base.position.size
                };

                let render_layer = RenderLayer {
                    display_list: display_list.take(),
                    size: Size2D(root_size.width.to_px() as uint, root_size.height.to_px() as uint)
                };

                self.render_chan.send(RenderMsg(render_layer));
            } // time(layout: display list building)
        }

        debug!("%?", layout_root.dump());

        // Tell script that we're done.
        //
        // FIXME(pcwalton): This should probably be *one* channel, but we can't fix this without
        // either select or a filtered recv() that only looks for messages of a given type.
        data.script_join_chan.send(());
        data.script_chan.send(ReflowCompleteMsg);
    }

    /// Handles a query from the script task. This is the main routine that DOM functions like
    /// `getClientRects()` or `getBoundingClientRect()` ultimately invoke.
    fn handle_query(&self, query: LayoutQuery) {
        match query {
            ContentBoxQuery(node, reply_chan) => {
                // FIXME: Isolate this transmutation into a single "bridge" module.
                let node: AbstractNode<LayoutView> = unsafe {
                    transmute(node)
                };

                let response = match node.layout_data().flow {
                    None => {
                        error!("no flow present");
                        Err(())
                    }
                    Some(flow) => {
                        let start_val: Option<Rect<Au>> = None;
                        let rect = do flow.foldl_boxes_for_node(node, start_val) |acc, box| {
                            match acc {
                                Some(acc) => Some(acc.union(&box.content_box())),
                                None => Some(box.content_box())
                            }
                        };

                        match rect {
                            None => {
                                error!("no boxes for node");
                                Err(())
                            }
                            Some(rect) => Ok(ContentBoxResponse(rect))
                        }
                    }
                };

                reply_chan.send(response)
            }
            ContentBoxesQuery(node, reply_chan) => {
                // FIXME: Isolate this transmutation into a single "bridge" module.
                let node: AbstractNode<LayoutView> = unsafe {
                    transmute(node)
                };

                let response = match node.layout_data().flow {
                    None => Err(()),
                    Some(flow) => {
                        let mut boxes = ~[];
                        for flow.iter_boxes_for_node(node) |box| {
                            boxes.push(box.content_box());
                        }

                        Ok(ContentBoxesResponse(boxes))
                    }
                };

                reply_chan.send(response)
            }
            HitTestQuery(node, point, reply_chan) => {
                // FIXME: Isolate this transmutation into a single "bridge" module.
                let node: AbstractNode<LayoutView> = unsafe {
                    transmute(node)
                };
                let mut flow_node: AbstractNode<LayoutView> = node;
                for node.traverse_preorder |node| {
                    if node.layout_data().flow.is_some() {
                        flow_node = node;
                        break;
                    }
                };

                let response = match flow_node.layout_data().flow {
                    None => {
                        debug!("HitTestQuery: flow is None");
                        Err(())
                    }
                    Some(flow) => {
                        let layout_ctx = self.build_layout_context();
                        let builder = DisplayListBuilder {
                            ctx: &layout_ctx,
                        };
                        let display_list: @Cell<DisplayList<RenderBox>> =
                            @Cell::new(DisplayList::new());

                        do flow.partially_traverse_preorder |this_flow| {
                            this_flow.build_display_list(&builder,
                                                    &flow.position(),
                                                    display_list)

                        }
                        let (x, y) = (Au::from_frac_px(point.x as float),
                                      Au::from_frac_px(point.y as float));
                        let mut resp = Err(());
                        let display_list = &display_list.take().list;
                        // iterate in reverse to ensure we have the most recently painted render box
                        for display_list.rev_iter().advance |display_item| {
                            let bounds = display_item.bounds();
                            // TODO this check should really be performed by a method of DisplayItem
                            if x <= bounds.origin.x + bounds.size.width &&
                               bounds.origin.x <= x &&
                               y < bounds.origin.y + bounds.size.height &&
                               bounds.origin.y <  y {
                                resp = Ok(HitTestResponse(display_item.base().extra.node()));
                                break;
                            }
                        }
                        resp
                    }
                };

                reply_chan.send(response)
            }
        }
    }

    // TODO(tkuehn): once there are multiple script tasks, this is where the layout task will
    // determine which script task should receive the message. The prototype will need to change
    fn route_script_msg(&self, script_msg: ScriptMsg) {
        self.script_chan.send(script_msg);
    }

    // When images can't be loaded in time to display they trigger
    // this callback in some task somewhere. This will send a message
    // to the script task, and ultimately cause the image to be
    // re-requested. We probably don't need to go all the way back to
    // the script task for this.
    fn make_on_image_available_cb(&self, script_chan: ScriptChan)
                                  -> @fn() -> ~fn(ImageResponseMsg) {
        // This has a crazy signature because the image cache needs to
        // make multiple copies of the callback, and the dom event
        // channel is not a copyable type, so this is actually a
        // little factory to produce callbacks
        let f: @fn() -> ~fn(ImageResponseMsg) = || {
            let script_chan = script_chan.clone();
            let f: ~fn(ImageResponseMsg) = |_| {
                script_chan.send(SendEventMsg(ReflowEvent))
            };
            f
        };
        return f;
    }
}

<|endoftext|>