<commit_before>use std::io::Write;
use hyper::header::{ContentLength, ContentType};
use hyper::server::{Request, Response};
use hyper::status::StatusCode;
use hyper::uri::RequestUri;

macro_rules! dispatch {
    ($m0:expr => $h0:expr, $($m1:expr => $h1:expr,)*) => {{
        if $m0 { $h0 }
        $( else if $m1 { $h1 } )*
    }}
}

pub fn get(request: Request, mut response: Response) {
    println!("** Handling GET {}", request.uri);
    println!("** Incoming headers {:?}", request.headers);
    if let RequestUri::AbsolutePath(ref path) = request.uri {
        dispatch![
            path == "/" => index(&request, response),
            path == "/ip" => origin(&request, response),
            path.starts_with("/status") => status(&request, response),
            path.starts_with("/test") => test(&request, response),
        ];
    }
}

fn index(request: &Request, mut response: Response) {
    let index = "<!DOCTYPE html>

            <title>HTTPTIN</title>

HTTPTIN - HTTP tester in Rust and Rocket

    ";

    response.headers_mut().set(ContentType::html());
    response.headers_mut().set(ContentLength(index.len() as u64));
    response.start().unwrap().write(index.as_bytes());
}

fn status(request: &Request, mut response: Response) {
    *response.status_mut() = StatusCode::ImATeapot;
}

fn origin(request: &Request, mut response: Response) {
    let text = "Remote address decoding is not implemented yet";
    response.headers_mut().set(ContentType::plaintext());
    response.headers_mut().set(ContentLength(text.len() as u64));
    response.start().unwrap().write(text.as_bytes());
}

fn test(request: &Request, mut response: Response) {
    let text = format!("<!DOCTYPE html>

            <title>HTTPTIN TEST</title>

            Remote Address: {}

            Method: {}

            HTTPVersion: {}

            Headers: {}

            URI: {}

    ",
                       request.remote_addr,
                       request.method,
                       request.version,
                       request.headers,
                       request.uri);
    response.headers_mut().set(ContentType::html());
    response.headers_mut().set(ContentLength(text.len() as u64));
    response.start().unwrap().write(text.as_bytes());
}
<commit_msg>Call .unwrap() on write() result<commit_after>use std::io::Write;
use hyper::header::{ContentLength, ContentType};
use hyper::server::{Request, Response};
use hyper::status::StatusCode;
use hyper::uri::RequestUri;

macro_rules! dispatch {
    ($m0:expr => $h0:expr, $($m1:expr => $h1:expr,)*) => {{
        if $m0 { $h0 }
        $( else if $m1 { $h1 } )*
    }}
}

pub fn get(request: Request, mut response: Response) {
    println!("** Handling GET {}", request.uri);
    println!("** Incoming headers {:?}", request.headers);
    if let RequestUri::AbsolutePath(ref path) = request.uri {
        dispatch![
            path == "/" => index(&request, response),
            path == "/ip" => origin(&request, response),
            path.starts_with("/status") => status(&request, response),
            path.starts_with("/test") => test(&request, response),
        ];
    }
}

fn index(request: &Request, mut response: Response) {
    let index = "<!DOCTYPE html>

            <title>HTTPTIN</title>

HTTPTIN - HTTP tester in Rust and Rocket

    ";

    response.headers_mut().set(ContentType::html());
    response.headers_mut().set(ContentLength(index.len() as u64));
    response.start().unwrap().write(index.as_bytes()).unwrap();
}

fn status(request: &Request, mut response: Response) {
    *response.status_mut() = StatusCode::ImATeapot;
}

fn origin(request: &Request, mut response: Response) {
    let text = "Remote address decoding is not implemented yet";
    response.headers_mut().set(ContentType::plaintext());
    response.headers_mut().set(ContentLength(text.len() as u64));
    response.start().unwrap().write(text.as_bytes()).unwrap();
}

fn test(request: &Request, mut response: Response) {
    let text = format!("<!DOCTYPE html>

            <title>HTTPTIN TEST</title>

            Remote Address: {}

            Method: {}

            HTTPVersion: {}

            Headers: {}

            URI: {}

    ",
                       request.remote_addr,
                       request.method,
                       request.version,
                       request.headers,
                       request.uri);
    response.headers_mut().set(ContentType::html());
    response.headers_mut().set(ContentLength(text.len() as u64));
    response.start().unwrap().write(text.as_bytes()).unwrap();
}
<|endoftext|>


<commit_before>use bytes::Bytes;
use crypto::digest::Digest;
use crypto::sha1::Sha1;
use futures::sync::mpsc;
use futures::{Future, Stream, IntoFuture, Poll, Async};
use std::io;
use std::sync::{RwLock, Arc, Weak};
use tokio_core::reactor::{Handle, Remote};
use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering};

use apresolve::apresolve_or_fallback;
use authentication::Credentials;
use cache::Cache;
use component::Lazy;
use connection;
use config::SessionConfig;

use audio_key::AudioKeyManager;
use channel::ChannelManager;
use mercury::MercuryManager;

pub struct SessionData {
    country: String,
    canonical_username: String,
}

pub struct SessionInternal {
    config: SessionConfig,
    data: RwLock<SessionData>,

    tx_connection: mpsc::UnboundedSender<(u8, Vec<u8>)>,

    audio_key: Lazy<AudioKeyManager>,
    channel: Lazy<ChannelManager>,
    mercury: Lazy<MercuryManager>,
    cache: Option<Arc<Cache>>,

    handle: Remote,

    session_id: usize,
}

static SESSION_COUNTER : AtomicUsize = ATOMIC_USIZE_INIT;

#[derive(Clone)]
pub struct Session(pub Arc<SessionInternal>);

pub fn device_id(name: &str) -> String {
    let mut h = Sha1::new();
    h.input_str(name);
    h.result_str()
}

impl Session {
    pub fn connect(config: SessionConfig, credentials: Credentials,
                   cache: Option<Cache>, handle: Handle)
        -> Box<Future<Item=Session, Error=io::Error>>
    {
        let access_point = apresolve_or_fallback::<io::Error>(&handle);

        let handle_ = handle.clone();
        let connection = access_point.and_then(move |addr| {
            info!("Connecting to AP \"{}\"", addr);
            connection::connect::<&str>(&addr, &handle_)
        });

        let device_id = config.device_id.clone();
        let authentication = connection.and_then(move |connection| {
            connection::authenticate(connection, credentials, device_id)
        });

        let result = authentication.map(move |(transport, reusable_credentials)| {
            info!("Authenticated as \"{}\" !", reusable_credentials.username);
            if let Some(ref cache) = cache {
                cache.save_credentials(&reusable_credentials);
            }

            let (session, task) = Session::create(
                &handle, transport, config, cache, reusable_credentials.username.clone()
            );

            handle.spawn(task.map_err(|e| panic!(e)));

            session
        });

        Box::new(result)
    }

    fn create(handle: &Handle, transport: connection::Transport,
              config: SessionConfig, cache: Option<Cache>, username: String)
        -> (Session, Box<Future<Item = (), Error = io::Error>>)
    {
        let (sink, stream) = transport.split();

        let (sender_tx, sender_rx) = mpsc::unbounded();
        let session_id = SESSION_COUNTER.fetch_add(1, Ordering::Relaxed);

        debug!("new Session[{}]", session_id);

        let session = Session(Arc::new(SessionInternal {
            config: config,
            data: RwLock::new(SessionData {
                country: String::new(),
                canonical_username: username,
            }),

            tx_connection: sender_tx,

            cache: cache.map(Arc::new),

            audio_key: Lazy::new(),
            channel: Lazy::new(),
            mercury: Lazy::new(),

            handle: handle.remote().clone(),

            session_id: session_id,
        }));

        let sender_task = sender_rx
            .map_err(|e| -> io::Error { panic!(e) })
            .forward(sink).map(|_| ());
        let receiver_task = DispatchTask(stream, session.weak());

        let task = Box::new((receiver_task, sender_task).into_future()
            .map(|((), ())| ()));

        (session, task)
    }

    pub fn audio_key(&self) -> &AudioKeyManager {
        self.0.audio_key.get(|| AudioKeyManager::new(self.weak()))
    }

    pub fn channel(&self) -> &ChannelManager {
        self.0.channel.get(|| ChannelManager::new(self.weak()))
    }

    pub fn mercury(&self) -> &MercuryManager {
        self.0.mercury.get(|| MercuryManager::new(self.weak()))
    }

    pub fn spawn<F, R>(&self, f: F)
        where F: FnOnce(&Handle) -> R + Send + 'static,
              R: IntoFuture<Item=(), Error=()>,
              R::Future: 'static
    {
        self.0.handle.spawn(f)
    }

    fn debug_info(&self) {
        debug!("Session[{}] strong={} weak={}",
               self.0.session_id, Arc::strong_count(&self.0), Arc::weak_count(&self.0));
    }

    #[cfg_attr(feature = "cargo-clippy", allow(match_same_arms))]
    fn dispatch(&self, cmd: u8, data: Bytes) {
        match cmd {
            0x4 => {
                self.debug_info();
                self.send_packet(0x49, data.as_ref().to_owned());
            },
            0x4a => (),
            0x1b => {
                let country = String::from_utf8(data.as_ref().to_owned()).unwrap();
                info!("Country: {:?}", country);
                self.0.data.write().unwrap().country = country;
            }

            0x9 | 0xa => self.channel().dispatch(cmd, data),
            0xd | 0xe => self.audio_key().dispatch(cmd, data),
            0xb2...0xb6 => self.mercury().dispatch(cmd, data),
            _ => (),
        }
    }

    pub fn send_packet(&self, cmd: u8, data: Vec<u8>) {
        self.0.tx_connection.unbounded_send((cmd, data)).unwrap();
    }

    pub fn cache(&self) -> Option<&Arc<Cache>> {
        self.0.cache.as_ref()
    }

    pub fn config(&self) -> &SessionConfig {
        &self.0.config
    }

    pub fn username(&self) -> String {
        self.0.data.read().unwrap().canonical_username.clone()
    }

    pub fn country(&self) -> String {
        self.0.data.read().unwrap().country.clone()
    }

    pub fn device_id(&self) -> &str {
        &self.config().device_id
    }

    pub fn weak(&self) -> SessionWeak {
        SessionWeak(Arc::downgrade(&self.0))
    }

    pub fn session_id(&self) -> usize {
        self.0.session_id
    }
}

#[derive(Clone)]
pub struct SessionWeak(pub Weak<SessionInternal>);

impl SessionWeak {
    pub fn try_upgrade(&self) -> Option<Session> {
        self.0.upgrade().map(Session)
    }

    pub fn upgrade(&self) -> Session {
        self.try_upgrade().expect("Session died")
    }
}

impl Drop for SessionInternal {
    fn drop(&mut self) {
        debug!("drop Session[{}]", self.session_id);
    }
}

struct DispatchTask<S>(S, SessionWeak)
    where S: Stream<Item = (u8, Bytes)>;

impl <S> Future for DispatchTask<S>
    where S: Stream<Item = (u8, Bytes)>
{
    type Item = ();
    type Error = S::Error;

    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        let session = match self.1.try_upgrade() {
            Some(session) => session,
            None => {
                return Ok(Async::Ready(()))
            },
        };

        loop {
            let (cmd, data) = try_ready!(self.0.poll()).expect("connection closed");
            session.dispatch(cmd, data);
        }
    }
}

impl <S> Drop for DispatchTask<S>
    where S: Stream<Item = (u8, Bytes)>
{
    fn drop(&mut self) {
        debug!("drop Dispatch");
    }
}
<commit_msg>core API: Session.config()<commit_after>use bytes::Bytes;
use crypto::digest::Digest;
use crypto::sha1::Sha1;
use futures::sync::mpsc;
use futures::{Future, Stream, IntoFuture, Poll, Async};
use std::io;
use std::sync::{RwLock, Arc, Weak};
use tokio_core::reactor::{Handle, Remote};
use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering};

use apresolve::apresolve_or_fallback;
use authentication::Credentials;
use cache::Cache;
use component::Lazy;
use connection;
use config::SessionConfig;

use audio_key::AudioKeyManager;
use channel::ChannelManager;
use mercury::MercuryManager;

pub struct SessionData {
    country: String,
    canonical_username: String,
}

pub struct SessionInternal {
    config: SessionConfig,
    data: RwLock<SessionData>,

    tx_connection: mpsc::UnboundedSender<(u8, Vec<u8>)>,

    audio_key: Lazy<AudioKeyManager>,
    channel: Lazy<ChannelManager>,
    mercury: Lazy<MercuryManager>,
    cache: Option<Arc<Cache>>,

    handle: Remote,

    session_id: usize,
}

static SESSION_COUNTER : AtomicUsize = ATOMIC_USIZE_INIT;

#[derive(Clone)]
pub struct Session(pub Arc<SessionInternal>);

pub fn device_id(name: &str) -> String {
    let mut h = Sha1::new();
    h.input_str(name);
    h.result_str()
}

impl Session {
    pub fn connect(config: SessionConfig, credentials: Credentials,
                   cache: Option<Cache>, handle: Handle)
        -> Box<Future<Item=Session, Error=io::Error>>
    {
        let access_point = apresolve_or_fallback::<io::Error>(&handle);

        let handle_ = handle.clone();
        let connection = access_point.and_then(move |addr| {
            info!("Connecting to AP \"{}\"", addr);
            connection::connect::<&str>(&addr, &handle_)
        });

        let device_id = config.device_id.clone();
        let authentication = connection.and_then(move |connection| {
            connection::authenticate(connection, credentials, device_id)
        });

        let result = authentication.map(move |(transport, reusable_credentials)| {
            info!("Authenticated as \"{}\" !", reusable_credentials.username);
            if let Some(ref cache) = cache {
                cache.save_credentials(&reusable_credentials);
            }

            let (session, task) = Session::create(
                &handle, transport, config, cache, reusable_credentials.username.clone()
            );

            handle.spawn(task.map_err(|e| panic!(e)));

            session
        });

        Box::new(result)
    }

    fn create(handle: &Handle, transport: connection::Transport,
              config: SessionConfig, cache: Option<Cache>, username: String)
        -> (Session, Box<Future<Item = (), Error = io::Error>>)
    {
        let (sink, stream) = transport.split();

        let (sender_tx, sender_rx) = mpsc::unbounded();
        let session_id = SESSION_COUNTER.fetch_add(1, Ordering::Relaxed);

        debug!("new Session[{}]", session_id);

        let session = Session(Arc::new(SessionInternal {
            config: config,
            data: RwLock::new(SessionData {
                country: String::new(),
                canonical_username: username,
            }),

            tx_connection: sender_tx,

            cache: cache.map(Arc::new),

            audio_key: Lazy::new(),
            channel: Lazy::new(),
            mercury: Lazy::new(),

            handle: handle.remote().clone(),

            session_id: session_id,
        }));

        let sender_task = sender_rx
            .map_err(|e| -> io::Error { panic!(e) })
            .forward(sink).map(|_| ());
        let receiver_task = DispatchTask(stream, session.weak());

        let task = Box::new((receiver_task, sender_task).into_future()
            .map(|((), ())| ()));

        (session, task)
    }

    pub fn audio_key(&self) -> &AudioKeyManager {
        self.0.audio_key.get(|| AudioKeyManager::new(self.weak()))
    }

    pub fn channel(&self) -> &ChannelManager {
        self.0.channel.get(|| ChannelManager::new(self.weak()))
    }

    pub fn mercury(&self) -> &MercuryManager {
        self.0.mercury.get(|| MercuryManager::new(self.weak()))
    }

    pub fn spawn<F, R>(&self, f: F)
        where F: FnOnce(&Handle) -> R + Send + 'static,
              R: IntoFuture<Item=(), Error=()>,
              R::Future: 'static
    {
        self.0.handle.spawn(f)
    }

    fn debug_info(&self) {
        debug!("Session[{}] strong={} weak={}",
               self.0.session_id, Arc::strong_count(&self.0), Arc::weak_count(&self.0));
    }

    #[cfg_attr(feature = "cargo-clippy", allow(match_same_arms))]
    fn dispatch(&self, cmd: u8, data: Bytes) {
        match cmd {
            0x4 => {
                self.debug_info();
                self.send_packet(0x49, data.as_ref().to_owned());
            },
            0x4a => (),
            0x1b => {
                let country = String::from_utf8(data.as_ref().to_owned()).unwrap();
                info!("Country: {:?}", country);
                self.0.data.write().unwrap().country = country;
            }

            0x9 | 0xa => self.channel().dispatch(cmd, data),
            0xd | 0xe => self.audio_key().dispatch(cmd, data),
            0xb2...0xb6 => self.mercury().dispatch(cmd, data),
            _ => (),
        }
    }

    pub fn send_packet(&self, cmd: u8, data: Vec<u8>) {
        self.0.tx_connection.unbounded_send((cmd, data)).unwrap();
    }

    pub fn cache(&self) -> Option<&Arc<Cache>> {
        self.0.cache.as_ref()
    }

    fn config(&self) -> &SessionConfig {
        &self.0.config
    }

    pub fn username(&self) -> String {
        self.0.data.read().unwrap().canonical_username.clone()
    }

    pub fn country(&self) -> String {
        self.0.data.read().unwrap().country.clone()
    }

    pub fn device_id(&self) -> &str {
        &self.config().device_id
    }

    pub fn weak(&self) -> SessionWeak {
        SessionWeak(Arc::downgrade(&self.0))
    }

    pub fn session_id(&self) -> usize {
        self.0.session_id
    }
}

#[derive(Clone)]
pub struct SessionWeak(pub Weak<SessionInternal>);

impl SessionWeak {
    pub fn try_upgrade(&self) -> Option<Session> {
        self.0.upgrade().map(Session)
    }

    pub fn upgrade(&self) -> Session {
        self.try_upgrade().expect("Session died")
    }
}

impl Drop for SessionInternal {
    fn drop(&mut self) {
        debug!("drop Session[{}]", self.session_id);
    }
}

struct DispatchTask<S>(S, SessionWeak)
    where S: Stream<Item = (u8, Bytes)>;

impl <S> Future for DispatchTask<S>
    where S: Stream<Item = (u8, Bytes)>
{
    type Item = ();
    type Error = S::Error;

    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        let session = match self.1.try_upgrade() {
            Some(session) => session,
            None => {
                return Ok(Async::Ready(()))
            },
        };

        loop {
            let (cmd, data) = try_ready!(self.0.poll()).expect("connection closed");
            session.dispatch(cmd, data);
        }
    }
}

impl <S> Drop for DispatchTask<S>
    where S: Stream<Item = (u8, Bytes)>
{
    fn drop(&mut self) {
        debug!("drop Dispatch");
    }
}
<|endoftext|>


<commit_before>use std::cell::{Cell, RefCell};
use std::ptr;

use cairo::{self, ImageSurface, MatrixTrait, PatternTrait};

use allowed_url::AllowedUrl;
use aspect_ratio::AspectRatio;
use attributes::Attribute;
use defs::{Fragment, Href};
use drawing_ctx::DrawingCtx;
use error::RenderingError;
use handle::{self, RsvgHandle};
use node::{CascadedValues, NodeResult, NodeTrait, RsvgNode};
use parsers::parse;
use property_bag::PropertyBag;
use surface_utils::shared_surface::{SharedImageSurface, SurfaceType};

use super::bounds::BoundsBuilder;
use super::context::{FilterContext, FilterOutput, FilterResult, IRect};
use super::{Filter, FilterError, Primitive};

/// The `feImage` filter primitive.
pub struct Image {
    base: Primitive,
    aspect: Cell<AspectRatio>,
    href: RefCell<Option<String>>,

    // Storing this here seems hack-ish... It's required by rsvg_cairo_surface_new_from_href(). The
    // <image> element calls it in set_atts() but I don't think it belongs there.
    handle: Cell<*const RsvgHandle>,
}

impl Image {
    /// Constructs a new `Image` with empty properties.
    #[inline]
    pub fn new() -> Image {
        Image {
            base: Primitive::new::<Self>(),
            aspect: Cell::new(AspectRatio::default()),
            href: RefCell::new(None),

            handle: Cell::new(ptr::null()),
        }
    }

    /// Renders the filter if the source is an existing node.
    fn render_node(
        &self,
        ctx: &FilterContext,
        draw_ctx: &mut DrawingCtx<'_>,
        bounds: IRect,
        fragment: &Fragment,
    ) -> Result<ImageSurface, FilterError> {
        let acquired_drawable = draw_ctx
            .get_acquired_node(fragment)
            .ok_or(FilterError::InvalidInput)?;
        let drawable = acquired_drawable.get();

        let surface = ImageSurface::create(
            cairo::Format::ARgb32,
            ctx.source_graphic().width(),
            ctx.source_graphic().height(),
        )?;

        draw_ctx.get_cairo_context().set_matrix(ctx.paffine());

        let node_being_filtered_values = ctx.get_computed_values_from_node_being_filtered();

        let cascaded = CascadedValues::new_from_values(&drawable, node_being_filtered_values);

        draw_ctx
            .draw_node_on_surface(
                &drawable,
                &cascaded,
                &surface,
                f64::from(ctx.source_graphic().width()),
                f64::from(ctx.source_graphic().height()),
            )
            .map_err(|e| {
                if let RenderingError::Cairo(status) = e {
                    FilterError::CairoError(status)
                } else {
                    // FIXME: this is just a dummy value; we should probably have a way to indicate
                    // an error in the underlying drawing process.
                    FilterError::CairoError(cairo::Status::InvalidStatus)
                }
            })?;

        // Clip the output to bounds.
        let output_surface = ImageSurface::create(
            cairo::Format::ARgb32,
            ctx.source_graphic().width(),
            ctx.source_graphic().height(),
        )?;

        let cr = cairo::Context::new(&output_surface);
        cr.rectangle(
            f64::from(bounds.x0),
            f64::from(bounds.y0),
            f64::from(bounds.x1 - bounds.x0),
            f64::from(bounds.y1 - bounds.y0),
        );
        cr.clip();
        cr.set_source_surface(&surface, 0f64, 0f64);
        cr.paint();

        Ok(output_surface)
    }

    /// Renders the filter if the source is an external image.
    fn render_external_image(
        &self,
        ctx: &FilterContext,
        draw_ctx: &mut DrawingCtx<'_>,
        bounds_builder: BoundsBuilder<'_>,
        href: &Href,
    ) -> Result<ImageSurface, FilterError> {
        let url = if let Href::PlainUri(ref url) = *href {
            url
        } else {
            unreachable!();
        };

        let aurl = AllowedUrl::from_href(url, handle::get_base_url(self.handle.get()).as_ref())
            .map_err(|_| FilterError::InvalidInput)?;

        // FIXME: translate the error better here
        let surface = handle::load_image_to_surface(self.handle.get() as *mut _, &aurl)
            .map_err(|_| FilterError::InvalidInput)?;

        let output_surface = ImageSurface::create(
            cairo::Format::ARgb32,
            ctx.source_graphic().width(),
            ctx.source_graphic().height(),
        )?;

        // TODO: this goes through a f64->i32->f64 conversion.
        let render_bounds = bounds_builder.into_irect_without_clipping(draw_ctx);
        let aspect = self.aspect.get();
        let (x, y, w, h) = aspect.compute(
            f64::from(surface.get_width()),
            f64::from(surface.get_height()),
            f64::from(render_bounds.x0),
            f64::from(render_bounds.y0),
            f64::from(render_bounds.x1 - render_bounds.x0),
            f64::from(render_bounds.y1 - render_bounds.y0),
        );

        if w != 0f64 && h != 0f64 {
            let ptn = cairo::SurfacePattern::create(&surface);
            let mut matrix = cairo::Matrix::new(
                w / f64::from(surface.get_width()),
                0f64,
                0f64,
                h / f64::from(surface.get_height()),
                x,
                y,
            );
            matrix.invert();
            ptn.set_matrix(matrix);

            let bounds = bounds_builder.into_irect(draw_ctx);
            let cr = cairo::Context::new(&output_surface);
            cr.rectangle(
                f64::from(bounds.x0),
                f64::from(bounds.y0),
                f64::from(bounds.x1 - bounds.x0),
                f64::from(bounds.y1 - bounds.y0),
            );
            cr.clip();
            cr.set_source(&cairo::Pattern::SurfacePattern(ptn));
            cr.paint();
        }

        Ok(output_surface)
    }
}

impl NodeTrait for Image {
    fn set_atts(
        &self,
        node: &RsvgNode,
        handle: *const RsvgHandle,
        pbag: &PropertyBag<'_>,
    ) -> NodeResult {
        self.base.set_atts(node, handle, pbag)?;

        for (_key, attr, value) in pbag.iter() {
            match attr {
                Attribute::PreserveAspectRatio => {
                    self.aspect.set(parse("preserveAspectRatio", value, ())?)
                }

                // "path" is used by some older Adobe Illustrator versions
                Attribute::XlinkHref | Attribute::Path => {
                    drop(self.href.replace(Some(value.to_string())))
                }
                _ => (),
            }
        }

        self.handle.set(handle);

        Ok(())
    }
}

impl Filter for Image {
    fn render(
        &self,
        _node: &RsvgNode,
        ctx: &FilterContext,
        draw_ctx: &mut DrawingCtx<'_>,
    ) -> Result<FilterResult, FilterError> {
        let href_str = self.href.borrow();
        let href_str = href_str.as_ref().ok_or(FilterError::InvalidInput)?;

        let bounds_builder = self.base.get_bounds(ctx);
        let bounds = bounds_builder.into_irect(draw_ctx);

        let href = Href::parse(href_str).map_err(|_| FilterError::InvalidInput)?;

        let output_surface = match href {
            Href::PlainUri(_) => {
                self.render_external_image(ctx, draw_ctx, bounds_builder, &href)?
            }
            Href::WithFragment(frag) => self.render_node(ctx, draw_ctx, bounds, &frag)?,
        };

        Ok(FilterResult {
            name: self.base.result.borrow().clone(),
            output: FilterOutput {
                surface: SharedImageSurface::new(output_surface, SurfaceType::SRgb)?,
                bounds,
            },
        })
    }

    #[inline]
    fn is_affected_by_color_interpolation_filters(&self) -> bool {
        false
    }
}
<commit_msg>filters/image.rs: Parse the href at loading time, in set_atts()<commit_after>use std::cell::{Cell, RefCell};
use std::ptr;

use cairo::{self, ImageSurface, MatrixTrait, PatternTrait};

use allowed_url::AllowedUrl;
use aspect_ratio::AspectRatio;
use attributes::Attribute;
use defs::{Fragment, Href};
use drawing_ctx::DrawingCtx;
use error::{NodeError, RenderingError};
use handle::{self, RsvgHandle};
use node::{CascadedValues, NodeResult, NodeTrait, RsvgNode};
use parsers::{parse, ParseError};
use property_bag::PropertyBag;
use surface_utils::shared_surface::{SharedImageSurface, SurfaceType};

use super::bounds::BoundsBuilder;
use super::context::{FilterContext, FilterOutput, FilterResult, IRect};
use super::{Filter, FilterError, Primitive};

/// The `feImage` filter primitive.
pub struct Image {
    base: Primitive,
    aspect: Cell<AspectRatio>,
    href: RefCell<Option<Href>>,

    // Storing this here seems hack-ish... It's required by rsvg_cairo_surface_new_from_href(). The
    // <image> element calls it in set_atts() but I don't think it belongs there.
    handle: Cell<*const RsvgHandle>,
}

impl Image {
    /// Constructs a new `Image` with empty properties.
    #[inline]
    pub fn new() -> Image {
        Image {
            base: Primitive::new::<Self>(),
            aspect: Cell::new(AspectRatio::default()),
            href: RefCell::new(None),

            handle: Cell::new(ptr::null()),
        }
    }

    fn set_href(&self, attr: Attribute, href_str: &str) -> NodeResult {
        let href = Href::parse(href_str)
            .map_err(|_| NodeError::parse_error(attr, ParseError::new("could not parse href")))?;

        *self.href.borrow_mut() = Some(href);

        Ok(())
    }

    /// Renders the filter if the source is an existing node.
    fn render_node(
        &self,
        ctx: &FilterContext,
        draw_ctx: &mut DrawingCtx<'_>,
        bounds: IRect,
        fragment: &Fragment,
    ) -> Result<ImageSurface, FilterError> {
        let acquired_drawable = draw_ctx
            .get_acquired_node(fragment)
            .ok_or(FilterError::InvalidInput)?;
        let drawable = acquired_drawable.get();

        let surface = ImageSurface::create(
            cairo::Format::ARgb32,
            ctx.source_graphic().width(),
            ctx.source_graphic().height(),
        )?;

        draw_ctx.get_cairo_context().set_matrix(ctx.paffine());

        let node_being_filtered_values = ctx.get_computed_values_from_node_being_filtered();

        let cascaded = CascadedValues::new_from_values(&drawable, node_being_filtered_values);

        draw_ctx
            .draw_node_on_surface(
                &drawable,
                &cascaded,
                &surface,
                f64::from(ctx.source_graphic().width()),
                f64::from(ctx.source_graphic().height()),
            )
            .map_err(|e| {
                if let RenderingError::Cairo(status) = e {
                    FilterError::CairoError(status)
                } else {
                    // FIXME: this is just a dummy value; we should probably have a way to indicate
                    // an error in the underlying drawing process.
                    FilterError::CairoError(cairo::Status::InvalidStatus)
                }
            })?;

        // Clip the output to bounds.
        let output_surface = ImageSurface::create(
            cairo::Format::ARgb32,
            ctx.source_graphic().width(),
            ctx.source_graphic().height(),
        )?;

        let cr = cairo::Context::new(&output_surface);
        cr.rectangle(
            f64::from(bounds.x0),
            f64::from(bounds.y0),
            f64::from(bounds.x1 - bounds.x0),
            f64::from(bounds.y1 - bounds.y0),
        );
        cr.clip();
        cr.set_source_surface(&surface, 0f64, 0f64);
        cr.paint();

        Ok(output_surface)
    }

    /// Renders the filter if the source is an external image.
    fn render_external_image(
        &self,
        ctx: &FilterContext,
        draw_ctx: &mut DrawingCtx<'_>,
        bounds_builder: BoundsBuilder<'_>,
        href: &Href,
    ) -> Result<ImageSurface, FilterError> {
        let url = if let Href::PlainUri(ref url) = *href {
            url
        } else {
            unreachable!();
        };

        let aurl = AllowedUrl::from_href(url, handle::get_base_url(self.handle.get()).as_ref())
            .map_err(|_| FilterError::InvalidInput)?;

        // FIXME: translate the error better here
        let surface = handle::load_image_to_surface(self.handle.get() as *mut _, &aurl)
            .map_err(|_| FilterError::InvalidInput)?;

        let output_surface = ImageSurface::create(
            cairo::Format::ARgb32,
            ctx.source_graphic().width(),
            ctx.source_graphic().height(),
        )?;

        // TODO: this goes through a f64->i32->f64 conversion.
        let render_bounds = bounds_builder.into_irect_without_clipping(draw_ctx);
        let aspect = self.aspect.get();
        let (x, y, w, h) = aspect.compute(
            f64::from(surface.get_width()),
            f64::from(surface.get_height()),
            f64::from(render_bounds.x0),
            f64::from(render_bounds.y0),
            f64::from(render_bounds.x1 - render_bounds.x0),
            f64::from(render_bounds.y1 - render_bounds.y0),
        );

        if w != 0f64 && h != 0f64 {
            let ptn = cairo::SurfacePattern::create(&surface);
            let mut matrix = cairo::Matrix::new(
                w / f64::from(surface.get_width()),
                0f64,
                0f64,
                h / f64::from(surface.get_height()),
                x,
                y,
            );
            matrix.invert();
            ptn.set_matrix(matrix);

            let bounds = bounds_builder.into_irect(draw_ctx);
            let cr = cairo::Context::new(&output_surface);
            cr.rectangle(
                f64::from(bounds.x0),
                f64::from(bounds.y0),
                f64::from(bounds.x1 - bounds.x0),
                f64::from(bounds.y1 - bounds.y0),
            );
            cr.clip();
            cr.set_source(&cairo::Pattern::SurfacePattern(ptn));
            cr.paint();
        }

        Ok(output_surface)
    }
}

impl NodeTrait for Image {
    fn set_atts(
        &self,
        node: &RsvgNode,
        handle: *const RsvgHandle,
        pbag: &PropertyBag<'_>,
    ) -> NodeResult {
        self.base.set_atts(node, handle, pbag)?;

        for (_key, attr, value) in pbag.iter() {
            match attr {
                Attribute::PreserveAspectRatio => {
                    self.aspect.set(parse("preserveAspectRatio", value, ())?)
                }

                // "path" is used by some older Adobe Illustrator versions
                Attribute::XlinkHref | Attribute::Path => self.set_href(attr, value)?,

                _ => (),
            }
        }

        self.handle.set(handle);

        Ok(())
    }
}

impl Filter for Image {
    fn render(
        &self,
        _node: &RsvgNode,
        ctx: &FilterContext,
        draw_ctx: &mut DrawingCtx<'_>,
    ) -> Result<FilterResult, FilterError> {
        let bounds_builder = self.base.get_bounds(ctx);
        let bounds = bounds_builder.into_irect(draw_ctx);

        let href_borrow = self.href.borrow();
        let href_opt = href_borrow.as_ref();

        if let Some(href) = href_opt {
            let output_surface = match *href {
                Href::PlainUri(_) => {
                    self.render_external_image(ctx, draw_ctx, bounds_builder, href)?
                }
                Href::WithFragment(ref frag) => self.render_node(ctx, draw_ctx, bounds, frag)?,
            };

            Ok(FilterResult {
                name: self.base.result.borrow().clone(),
                output: FilterOutput {
                    surface: SharedImageSurface::new(output_surface, SurfaceType::SRgb)?,
                    bounds,
                },
            })
        } else {
            Err(FilterError::InvalidInput)
        }
    }

    #[inline]
    fn is_affected_by_color_interpolation_filters(&self) -> bool {
        false
    }
}
<|endoftext|>


<commit_before>use derivative::Derivative;
use eyre::Result;
use path_slash::PathExt;
use schemars::JsonSchema;
use serde::Serialize;
use serde_with::skip_serializing_none;
use std::path::{Path, PathBuf};

/// A resource in a dependency graph (the nodes of the graph)
#[derive(Debug, Clone, PartialEq, Eq, Hash, JsonSchema, Serialize)]
#[serde(tag = "type")]
pub enum Resource {
    /// A symbol within code, within a document
    Symbol(Symbol),

    /// A node containing code, or associated with code, within a document
    Code(Code),

    /// A node within a document
    Node(Node),

    /// A file within the project
    File(File),

    /// A declared project `Source`
    Source(Source),

    /// A programming language module, usually part of an external package
    Module(Module),

    /// A URL to a remote resource
    Url(Url),
}

/// The id of a resource
pub type ResourceId = String;

impl Resource {
    /// Get the resource id
    pub fn id(&self) -> String {
        match self {
            Resource::Symbol(Symbol { path, name, .. }) => {
                ["symbol://", &path.display().to_string(), "#", name].concat()
            }
            Resource::Code(Code { path, id, .. }) => {
                ["code://", &path.display().to_string(), "#", id].concat()
            }
            Resource::Node(Node { path, id, .. }) => {
                ["node://", &path.display().to_string(), "#", id].concat()
            }
            Resource::File(File { path, .. }) => ["file://", &path.display().to_string()].concat(),
            Resource::Source(Source { name, .. }) => ["source://", name].concat(),
            Resource::Module(Module { language, name, .. }) => {
                ["module://", language, "#", name].concat()
            }
            Resource::Url(Url { url }) => url.clone(),
        }
    }
}

/// A summary of the dependencies of a resource in a dependency graph
#[derive(Debug, Clone, Serialize)]
pub struct ResourceDependencies {
    /// The id of the resource
    pub id: String,

    /// The ids of any dependencies in the dependency graph
    pub dependencies: Vec<String>,

    /// The depth of the resource in the dependency graph.
    ///
    /// A resource that has no dependencies has a depth of zero.
    /// Otherwise the depth is the maximum depth of dependencies plus one.
    pub depth: usize,
}

#[derive(Debug, Clone, Derivative, JsonSchema, Serialize)]
#[derivative(PartialEq, Eq, Hash)]
#[schemars(deny_unknown_fields)]
pub struct Symbol {
    /// The path of the file that the symbol is defined in
    #[serde(serialize_with = "serialize_path")]
    pub path: PathBuf,

    /// The name/identifier of the symbol
    pub name: String,

    /// The type of the object that the symbol refers to (e.g `Number`, `Function`)
    ///
    /// Should be used as a hint only, and as such is excluded from
    /// equality and hash functions.
    #[derivative(PartialEq = "ignore")]
    #[derivative(Hash = "ignore")]
    pub kind: String,
}

/// Create a new `Symbol` resource
pub fn symbol(path: &Path, name: &str, kind: &str) -> Resource {
    Resource::Symbol(Symbol {
        path: path.to_path_buf(),
        name: name.into(),
        kind: kind.into(),
    })
}

#[derive(Debug, Clone, PartialEq, Eq, Hash, JsonSchema, Serialize)]
#[schemars(deny_unknown_fields)]
pub struct Node {
    /// The path of the file that the node is defined in
    #[serde(serialize_with = "serialize_path")]
    pub path: PathBuf,

    /// The id of the node with the document
    pub id: String,

    /// The type of node e.g. `Link`, `ImageObject`
    pub kind: String,
}

/// Create a new `Node` resource
pub fn node(path: &Path, id: &str, kind: &str) -> Resource {
    Resource::Node(Node {
        path: path.to_path_buf(),
        id: id.into(),
        kind: kind.into(),
    })
}

#[skip_serializing_none]
#[derive(Debug, Clone, PartialEq, Eq, Hash, JsonSchema, Serialize)]
#[schemars(deny_unknown_fields)]
pub struct Code {
    /// The path of the file that the node is defined in
    #[serde(serialize_with = "serialize_path")]
    pub path: PathBuf,

    /// The id of the node with the document
    pub id: String,

    /// The type of node e.g. `Parameter`, `CodeChunk`
    pub kind: String,

    /// The programming language associated with the node (if any)
    ///
    /// Used when planning which kernel to execute the node in
    pub language: Option<String>,

    /// Whether the code has side effects
    ///
    /// Inferred from the relations of the resource (ie. whether it
    /// has `Assign`, `Alter`, `Import` etc relations) or from an explicit
    /// `@pure` or `@impure` tag.
    pub pure: bool,
}

/// Create a new `Code` resource
pub fn code(path: &Path, id: &str, kind: &str, language: Option<String>, pure: bool) -> Resource {
    Resource::Code(Code {
        path: path.to_path_buf(),
        id: id.into(),
        kind: kind.into(),
        language,
        pure,
    })
}

#[derive(Debug, Clone, PartialEq, Eq, Hash, JsonSchema, Serialize)]
#[schemars(deny_unknown_fields)]
pub struct File {
    /// The path of the file
    #[serde(serialize_with = "serialize_path")]
    pub path: PathBuf,
}

/// Create a new `File` resource
pub fn file(path: &Path) -> Resource {
    Resource::File(File {
        path: path.to_path_buf(),
    })
}

#[derive(Debug, Clone, PartialEq, Eq, Hash, JsonSchema, Serialize)]
#[schemars(deny_unknown_fields)]
pub struct Source {
    /// The name of the project source
    pub name: String,
}

/// Create a new `Source` resource
pub fn source(name: &str) -> Resource {
    Resource::Source(Source { name: name.into() })
}

#[derive(Debug, Clone, PartialEq, Eq, Hash, JsonSchema, Serialize)]
#[schemars(deny_unknown_fields)]
pub struct Module {
    /// The programming language of the module
    pub language: String,

    /// The name of the module
    pub name: String,
}

/// Create a new `Module` resource
pub fn module(language: &str, name: &str) -> Resource {
    Resource::Module(Module {
        language: language.into(),
        name: name.into(),
    })
}

#[derive(Debug, Clone, Default, PartialEq, Eq, Hash, JsonSchema, Serialize)]
#[schemars(deny_unknown_fields)]
pub struct Url {
    /// The URL of the external resource
    pub url: String,
}

/// Create a new `Url` resource
pub fn url(url: &str) -> Resource {
    Resource::Url(Url { url: url.into() })
}

/// Serialize the `path` fields of resources so that they use Unix forward slash
/// separators on all platforms.
fn serialize_path<S>(path: &Path, serializer: S) -> Result<S::Ok, S::Error>
where
    S: serde::Serializer,
{
    path.to_slash_lossy().serialize(serializer)
}
<commit_msg>fix(Graph resources): Use forward slashes for `File` resources for consistency across platforms<commit_after>use derivative::Derivative;
use eyre::Result;
use path_slash::PathExt;
use schemars::JsonSchema;
use serde::Serialize;
use serde_with::skip_serializing_none;
use std::path::{Path, PathBuf};

/// A resource in a dependency graph (the nodes of the graph)
#[derive(Debug, Clone, PartialEq, Eq, Hash, JsonSchema, Serialize)]
#[serde(tag = "type")]
pub enum Resource {
    /// A symbol within code, within a document
    Symbol(Symbol),

    /// A node containing code, or associated with code, within a document
    Code(Code),

    /// A node within a document
    Node(Node),

    /// A file within the project
    File(File),

    /// A declared project `Source`
    Source(Source),

    /// A programming language module, usually part of an external package
    Module(Module),

    /// A URL to a remote resource
    Url(Url),
}

/// The id of a resource
pub type ResourceId = String;

impl Resource {
    /// Get the resource id
    pub fn id(&self) -> String {
        match self {
            Resource::Symbol(Symbol { path, name, .. }) => {
                ["symbol://", &path.display().to_string(), "#", name].concat()
            }
            Resource::Code(Code { path, id, .. }) => {
                ["code://", &path.display().to_string(), "#", id].concat()
            }
            Resource::Node(Node { path, id, .. }) => {
                ["node://", &path.display().to_string(), "#", id].concat()
            }
            Resource::File(File { path, .. }) => ["file://", &path.to_slash_lossy()].concat(),
            Resource::Source(Source { name, .. }) => ["source://", name].concat(),
            Resource::Module(Module { language, name, .. }) => {
                ["module://", language, "#", name].concat()
            }
            Resource::Url(Url { url }) => url.clone(),
        }
    }
}

/// A summary of the dependencies of a resource in a dependency graph
#[derive(Debug, Clone, Serialize)]
pub struct ResourceDependencies {
    /// The id of the resource
    pub id: String,

    /// The ids of any dependencies in the dependency graph
    pub dependencies: Vec<String>,

    /// The depth of the resource in the dependency graph.
    ///
    /// A resource that has no dependencies has a depth of zero.
    /// Otherwise the depth is the maximum depth of dependencies plus one.
    pub depth: usize,
}

#[derive(Debug, Clone, Derivative, JsonSchema, Serialize)]
#[derivative(PartialEq, Eq, Hash)]
#[schemars(deny_unknown_fields)]
pub struct Symbol {
    /// The path of the file that the symbol is defined in
    #[serde(serialize_with = "serialize_path")]
    pub path: PathBuf,

    /// The name/identifier of the symbol
    pub name: String,

    /// The type of the object that the symbol refers to (e.g `Number`, `Function`)
    ///
    /// Should be used as a hint only, and as such is excluded from
    /// equality and hash functions.
    #[derivative(PartialEq = "ignore")]
    #[derivative(Hash = "ignore")]
    pub kind: String,
}

/// Create a new `Symbol` resource
pub fn symbol(path: &Path, name: &str, kind: &str) -> Resource {
    Resource::Symbol(Symbol {
        path: path.to_path_buf(),
        name: name.into(),
        kind: kind.into(),
    })
}

#[derive(Debug, Clone, PartialEq, Eq, Hash, JsonSchema, Serialize)]
#[schemars(deny_unknown_fields)]
pub struct Node {
    /// The path of the file that the node is defined in
    #[serde(serialize_with = "serialize_path")]
    pub path: PathBuf,

    /// The id of the node with the document
    pub id: String,

    /// The type of node e.g. `Link`, `ImageObject`
    pub kind: String,
}

/// Create a new `Node` resource
pub fn node(path: &Path, id: &str, kind: &str) -> Resource {
    Resource::Node(Node {
        path: path.to_path_buf(),
        id: id.into(),
        kind: kind.into(),
    })
}

#[skip_serializing_none]
#[derive(Debug, Clone, PartialEq, Eq, Hash, JsonSchema, Serialize)]
#[schemars(deny_unknown_fields)]
pub struct Code {
    /// The path of the file that the node is defined in
    #[serde(serialize_with = "serialize_path")]
    pub path: PathBuf,

    /// The id of the node with the document
    pub id: String,

    /// The type of node e.g. `Parameter`, `CodeChunk`
    pub kind: String,

    /// The programming language associated with the node (if any)
    ///
    /// Used when planning which kernel to execute the node in
    pub language: Option<String>,

    /// Whether the code has side effects
    ///
    /// Inferred from the relations of the resource (ie. whether it
    /// has `Assign`, `Alter`, `Import` etc relations) or from an explicit
    /// `@pure` or `@impure` tag.
    pub pure: bool,
}

/// Create a new `Code` resource
pub fn code(path: &Path, id: &str, kind: &str, language: Option<String>, pure: bool) -> Resource {
    Resource::Code(Code {
        path: path.to_path_buf(),
        id: id.into(),
        kind: kind.into(),
        language,
        pure,
    })
}

#[derive(Debug, Clone, PartialEq, Eq, Hash, JsonSchema, Serialize)]
#[schemars(deny_unknown_fields)]
pub struct File {
    /// The path of the file
    #[serde(serialize_with = "serialize_path")]
    pub path: PathBuf,
}

/// Create a new `File` resource
pub fn file(path: &Path) -> Resource {
    Resource::File(File {
        path: path.to_path_buf(),
    })
}

#[derive(Debug, Clone, PartialEq, Eq, Hash, JsonSchema, Serialize)]
#[schemars(deny_unknown_fields)]
pub struct Source {
    /// The name of the project source
    pub name: String,
}

/// Create a new `Source` resource
pub fn source(name: &str) -> Resource {
    Resource::Source(Source { name: name.into() })
}

#[derive(Debug, Clone, PartialEq, Eq, Hash, JsonSchema, Serialize)]
#[schemars(deny_unknown_fields)]
pub struct Module {
    /// The programming language of the module
    pub language: String,

    /// The name of the module
    pub name: String,
}

/// Create a new `Module` resource
pub fn module(language: &str, name: &str) -> Resource {
    Resource::Module(Module {
        language: language.into(),
        name: name.into(),
    })
}

#[derive(Debug, Clone, Default, PartialEq, Eq, Hash, JsonSchema, Serialize)]
#[schemars(deny_unknown_fields)]
pub struct Url {
    /// The URL of the external resource
    pub url: String,
}

/// Create a new `Url` resource
pub fn url(url: &str) -> Resource {
    Resource::Url(Url { url: url.into() })
}

/// Serialize the `path` fields of resources so that they use Unix forward slash
/// separators on all platforms.
fn serialize_path<S>(path: &Path, serializer: S) -> Result<S::Ok, S::Error>
where
    S: serde::Serializer,
{
    path.to_slash_lossy().serialize(serializer)
}
<|endoftext|>


<commit_before>//xfail-stage0
//xfail-stage1
//xfail-stage2
//xfail-stage3
fn int_id(int x) -> int {
  ret x;
}

fn main() {
  while(true) {
      int_id(break);
  }
}<commit_msg>Un-XFAIL break-value<commit_after>// xfail-stage0
fn int_id(int x) -> int {
  ret x;
}

fn main() {
  while(true) {
      int_id(break);
  }
}<|endoftext|>


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

#![cfg(target_thread_local)]
#![unstable(feature = "thread_local_internals", issue = "0")]

// Since what appears to be glibc 2.18 this symbol has been shipped which
// GCC and clang both use to invoke destructors in thread_local globals, so
// let's do the same!
//
// Note, however, that we run on lots older linuxes, as well as cross
// compiling from a newer linux to an older linux, so we also have a
// fallback implementation to use as well.
//
// Due to rust-lang/rust#18804, make sure this is not generic!
#[cfg(target_os = "linux")]
pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern fn(*mut u8)) {
    use libc;
    use mem;
    use sys_common::thread_local::register_dtor_fallback;

    extern {
        #[linkage = "extern_weak"]
        static __dso_handle: *mut u8;
        #[linkage = "extern_weak"]
        static __cxa_thread_atexit_impl: *const libc::c_void;
    }
    if !__cxa_thread_atexit_impl.is_null() {
        type F = unsafe extern fn(dtor: unsafe extern fn(*mut u8),
                                  arg: *mut u8,
                                  dso_handle: *mut u8) -> libc::c_int;
        mem::transmute::<*const libc::c_void, F>(__cxa_thread_atexit_impl)
            (dtor, t, &__dso_handle as *const _ as *mut _);
        return
    }
    register_dtor_fallback(t, dtor);
}

// macOS's analog of the above linux function is this _tlv_atexit function.
// The disassembly of thread_local globals in C++ (at least produced by
// clang) will have this show up in the output.
#[cfg(target_os = "macos")]
pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern fn(*mut u8)) {
    extern {
        fn _tlv_atexit(dtor: unsafe extern fn(*mut u8),
                       arg: *mut u8);
    }
    _tlv_atexit(dtor, t);
}

// Just use the thread_local fallback implementation, at least until there's
// a more direct implementation.
#[cfg(target_os = "fuchsia")]
pub use sys_common::thread_local::register_dtor_fallback as register_dtor;

pub fn requires_move_before_drop() -> bool {
    // The macOS implementation of TLS apparently had an odd aspect to it
    // where the pointer we have may be overwritten while this destructor
    // is running. Specifically if a TLS destructor re-accesses TLS it may
    // trigger a re-initialization of all TLS variables, paving over at
    // least some destroyed ones with initial values.
    //
    // This means that if we drop a TLS value in place on macOS that we could
    // revert the value to its original state halfway through the
    // destructor, which would be bad!
    //
    // Hence, we use `ptr::read` on macOS (to move to a "safe" location)
    // instead of drop_in_place.
    cfg!(target_os = "macos")
}
<commit_msg>Auto merge of #52281 - cramertj:fast-tls, r=alexcrichton<commit_after>// Copyright 2014-2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![cfg(target_thread_local)]
#![unstable(feature = "thread_local_internals", issue = "0")]

// Since what appears to be glibc 2.18 this symbol has been shipped which
// GCC and clang both use to invoke destructors in thread_local globals, so
// let's do the same!
//
// Note, however, that we run on lots older linuxes, as well as cross
// compiling from a newer linux to an older linux, so we also have a
// fallback implementation to use as well.
//
// Due to rust-lang/rust#18804, make sure this is not generic!
#[cfg(any(target_os = "linux", target_os = "fuchsia"))]
pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern fn(*mut u8)) {
    use libc;
    use mem;
    use sys_common::thread_local::register_dtor_fallback;

    extern {
        #[linkage = "extern_weak"]
        static __dso_handle: *mut u8;
        #[linkage = "extern_weak"]
        static __cxa_thread_atexit_impl: *const libc::c_void;
    }
    if !__cxa_thread_atexit_impl.is_null() {
        type F = unsafe extern fn(dtor: unsafe extern fn(*mut u8),
                                  arg: *mut u8,
                                  dso_handle: *mut u8) -> libc::c_int;
        mem::transmute::<*const libc::c_void, F>(__cxa_thread_atexit_impl)
            (dtor, t, &__dso_handle as *const _ as *mut _);
        return
    }
    register_dtor_fallback(t, dtor);
}

// macOS's analog of the above linux function is this _tlv_atexit function.
// The disassembly of thread_local globals in C++ (at least produced by
// clang) will have this show up in the output.
#[cfg(target_os = "macos")]
pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern fn(*mut u8)) {
    extern {
        fn _tlv_atexit(dtor: unsafe extern fn(*mut u8),
                       arg: *mut u8);
    }
    _tlv_atexit(dtor, t);
}

pub fn requires_move_before_drop() -> bool {
    // The macOS implementation of TLS apparently had an odd aspect to it
    // where the pointer we have may be overwritten while this destructor
    // is running. Specifically if a TLS destructor re-accesses TLS it may
    // trigger a re-initialization of all TLS variables, paving over at
    // least some destroyed ones with initial values.
    //
    // This means that if we drop a TLS value in place on macOS that we could
    // revert the value to its original state halfway through the
    // destructor, which would be bad!
    //
    // Hence, we use `ptr::read` on macOS (to move to a "safe" location)
    // instead of drop_in_place.
    cfg!(target_os = "macos")
}
<|endoftext|>


<commit_before>use types::*;

use nom::{be_i8, be_u8, be_i16, be_u16, be_i32, be_u32, be_i64, be_u64, be_f32, be_f64};

/* FIXME: we convert to a Some so that nom can happily handle some _ case which would otherwise fail as we're exhaustive */
named!(pub parse_value<AMQPValue>,                 switch!(map!(parse_type, |t| Some(t)),
    Some(AMQPType::Boolean)        => map!(call!(parse_boolean),          |b| AMQPValue::Boolean(b))        |
    Some(AMQPType::ShortShortInt)  => map!(call!(parse_short_short_int),  |i| AMQPValue::ShortShortInt(i))  |
    Some(AMQPType::ShortShortUInt) => map!(call!(parse_short_short_uint), |u| AMQPValue::ShortShortUInt(u)) |
    Some(AMQPType::ShortInt)       => map!(call!(parse_short_int),        |i| AMQPValue::ShortInt(i))       |
    Some(AMQPType::ShortUInt)      => map!(call!(parse_short_uint),       |u| AMQPValue::ShortUInt(u))      |
    Some(AMQPType::LongInt)        => map!(call!(parse_long_int),         |i| AMQPValue::LongInt(i))        |
    Some(AMQPType::LongUInt)       => map!(call!(parse_long_uint),        |u| AMQPValue::LongUInt(u))       |
    Some(AMQPType::LongLongInt)    => map!(call!(parse_long_long_int),    |i| AMQPValue::LongLongInt(i))    |
    Some(AMQPType::LongLongUInt)   => map!(call!(parse_long_long_uint),   |u| AMQPValue::LongLongUInt(u))   |
    Some(AMQPType::Float)          => map!(call!(parse_float),            |f| AMQPValue::Float(f))          |
    Some(AMQPType::Double)         => map!(call!(parse_double),           |d| AMQPValue::Double(d))         |
    Some(AMQPType::DecimalValue)   => map!(call!(parse_decimal_value),    |d| AMQPValue::DecimalValue(d))   |
    Some(AMQPType::ShortString)    => map!(call!(parse_short_string),     |s| AMQPValue::ShortString(s))    |
    Some(AMQPType::LongString)     => map!(call!(parse_long_string),      |s| AMQPValue::LongString(s))     |
    Some(AMQPType::FieldArray)     => map!(call!(parse_field_array),      |a| AMQPValue::FieldArray(a))     |
    Some(AMQPType::Timestamp)      => map!(call!(parse_timestamp),        |t| AMQPValue::Timestamp(t))      |
    Some(AMQPType::FieldTable)     => map!(call!(parse_field_table),      |t| AMQPValue::FieldTable(t))     |
    Some(AMQPType::Void)           => value!(AMQPValue::Void)
));

named!(pub parse_type<AMQPType>,                   map_opt!(be_u8, |t| AMQPType::from_id(t as char)));

named!(pub parse_boolean<Boolean>,                 map!(be_u8, |b| b != 0));
named!(pub parse_short_short_int<ShortShortInt>,   call!(be_i8));
named!(pub parse_short_short_uint<ShortShortUInt>, call!(be_u8));
named!(pub parse_short_int<ShortInt>,              call!(be_i16));
named!(pub parse_short_uint<ShortUInt>,            call!(be_u16));
named!(pub parse_long_int<LongInt>,                call!(be_i32));
named!(pub parse_long_uint<LongUInt>,              call!(be_u32));
named!(pub parse_long_long_int<LongLongInt>,       call!(be_i64));
named!(pub parse_long_long_uint<LongLongUInt>,     call!(be_u64));
named!(pub parse_float<Float>,                     call!(be_f32));
named!(pub parse_double<Double>,                   call!(be_f64));
named!(pub parse_decimal_value<DecimalValue>,      do_parse!(scale: parse_short_short_uint >> value: parse_long_uint >> (DecimalValue { scale: scale, value: value, })));
named!(pub parse_short_string<ShortString>,        do_parse!(length: parse_short_short_uint >> s: take_str!(length) >> (s.to_string())));
named!(pub parse_long_string<LongString>,          do_parse!(length: parse_long_uint >> s: take_str!(length) >> (s.to_string())));
named!(pub parse_field_array<FieldArray>,          do_parse!(length: parse_long_int >> array: count!(parse_value, length as usize) >> (array)));
named!(pub parse_timestamp<Timestamp>,             call!(parse_long_long_uint));
named!(pub parse_field_table<FieldTable>,          do_parse!(length: parse_long_uint >> table: flat_map!(take!(length as usize), fold_many0!(complete!(pair!(parse_short_string, parse_value)), FieldTable::new(), |mut acc: FieldTable, (key, value)| {
    acc.insert(key, value);
    acc
})) >> (table)));
<commit_msg>types: add parsing tests for basic types<commit_after>use types::*;

use nom::{be_i8, be_u8, be_i16, be_u16, be_i32, be_u32, be_i64, be_u64, be_f32, be_f64};

/* FIXME: we convert to a Some so that nom can happily handle some _ case which would otherwise fail as we're exhaustive */
named!(pub parse_value<AMQPValue>,                 switch!(map!(parse_type, |t| Some(t)),
    Some(AMQPType::Boolean)        => map!(call!(parse_boolean),          |b| AMQPValue::Boolean(b))        |
    Some(AMQPType::ShortShortInt)  => map!(call!(parse_short_short_int),  |i| AMQPValue::ShortShortInt(i))  |
    Some(AMQPType::ShortShortUInt) => map!(call!(parse_short_short_uint), |u| AMQPValue::ShortShortUInt(u)) |
    Some(AMQPType::ShortInt)       => map!(call!(parse_short_int),        |i| AMQPValue::ShortInt(i))       |
    Some(AMQPType::ShortUInt)      => map!(call!(parse_short_uint),       |u| AMQPValue::ShortUInt(u))      |
    Some(AMQPType::LongInt)        => map!(call!(parse_long_int),         |i| AMQPValue::LongInt(i))        |
    Some(AMQPType::LongUInt)       => map!(call!(parse_long_uint),        |u| AMQPValue::LongUInt(u))       |
    Some(AMQPType::LongLongInt)    => map!(call!(parse_long_long_int),    |i| AMQPValue::LongLongInt(i))    |
    Some(AMQPType::LongLongUInt)   => map!(call!(parse_long_long_uint),   |u| AMQPValue::LongLongUInt(u))   |
    Some(AMQPType::Float)          => map!(call!(parse_float),            |f| AMQPValue::Float(f))          |
    Some(AMQPType::Double)         => map!(call!(parse_double),           |d| AMQPValue::Double(d))         |
    Some(AMQPType::DecimalValue)   => map!(call!(parse_decimal_value),    |d| AMQPValue::DecimalValue(d))   |
    Some(AMQPType::ShortString)    => map!(call!(parse_short_string),     |s| AMQPValue::ShortString(s))    |
    Some(AMQPType::LongString)     => map!(call!(parse_long_string),      |s| AMQPValue::LongString(s))     |
    Some(AMQPType::FieldArray)     => map!(call!(parse_field_array),      |a| AMQPValue::FieldArray(a))     |
    Some(AMQPType::Timestamp)      => map!(call!(parse_timestamp),        |t| AMQPValue::Timestamp(t))      |
    Some(AMQPType::FieldTable)     => map!(call!(parse_field_table),      |t| AMQPValue::FieldTable(t))     |
    Some(AMQPType::Void)           => value!(AMQPValue::Void)
));

named!(pub parse_type<AMQPType>,                   map_opt!(be_u8, |t| AMQPType::from_id(t as char)));

named!(pub parse_boolean<Boolean>,                 map!(be_u8, |b| b != 0));
named!(pub parse_short_short_int<ShortShortInt>,   call!(be_i8));
named!(pub parse_short_short_uint<ShortShortUInt>, call!(be_u8));
named!(pub parse_short_int<ShortInt>,              call!(be_i16));
named!(pub parse_short_uint<ShortUInt>,            call!(be_u16));
named!(pub parse_long_int<LongInt>,                call!(be_i32));
named!(pub parse_long_uint<LongUInt>,              call!(be_u32));
named!(pub parse_long_long_int<LongLongInt>,       call!(be_i64));
named!(pub parse_long_long_uint<LongLongUInt>,     call!(be_u64));
named!(pub parse_float<Float>,                     call!(be_f32));
named!(pub parse_double<Double>,                   call!(be_f64));
named!(pub parse_decimal_value<DecimalValue>,      do_parse!(scale: parse_short_short_uint >> value: parse_long_uint >> (DecimalValue { scale: scale, value: value, })));
named!(pub parse_short_string<ShortString>,        do_parse!(length: parse_short_short_uint >> s: take_str!(length) >> (s.to_string())));
named!(pub parse_long_string<LongString>,          do_parse!(length: parse_long_uint >> s: take_str!(length) >> (s.to_string())));
named!(pub parse_field_array<FieldArray>,          do_parse!(length: parse_long_int >> array: count!(parse_value, length as usize) >> (array)));
named!(pub parse_timestamp<Timestamp>,             call!(parse_long_long_uint));
named!(pub parse_field_table<FieldTable>,          do_parse!(length: parse_long_uint >> table: flat_map!(take!(length as usize), fold_many0!(complete!(pair!(parse_short_string, parse_value)), FieldTable::new(), |mut acc: FieldTable, (key, value)| {
    acc.insert(key, value);
    acc
})) >> (table)));

#[cfg(test)]
mod test {
    use super::*;

    use nom::IResult;

    const EMPTY: &'static [u8] = b"";

    #[test]
    fn test_parse_boolean() {
        assert_eq!(parse_boolean(&[0]), IResult::Done(EMPTY, false));
        assert_eq!(parse_boolean(&[1]), IResult::Done(EMPTY, true));
    }

    #[test]
    fn test_parse_short_short_uint() {
        assert_eq!(parse_short_short_uint(&[0]),   IResult::Done(EMPTY, 0));
        assert_eq!(parse_short_short_uint(&[255]), IResult::Done(EMPTY, 255));
    }

    #[test]
    fn test_parse_short_short_int() {
        assert_eq!(parse_short_short_int(&[0]),   IResult::Done(EMPTY, 0));
        assert_eq!(parse_short_short_int(&[255]), IResult::Done(EMPTY, -1));
    }

    #[test]
    fn test_parse_short_uint() {
        assert_eq!(parse_short_uint(&[0,   0]),   IResult::Done(EMPTY, 0));
        assert_eq!(parse_short_uint(&[255, 255]), IResult::Done(EMPTY, 65535));
    }

    #[test]
    fn test_parse_short_int() {
        assert_eq!(parse_short_int(&[0,   0]),   IResult::Done(EMPTY, 0));
        assert_eq!(parse_short_int(&[255, 255]), IResult::Done(EMPTY, -1));
    }

    #[test]
    fn test_parse_long_uint() {
        assert_eq!(parse_long_uint(&[0,   0,   0,   0]),   IResult::Done(EMPTY, 0));
        assert_eq!(parse_long_uint(&[255, 255, 255, 255]), IResult::Done(EMPTY, 4294967295));
    }

    #[test]
    fn test_parse_long_int() {
        assert_eq!(parse_long_int(&[0,   0,   0,   0]),   IResult::Done(EMPTY, 0));
        assert_eq!(parse_long_int(&[255, 255, 255, 255]), IResult::Done(EMPTY, -1));
    }

    #[test]
    fn test_parse_long_long_uint() {
        assert_eq!(parse_long_long_uint(&[0,   0,   0,   0,   0,   0,   0,   0]),   IResult::Done(EMPTY, 0));
        assert_eq!(parse_long_long_uint(&[255, 255, 255, 255, 255, 255, 255, 255]), IResult::Done(EMPTY, 18446744073709551615));
    }

    #[test]
    fn test_parse_long_long_int() {
        assert_eq!(parse_long_long_int(&[0,   0,   0,   0,   0,   0,   0,   0]),   IResult::Done(EMPTY, 0));
        assert_eq!(parse_long_long_int(&[255, 255, 255, 255, 255, 255, 255, 255]), IResult::Done(EMPTY, -1));
    }

    #[test]
    fn test_parse_float() {
        assert_eq!(parse_float(&[0,  0,  0,  0]),  IResult::Done(EMPTY, 0.));
        assert_eq!(parse_float(&[42, 42, 42, 42]), IResult::Done(EMPTY, 0.00000000000015113662));
    }

    #[test]
    fn test_parse_double() {
        assert_eq!(parse_double(&[0,  0,  0,  0,  0,  0,  0,  0]),  IResult::Done(EMPTY, 0.));
        assert_eq!(parse_double(&[42, 42, 42, 42, 42, 42, 42, 42]), IResult::Done(EMPTY, 0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000014260258159703532));
    }

    #[test]
    fn test_parse_decimal_value() {
        assert_eq!(parse_decimal_value(&[0,   0,   0,   0,   0]),   IResult::Done(EMPTY, DecimalValue { scale: 0,   value: 0          }));
        assert_eq!(parse_decimal_value(&[255, 255, 255, 255, 255]), IResult::Done(EMPTY, DecimalValue { scale: 255, value: 4294967295 }));
    }

    #[test]
    fn test_parse_short_string() {
        assert_eq!(parse_short_string(&[0]),                                             IResult::Done(EMPTY, ShortString::new()));
        assert_eq!(parse_short_string(&[4, 't' as u8, 'e' as u8, 's' as u8, 't' as u8]), IResult::Done(EMPTY, "test".to_string()));
    }

    #[test]
    fn test_parse_long_string() {
        assert_eq!(parse_long_string(&[0, 0, 0, 0]),                                             IResult::Done(EMPTY, LongString::new()));
        assert_eq!(parse_long_string(&[0, 0, 0, 4, 't' as u8, 'e' as u8, 's' as u8, 't' as u8]), IResult::Done(EMPTY, "test".to_string()));
    }

    #[test]
    fn test_parse_timestamp() {
        assert_eq!(parse_timestamp(&[0,   0,   0,   0,   0,   0,   0,   0]),   IResult::Done(EMPTY, 0));
        assert_eq!(parse_timestamp(&[255, 255, 255, 255, 255, 255, 255, 255]), IResult::Done(EMPTY, 18446744073709551615));
    }
}
<|endoftext|>


<commit_before>//! This crate provides functionality to execute a daily schedule for home
//! automation purposes. It provided a variety to schedule tasks in a day.
//!
//! The purpose of this crate is only calculation of execution times, but
//! doesn't perform the actual execution of the schedule loop.
//!
//! It doesn't rely on system time on purpose to allow easier testing and
//! qualification, without considering the real-time aspects. All
//! calculated timestamps are UTC based and any local-time conversion are
//! based on the zoneinfo crate.
extern crate rand;
extern crate time;
extern crate zoneinfo;

use time::{Timespec, Duration, at_utc};
use std::collections::BTreeMap;
use std::rc::Rc;
use rand::{Rng, thread_rng};
use zoneinfo::{ZoneInfo, ZoneInfoElement};
use std::io::Result;

/// Represents a fixed moment in a day
pub enum Moment {
    /// Duration is offset in time based on local midnight
    LocalTime(Duration),
    /// Duration is offset in time based on UTC midnight
    UtcTime(Duration)
}

/// Local time definition
enum LocalTimeState {
    /// Zone-info state is not loaded yet
    Unknown,
    /// Current zone-info specifies no future daylight saving time change is expected
    NoChangePending(ZoneInfoElement),
    /// Daylight saving time change is pending
    ChangePending(Timespec, // transition time
                  ZoneInfoElement, // zone information before transition time
                  ZoneInfoElement) // zone information at and after transition time
}

impl Moment {
    /// Create a moment in a day
    pub fn new(h:u8, m:u8, s:u8) -> Moment {
        Moment::LocalTime(
            Duration::hours(h as i64) +
            Duration::minutes(m as i64) +
            Duration::seconds(s as i64))
    }

    /// Create a moment in a day based on Timespec
    pub fn new_from_timespec(ts: Timespec) -> Moment {
        let mut tm_utc = at_utc(ts);

        tm_utc.tm_hour = 0;
        tm_utc.tm_min = 0;
        tm_utc.tm_sec = 0;
        tm_utc.tm_nsec = 0;

        Moment::UtcTime(ts - tm_utc.to_timespec())
    }

    /// Convert schedule time to actual time stamp
    fn create_timestamp(&self, ut_midnight_reference: Timespec,
                        localtime: &LocalTimeState) -> Timespec {
        match self {
            // timestamp is simply a reference to UTC so just add the offset
            &Moment::UtcTime(offset) => ut_midnight_reference + offset,
            // timestamp is a reference to the moment in a day
            &Moment::LocalTime(offset) => {
                let pre_localtime_cor = ut_midnight_reference + offset;

                let ut_offset = match *localtime {
                    LocalTimeState::NoChangePending(ref info) => info.ut_offset,
                    LocalTimeState::ChangePending(transition_time, ref before, ref after) => {
                        let reftime = Timespec::new(pre_localtime_cor.sec - before.ut_offset as i64,
                                                    pre_localtime_cor.nsec);
                        if reftime < transition_time {
                            before.ut_offset
                        } else {
                            after.ut_offset
                        }
                    }
                    _ => unreachable!()
                };

                Timespec::new(pre_localtime_cor.sec - ut_offset as i64, pre_localtime_cor.nsec)
            }
        }
    }
}

impl std::fmt::Debug for Moment {
    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
        let duration = match self {
            &Moment::UtcTime(d) => d,
            &Moment::LocalTime(d) => d,
        };
        try!(write!(fmt, "{:02}:{:02}:{:02}", duration.num_hours(), duration.num_minutes() % 60, duration.num_seconds() % 60));
        if let &Moment::UtcTime(_) = self {
            try!(write!(fmt, " (UTC)"));
        }
        Ok(())
    }
}

/// Weekday filter specifier
pub enum Filter {
    /// Always execute  event
    Always,
    /// Only execute Monday till Friday
    MonToFri,
    /// Only execute Saturday and Sunday
    Weekend // FIXME: more abstractions?
}

impl Filter {
    /// Indicate whether given time is valid to be scheduled based on weekday
    fn filter_days(&self, time: Timespec, zoneinfo: &ZoneInfoElement) -> bool {
        // make sure reference time is in the same weekday in UTC as it would be
        // in local time.
        let ref_time = Timespec::new(time.sec + zoneinfo.ut_offset as i64, time.nsec);
        let wday = at_utc(ref_time).tm_wday;
        let weekend = wday == 0 || wday == 6; // 0 = Sunday, 6 = Saturday

        match self {
            &Filter::Always => true,
            &Filter::MonToFri => !weekend,
            &Filter::Weekend => weekend
        }
    }

    /// Indicate whether given time is valid to be scheduled based on weekday
    fn day_scheduled(&self, time: Timespec, localtime: &LocalTimeState) -> bool {
        match self {
            &Filter::Always => true,
            &Filter::MonToFri|&Filter::Weekend => {
                let zoneinfo = match localtime {
                    &LocalTimeState::NoChangePending(ref zoneinfo) => zoneinfo,
                    &LocalTimeState::ChangePending(ref transition, ref z1, ref z2) => {
                        if time < *transition {
                            z1
                        } else {
                            z2
                        }
                    }
                    _ => unreachable!()
                };

                self.filter_days(time, zoneinfo)
            },
        }
    }
}

/// Represent a (abstract) moment in a day
pub enum DailyEvent {
    /// A fixed moment in a day
    Fixed(Filter, Moment),
    /// A random moment between two given fixed moments
    Fuzzy(Filter, Moment, Moment),
    /// A externally provided moment in time + variance
    ByClosure(Filter, Box<Fn(Timespec) -> Moment>, Duration)
}

impl std::fmt::Debug for DailyEvent {
    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
        match self {
            &DailyEvent::Fixed(_, ref t) => write!(fmt, "Fixed {:?}", t),
            &DailyEvent::Fuzzy(_, ref b, ref a) => write!(fmt, "Fuzzy {:?} ~ {:?}", b, a),
            &DailyEvent::ByClosure(_, _, ref variance) =>
                write!(fmt, "ByClosure ~{:?}s", variance.num_seconds()),
        }
    }
}

/// Represents a moment and an specific action in a day
struct Event<C: Eq+PartialEq, H: Handler<C>> {
    /// A moment in a day
    moment: DailyEvent,
    /// Reference to a action handler
    action: Rc<H>,
    /// Externally provided reference for the implementor
    context: C
}

impl<C: Eq+PartialEq, H: Handler<C>> Event<C, H> {
    /// Determine time-stamp for event
    fn create_timestamp(&self, ut_midnight_reference: Timespec,
                        localtime: &LocalTimeState) -> Option<Timespec> {
        let ts = match self.moment {
            DailyEvent::Fixed(_, ref moment) =>
                moment.create_timestamp(ut_midnight_reference, localtime),
            DailyEvent::Fuzzy(_, ref m1, ref m2) => {
                // pick a time between both given moment
                let mut rng = rand::thread_rng();
                let t1 = m1.create_timestamp(ut_midnight_reference, localtime);
                let t2 = m2.create_timestamp(ut_midnight_reference, localtime);
                let t_start = if t1 >= t2 {t2} else {t1};
                let t_end = if t1 >= t2 {t1} else {t2};
                let duration = t_end - t_start;
                if duration > Duration::seconds(0) {
                    t_start + Duration::seconds(rng.gen_range(0, duration.num_seconds()))
                } else {
                    t_start
                }
            }
            DailyEvent::ByClosure(_, ref func, ref variance) => {
                let moment = func(ut_midnight_reference);
                // generate a offset based on variance compared to the generated moment
                let mut rng = rand::thread_rng();
                let offset = if *variance > Duration::seconds(0) {
                    rng.gen_range(0, variance.num_seconds())
                } else {
                    0
                };
                let offset = Duration::seconds(variance.num_seconds() / 2 - offset);
                moment.create_timestamp(ut_midnight_reference, localtime) + offset
            }
        };
        let do_schedule = match self.moment {
            DailyEvent::Fixed(ref w, _) |
            DailyEvent::Fuzzy(ref w, _, _) |
            DailyEvent::ByClosure(ref w, _, _) => w.day_scheduled(ts, localtime)
        };

        if do_schedule {
            Some(ts)
        } else {
            None
        }
    }
}

/// Trait to be implemented by the event handler
pub trait Handler<C: Eq + PartialEq> {
    /// Hint for future events
    fn hint(&self, timestamp: &Timespec, context: &C);
    /// Perform a action (in a day)
    fn kick(&self, timestamp: &Timespec, context: &C);
}

/// Calculates and executes scheduled events every day
pub struct Schedule<C: Eq + PartialEq, H: Handler<C>> {
    // List of (abstract) moments in a day
    events: Vec<Rc<Event<C, H>>>,

    // Time zone related information
    zoneinfo: ZoneInfo,

    // Next zone change
    localtime: LocalTimeState,

    // Tree of actual scheduled moments and reference to the abstract moment in a day
    schedule: BTreeMap<Timespec, Vec<Rc<Event<C, H>>>>
}

impl<C: Eq + PartialEq, H: Handler<C>> Schedule<C, H> {
    /// Create a (empty) list of scheduled daily events
    pub fn new(zoneinfo: ZoneInfo) -> Schedule<C, H> {
        Schedule {
            events: vec![],
            zoneinfo: zoneinfo,
            localtime: LocalTimeState::Unknown,
            schedule: BTreeMap::new()
        }
    }

    /// Create a (empty) list of scheduled daily events based on the default zoneinfo (local time
    /// settings)
    pub fn new_local() -> Result<Schedule<C, H>> {
        Ok(Schedule::new(try!(ZoneInfo::get_local_zoneinfo())))
    }

    /// Add a (abstract) moment and action in a day
    pub fn add_event(&mut self,
                     moment: DailyEvent,
                     action: Rc<H>,
                     context: C) {
        self.events.push(Rc::new(Event {
            moment: moment,
            action: action,
            context: context
        }));
    }

    /// Determine next zone info state
    fn new_change_state(&self, timestamp: Timespec) -> LocalTimeState {
        // yes, a unwrap, since a serious problem be present when no zone-info could be retrieved
        let actual = self.zoneinfo.get_actual_zoneinfo(timestamp).unwrap();
        match self.zoneinfo.get_next_transition_time(timestamp) {
            Some((next_change, next)) =>
                LocalTimeState::ChangePending(next_change, actual, next),
            None => LocalTimeState::NoChangePending(actual)
        }
    }

    /// Update the schedule for 24 hours (only use with 24 hour incrementing timestamps,
    /// preferably every day)
    pub fn update_schedule(&mut self, ut_midnight_reference: Timespec) {
        match self.localtime {
            LocalTimeState::Unknown =>
                self.localtime = self.new_change_state(ut_midnight_reference),
            LocalTimeState::ChangePending(time, _, _) => {
                if time <= ut_midnight_reference {
                    self.localtime = self.new_change_state(ut_midnight_reference);
                }
            },
            _ => {}
        }

        for event in &self.events {
            let timestamp = event.create_timestamp(ut_midnight_reference, &self.localtime);
            if let Some(timestamp) = timestamp {
                event.action.hint(&timestamp, &event.context);

                let event_cloned = event.clone();

                if self.schedule.contains_key(&timestamp) {
                    self.schedule.get_mut(&timestamp).unwrap().push(event_cloned);
                } else {
                    self.schedule.insert(timestamp, vec![event_cloned]);
                }
            }
        }
    }

    /// Consume schedule until provided moment `now` and kick last or current event and returns next event time
    pub fn kick_event(&mut self, now: Timespec) -> Option<Timespec> {
        let past_events: Vec<Timespec> = self.schedule.keys().filter(|&k| *k <= now).cloned().collect();

        // kick the current event...
        for timestamp in past_events.iter() {
            if let Some(schedule_events) = self.schedule.get(&timestamp) {
                for schedule_event in schedule_events {
                    schedule_event.action.kick(&timestamp, &schedule_event.context);
                }
            }
        }

        // ...and consume that and prior events
        for past_event in past_events {
            self.schedule.remove(&past_event);
        }

        self.schedule.keys().cloned().nth(0)
    }

    /// Peek when next event will happen
    pub fn peek_event(&self) -> Option<Timespec> {
        self.schedule.keys().cloned().nth(0)
    }
}
<commit_msg>rustup<commit_after>//! This crate provides functionality to execute a daily schedule for home
//! automation purposes. It provided a variety to schedule tasks in a day.
//!
//! The purpose of this crate is only calculation of execution times, but
//! doesn't perform the actual execution of the schedule loop.
//!
//! It doesn't rely on system time on purpose to allow easier testing and
//! qualification, without considering the real-time aspects. All
//! calculated timestamps are UTC based and any local-time conversion are
//! based on the zoneinfo crate.
extern crate rand;
extern crate time;
extern crate zoneinfo;

use time::{Timespec, Duration, at_utc};
use std::collections::BTreeMap;
use std::rc::Rc;
use rand::Rng;
use zoneinfo::{ZoneInfo, ZoneInfoElement};
use std::io::Result;

/// Represents a fixed moment in a day
pub enum Moment {
    /// Duration is offset in time based on local midnight
    LocalTime(Duration),
    /// Duration is offset in time based on UTC midnight
    UtcTime(Duration)
}

/// Local time definition
enum LocalTimeState {
    /// Zone-info state is not loaded yet
    Unknown,
    /// Current zone-info specifies no future daylight saving time change is expected
    NoChangePending(ZoneInfoElement),
    /// Daylight saving time change is pending
    ChangePending(Timespec, // transition time
                  ZoneInfoElement, // zone information before transition time
                  ZoneInfoElement) // zone information at and after transition time
}

impl Moment {
    /// Create a moment in a day
    pub fn new(h:u8, m:u8, s:u8) -> Moment {
        Moment::LocalTime(
            Duration::hours(h as i64) +
            Duration::minutes(m as i64) +
            Duration::seconds(s as i64))
    }

    /// Create a moment in a day based on Timespec
    pub fn new_from_timespec(ts: Timespec) -> Moment {
        let mut tm_utc = at_utc(ts);

        tm_utc.tm_hour = 0;
        tm_utc.tm_min = 0;
        tm_utc.tm_sec = 0;
        tm_utc.tm_nsec = 0;

        Moment::UtcTime(ts - tm_utc.to_timespec())
    }

    /// Convert schedule time to actual time stamp
    fn create_timestamp(&self, ut_midnight_reference: Timespec,
                        localtime: &LocalTimeState) -> Timespec {
        match self {
            // timestamp is simply a reference to UTC so just add the offset
            &Moment::UtcTime(offset) => ut_midnight_reference + offset,
            // timestamp is a reference to the moment in a day
            &Moment::LocalTime(offset) => {
                let pre_localtime_cor = ut_midnight_reference + offset;

                let ut_offset = match *localtime {
                    LocalTimeState::NoChangePending(ref info) => info.ut_offset,
                    LocalTimeState::ChangePending(transition_time, ref before, ref after) => {
                        let reftime = Timespec::new(pre_localtime_cor.sec - before.ut_offset as i64,
                                                    pre_localtime_cor.nsec);
                        if reftime < transition_time {
                            before.ut_offset
                        } else {
                            after.ut_offset
                        }
                    }
                    _ => unreachable!()
                };

                Timespec::new(pre_localtime_cor.sec - ut_offset as i64, pre_localtime_cor.nsec)
            }
        }
    }
}

impl std::fmt::Debug for Moment {
    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
        let duration = match self {
            &Moment::UtcTime(d) => d,
            &Moment::LocalTime(d) => d,
        };
        try!(write!(fmt, "{:02}:{:02}:{:02}", duration.num_hours(), duration.num_minutes() % 60, duration.num_seconds() % 60));
        if let &Moment::UtcTime(_) = self {
            try!(write!(fmt, " (UTC)"));
        }
        Ok(())
    }
}

/// Weekday filter specifier
pub enum Filter {
    /// Always execute  event
    Always,
    /// Only execute Monday till Friday
    MonToFri,
    /// Only execute Saturday and Sunday
    Weekend // FIXME: more abstractions?
}

impl Filter {
    /// Indicate whether given time is valid to be scheduled based on weekday
    fn filter_days(&self, time: Timespec, zoneinfo: &ZoneInfoElement) -> bool {
        // make sure reference time is in the same weekday in UTC as it would be
        // in local time.
        let ref_time = Timespec::new(time.sec + zoneinfo.ut_offset as i64, time.nsec);
        let wday = at_utc(ref_time).tm_wday;
        let weekend = wday == 0 || wday == 6; // 0 = Sunday, 6 = Saturday

        match self {
            &Filter::Always => true,
            &Filter::MonToFri => !weekend,
            &Filter::Weekend => weekend
        }
    }

    /// Indicate whether given time is valid to be scheduled based on weekday
    fn day_scheduled(&self, time: Timespec, localtime: &LocalTimeState) -> bool {
        match self {
            &Filter::Always => true,
            &Filter::MonToFri|&Filter::Weekend => {
                let zoneinfo = match localtime {
                    &LocalTimeState::NoChangePending(ref zoneinfo) => zoneinfo,
                    &LocalTimeState::ChangePending(ref transition, ref z1, ref z2) => {
                        if time < *transition {
                            z1
                        } else {
                            z2
                        }
                    }
                    _ => unreachable!()
                };

                self.filter_days(time, zoneinfo)
            },
        }
    }
}

/// Represent a (abstract) moment in a day
pub enum DailyEvent {
    /// A fixed moment in a day
    Fixed(Filter, Moment),
    /// A random moment between two given fixed moments
    Fuzzy(Filter, Moment, Moment),
    /// A externally provided moment in time + variance
    ByClosure(Filter, Box<Fn(Timespec) -> Moment>, Duration)
}

impl std::fmt::Debug for DailyEvent {
    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
        match self {
            &DailyEvent::Fixed(_, ref t) => write!(fmt, "Fixed {:?}", t),
            &DailyEvent::Fuzzy(_, ref b, ref a) => write!(fmt, "Fuzzy {:?} ~ {:?}", b, a),
            &DailyEvent::ByClosure(_, _, ref variance) =>
                write!(fmt, "ByClosure ~{:?}s", variance.num_seconds()),
        }
    }
}

/// Represents a moment and an specific action in a day
struct Event<C: Eq+PartialEq, H: Handler<C>> {
    /// A moment in a day
    moment: DailyEvent,
    /// Reference to a action handler
    action: Rc<H>,
    /// Externally provided reference for the implementor
    context: C
}

impl<C: Eq+PartialEq, H: Handler<C>> Event<C, H> {
    /// Determine time-stamp for event
    fn create_timestamp(&self, ut_midnight_reference: Timespec,
                        localtime: &LocalTimeState) -> Option<Timespec> {
        let ts = match self.moment {
            DailyEvent::Fixed(_, ref moment) =>
                moment.create_timestamp(ut_midnight_reference, localtime),
            DailyEvent::Fuzzy(_, ref m1, ref m2) => {
                // pick a time between both given moment
                let mut rng = rand::thread_rng();
                let t1 = m1.create_timestamp(ut_midnight_reference, localtime);
                let t2 = m2.create_timestamp(ut_midnight_reference, localtime);
                let t_start = if t1 >= t2 {t2} else {t1};
                let t_end = if t1 >= t2 {t1} else {t2};
                let duration = t_end - t_start;
                if duration > Duration::seconds(0) {
                    t_start + Duration::seconds(rng.gen_range(0, duration.num_seconds()))
                } else {
                    t_start
                }
            }
            DailyEvent::ByClosure(_, ref func, ref variance) => {
                let moment = func(ut_midnight_reference);
                // generate a offset based on variance compared to the generated moment
                let mut rng = rand::thread_rng();
                let offset = if *variance > Duration::seconds(0) {
                    rng.gen_range(0, variance.num_seconds())
                } else {
                    0
                };
                let offset = Duration::seconds(variance.num_seconds() / 2 - offset);
                moment.create_timestamp(ut_midnight_reference, localtime) + offset
            }
        };
        let do_schedule = match self.moment {
            DailyEvent::Fixed(ref w, _) |
            DailyEvent::Fuzzy(ref w, _, _) |
            DailyEvent::ByClosure(ref w, _, _) => w.day_scheduled(ts, localtime)
        };

        if do_schedule {
            Some(ts)
        } else {
            None
        }
    }
}

/// Trait to be implemented by the event handler
pub trait Handler<C: Eq + PartialEq> {
    /// Hint for future events
    fn hint(&self, timestamp: &Timespec, context: &C);
    /// Perform a action (in a day)
    fn kick(&self, timestamp: &Timespec, context: &C);
}

/// Calculates and executes scheduled events every day
pub struct Schedule<C: Eq + PartialEq, H: Handler<C>> {
    // List of (abstract) moments in a day
    events: Vec<Rc<Event<C, H>>>,

    // Time zone related information
    zoneinfo: ZoneInfo,

    // Next zone change
    localtime: LocalTimeState,

    // Tree of actual scheduled moments and reference to the abstract moment in a day
    schedule: BTreeMap<Timespec, Vec<Rc<Event<C, H>>>>
}

impl<C: Eq + PartialEq, H: Handler<C>> Schedule<C, H> {
    /// Create a (empty) list of scheduled daily events
    pub fn new(zoneinfo: ZoneInfo) -> Schedule<C, H> {
        Schedule {
            events: vec![],
            zoneinfo: zoneinfo,
            localtime: LocalTimeState::Unknown,
            schedule: BTreeMap::new()
        }
    }

    /// Create a (empty) list of scheduled daily events based on the default zoneinfo (local time
    /// settings)
    pub fn new_local() -> Result<Schedule<C, H>> {
        Ok(Schedule::new(try!(ZoneInfo::get_local_zoneinfo())))
    }

    /// Add a (abstract) moment and action in a day
    pub fn add_event(&mut self,
                     moment: DailyEvent,
                     action: Rc<H>,
                     context: C) {
        self.events.push(Rc::new(Event {
            moment: moment,
            action: action,
            context: context
        }));
    }

    /// Determine next zone info state
    fn new_change_state(&self, timestamp: Timespec) -> LocalTimeState {
        // yes, a unwrap, since a serious problem be present when no zone-info could be retrieved
        let actual = self.zoneinfo.get_actual_zoneinfo(timestamp).unwrap();
        match self.zoneinfo.get_next_transition_time(timestamp) {
            Some((next_change, next)) =>
                LocalTimeState::ChangePending(next_change, actual, next),
            None => LocalTimeState::NoChangePending(actual)
        }
    }

    /// Update the schedule for 24 hours (only use with 24 hour incrementing timestamps,
    /// preferably every day)
    pub fn update_schedule(&mut self, ut_midnight_reference: Timespec) {
        match self.localtime {
            LocalTimeState::Unknown =>
                self.localtime = self.new_change_state(ut_midnight_reference),
            LocalTimeState::ChangePending(time, _, _) => {
                if time <= ut_midnight_reference {
                    self.localtime = self.new_change_state(ut_midnight_reference);
                }
            },
            _ => {}
        }

        for event in &self.events {
            let timestamp = event.create_timestamp(ut_midnight_reference, &self.localtime);
            if let Some(timestamp) = timestamp {
                event.action.hint(&timestamp, &event.context);

                let event_cloned = event.clone();

                if self.schedule.contains_key(&timestamp) {
                    self.schedule.get_mut(&timestamp).unwrap().push(event_cloned);
                } else {
                    self.schedule.insert(timestamp, vec![event_cloned]);
                }
            }
        }
    }

    /// Consume schedule until provided moment `now` and kick last or current event and returns next event time
    pub fn kick_event(&mut self, now: Timespec) -> Option<Timespec> {
        let past_events: Vec<Timespec> = self.schedule.keys().filter(|&k| *k <= now).cloned().collect();

        // kick the current event...
        for timestamp in past_events.iter() {
            if let Some(schedule_events) = self.schedule.get(&timestamp) {
                for schedule_event in schedule_events {
                    schedule_event.action.kick(&timestamp, &schedule_event.context);
                }
            }
        }

        // ...and consume that and prior events
        for past_event in past_events {
            self.schedule.remove(&past_event);
        }

        self.schedule.keys().cloned().nth(0)
    }

    /// Peek when next event will happen
    pub fn peek_event(&self) -> Option<Timespec> {
        self.schedule.keys().cloned().nth(0)
    }
}
<|endoftext|>


<commit_before>#![allow(unused_features)]

#![doc="A generics based scientific computing library for Rust

# Introduction

This library aims to provide scientific computing functionality in Rust.
Current focus is to provide a comprehensive API with simple straight-forward
implementations. Different modules cover functionality
covering matrices, linear algebra, signal processing and statistics.

# Implementation

The implementation's aim to take the best advantage
of language features in Rust, yet avoid getting too much into
heavy degree of performance optimization. We do take
advantage of Rust features like generic programming,
immutable by default approach, type traits, iterators,
amongst others. This is a pure Rust implementation.
There is no integration planned at the moment with
C libraries like BLAS or LAPACK.

"]
 #![feature(core)]
#![feature(alloc)]
#![feature(step_by)]
#![feature(convert)]
#![feature(test)]

#[macro_use(plugin, link)]
#[macro_use]
extern crate log;
extern crate rand;
extern crate num;

// Common modules
pub mod error;
pub mod algebra;

// Main libraries
pub mod external;
pub mod discrete;
pub mod matrix;
pub mod linalg;
pub mod signal;
pub mod dx;
pub mod stat;
pub mod alg;
// // pub mod opt;
// pub mod image {
// #![doc="Image processing
// "]
// }

// pub mod audio {
// #![doc="Audio signal processing
// "]
// }

// // for internal use only
pub mod util;
mod testdata;

// // Overall API of SciRust
pub mod api;
<commit_msg>Allow feature(raw)<commit_after>#![allow(unused_features)]
#![feature(raw)]

#![doc="A generics based scientific computing library for Rust

# Introduction

This library aims to provide scientific computing functionality in Rust.
Current focus is to provide a comprehensive API with simple straight-forward
implementations. Different modules cover functionality
covering matrices, linear algebra, signal processing and statistics.

# Implementation

The implementation's aim to take the best advantage
of language features in Rust, yet avoid getting too much into
heavy degree of performance optimization. We do take
advantage of Rust features like generic programming,
immutable by default approach, type traits, iterators,
amongst others. This is a pure Rust implementation.
There is no integration planned at the moment with
C libraries like BLAS or LAPACK.

"]
 #![feature(core)]
#![feature(alloc)]
#![feature(step_by)]
#![feature(convert)]
#![feature(test)]

#[macro_use(plugin, link)]
#[macro_use]
extern crate log;
extern crate rand;
extern crate num;

// Common modules
pub mod error;
pub mod algebra;

// Main libraries
pub mod external;
pub mod discrete;
pub mod matrix;
pub mod linalg;
pub mod signal;
pub mod dx;
pub mod stat;
pub mod alg;
// // pub mod opt;
// pub mod image {
// #![doc="Image processing
// "]
// }

// pub mod audio {
// #![doc="Audio signal processing
// "]
// }

// // for internal use only
pub mod util;
mod testdata;

// // Overall API of SciRust
pub mod api;
<|endoftext|>