<commit_before>use serde_json::builder::ObjectBuilder;
use serde_json::Value;
use std::net::Shutdown;
use std::sync::mpsc::{
    TryRecvError,
    Receiver as MpscReceiver,
    Sender as MpscSender
};
use std::time::Duration as StdDuration;
use std::{env, thread};
use super::super::ClientError;
use super::{GatewayError, GatewayStatus};
use time::{self, Duration};
use websocket::client::request::Url as RequestUrl;
use websocket::client::{Receiver, Sender};
use websocket::stream::WebSocketStream;
use ::constants::{self, OpCode};
use ::error::{Error, Result};
use ::internal::ws_impl::{ReceiverExt, SenderExt};
use ::model::event::{Event, GatewayEvent, ReadyEvent};

#[inline]
pub fn parse_ready(event: GatewayEvent,
                   tx: &MpscSender<GatewayStatus>,
                   receiver: &mut Receiver<WebSocketStream>,
                   identification: Value)
                   -> Result<(ReadyEvent, u64)> {
    match event {
        GatewayEvent::Dispatch(seq, Event::Ready(event)) => {
            Ok((event, seq))
        },
        GatewayEvent::InvalidateSession => {
            debug!("Session invalidation");

            let _ = tx.send(GatewayStatus::SendMessage(identification));

            match receiver.recv_json(GatewayEvent::decode)? {
                GatewayEvent::Dispatch(seq, Event::Ready(event)) => {
                    Ok((event, seq))
                },
                other => {
                    debug!("Unexpected event: {:?}", other);

                    Err(Error::Gateway(GatewayError::InvalidHandshake))
                },
            }
        },
        other => {
            debug!("Unexpected event: {:?}", other);

            Err(Error::Gateway(GatewayError::InvalidHandshake))
        },
    }
}

pub fn identify(token: &str, shard_info: Option<[u8; 2]>) -> Value {
    ObjectBuilder::new()
        .insert("op", OpCode::Identify.num())
        .insert_object("d", |mut object| {
            object = identify_compression(object)
                .insert("large_threshold", 250) // max value
                .insert_object("properties", |object| object
                    .insert("$browser", "Ergonomic and high-level Rust library")
                    .insert("$device", "serenity")
                    .insert("$os", env::consts::OS)
                    .insert("$referrer", "")
                    .insert("$referring_domain", "")
                )
                .insert("token", token)
                .insert("v", constants::GATEWAY_VERSION);

            if let Some(shard_info) = shard_info {
                object = object.insert_array("shard", |a| a
                    .push(shard_info[0])
                    .push(shard_info[1]));
            }

            object
        })
        .build()
}

#[cfg(not(feature="debug"))]
pub fn identify_compression(object: ObjectBuilder) -> ObjectBuilder {
    object.insert("compression", true)
}

#[cfg(feature="debug")]
pub fn identify_compression(object: ObjectBuilder) -> ObjectBuilder {
    object.insert("compression", false)
}

pub fn build_gateway_url(base: &str) -> Result<RequestUrl> {
    RequestUrl::parse(&format!("{}?v={}", base, constants::GATEWAY_VERSION))
        .map_err(|_| Error::Client(ClientError::Gateway))
}

pub fn keepalive(interval: u64,
                 mut sender: Sender<WebSocketStream>,
                 channel: MpscReceiver<GatewayStatus>) {
    let mut base_interval = Duration::milliseconds(interval as i64);
    let mut next_tick = time::get_time() + base_interval;

    let mut last_sequence = 0;

    'outer: loop {
        thread::sleep(StdDuration::from_millis(100));

        loop {
            match channel.try_recv() {
                Ok(GatewayStatus::ChangeInterval(interval)) => {
                    base_interval = Duration::milliseconds(interval as i64);
                },
                Ok(GatewayStatus::ChangeSender(new_sender)) => {
                    sender = new_sender;
                },
                Ok(GatewayStatus::SendMessage(val)) => {
                    if let Err(why) = sender.send_json(&val) {
                        warn!("Error sending message: {:?}", why);
                    }
                },
                Ok(GatewayStatus::Sequence(seq)) => {
                    last_sequence = seq;
                },
                Err(TryRecvError::Empty) => break,
                Err(TryRecvError::Disconnected) => break 'outer,
            }
        }

        if time::get_time() >= next_tick {
            next_tick = next_tick + base_interval;

            let map = ObjectBuilder::new()
                .insert("d", last_sequence)
                .insert("op", OpCode::Heartbeat.num())
                .build();

            if let Err(why) = sender.send_json(&map) {
                warn!("Error sending keepalive: {:?}", why);
            }
        }
    }

    let _ = sender.get_mut().shutdown(Shutdown::Both);
}
<commit_msg>Cleanup gateway prep<commit_after>use serde_json::builder::ObjectBuilder;
use serde_json::Value;
use std::net::Shutdown;
use std::sync::mpsc::{
    TryRecvError,
    Receiver as MpscReceiver,
    Sender as MpscSender
};
use std::time::Duration as StdDuration;
use std::{env, thread};
use super::super::ClientError;
use super::{GatewayError, GatewayStatus};
use time::{self, Duration};
use websocket::client::request::Url as RequestUrl;
use websocket::client::{Receiver, Sender};
use websocket::stream::WebSocketStream;
use ::constants::{self, OpCode};
use ::error::{Error, Result};
use ::internal::ws_impl::{ReceiverExt, SenderExt};
use ::model::event::{Event, GatewayEvent, ReadyEvent};

#[inline]
pub fn parse_ready(event: GatewayEvent,
                   tx: &MpscSender<GatewayStatus>,
                   receiver: &mut Receiver<WebSocketStream>,
                   identification: Value)
                   -> Result<(ReadyEvent, u64)> {
    match event {
        GatewayEvent::Dispatch(seq, Event::Ready(event)) => {
            Ok((event, seq))
        },
        GatewayEvent::InvalidateSession => {
            debug!("Session invalidation");

            let _ = tx.send(GatewayStatus::SendMessage(identification));

            match receiver.recv_json(GatewayEvent::decode)? {
                GatewayEvent::Dispatch(seq, Event::Ready(event)) => {
                    Ok((event, seq))
                },
                other => {
                    debug!("Unexpected event: {:?}", other);

                    Err(Error::Gateway(GatewayError::InvalidHandshake))
                },
            }
        },
        other => {
            debug!("Unexpected event: {:?}", other);

            Err(Error::Gateway(GatewayError::InvalidHandshake))
        },
    }
}

pub fn identify(token: &str, shard_info: Option<[u8; 2]>) -> Value {
    ObjectBuilder::new()
        .insert("op", OpCode::Identify.num())
        .insert_object("d", |mut object| {
            object = identify_compression(object)
                .insert("large_threshold", 250) // max value
                .insert_object("properties", |object| object
                    .insert("$browser", "Ergonomic and high-level Rust library")
                    .insert("$device", "serenity")
                    .insert("$os", env::consts::OS))
                .insert("token", token)
                .insert("v", constants::GATEWAY_VERSION);

            if let Some(shard_info) = shard_info {
                object = object.insert_array("shard", |a| a
                    .push(shard_info[0])
                    .push(shard_info[1]));
            }

            object
        })
        .build()
}

#[cfg(not(feature="debug"))]
pub fn identify_compression(object: ObjectBuilder) -> ObjectBuilder {
    object.insert("compression", true)
}

#[cfg(feature="debug")]
pub fn identify_compression(object: ObjectBuilder) -> ObjectBuilder {
    object.insert("compression", false)
}

pub fn build_gateway_url(base: &str) -> Result<RequestUrl> {
    RequestUrl::parse(&format!("{}?v={}", base, constants::GATEWAY_VERSION))
        .map_err(|_| Error::Client(ClientError::Gateway))
}

pub fn keepalive(interval: u64,
                 mut sender: Sender<WebSocketStream>,
                 channel: MpscReceiver<GatewayStatus>) {
    let mut base_interval = Duration::milliseconds(interval as i64);
    let mut next_tick = time::get_time() + base_interval;

    let mut last_sequence = 0;

    'outer: loop {
        thread::sleep(StdDuration::from_millis(100));

        loop {
            match channel.try_recv() {
                Ok(GatewayStatus::ChangeInterval(interval)) => {
                    base_interval = Duration::milliseconds(interval as i64);
                },
                Ok(GatewayStatus::ChangeSender(new_sender)) => {
                    sender = new_sender;
                },
                Ok(GatewayStatus::SendMessage(val)) => {
                    if let Err(why) = sender.send_json(&val) {
                        warn!("Error sending message: {:?}", why);
                    }
                },
                Ok(GatewayStatus::Sequence(seq)) => {
                    last_sequence = seq;
                },
                Err(TryRecvError::Empty) => break,
                Err(TryRecvError::Disconnected) => break 'outer,
            }
        }

        if time::get_time() >= next_tick {
            next_tick = next_tick + base_interval;

            let map = ObjectBuilder::new()
                .insert("d", last_sequence)
                .insert("op", OpCode::Heartbeat.num())
                .build();

            if let Err(why) = sender.send_json(&map) {
                warn!("Error sending keepalive: {:?}", why);
            }
        }
    }

    let _ = sender.get_mut().shutdown(Shutdown::Both);
}
<|endoftext|>


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

use crate::dom::audiobuffer::AudioBuffer;
use crate::dom::audioparam::AudioParam;
use crate::dom::audioscheduledsourcenode::AudioScheduledSourceNode;
use crate::dom::baseaudiocontext::BaseAudioContext;
use crate::dom::bindings::codegen::Bindings::AudioBufferSourceNodeBinding;
use crate::dom::bindings::codegen::Bindings::AudioBufferSourceNodeBinding::AudioBufferSourceNodeMethods;
use crate::dom::bindings::codegen::Bindings::AudioBufferSourceNodeBinding::AudioBufferSourceOptions;
use crate::dom::bindings::codegen::Bindings::AudioParamBinding::AutomationRate;
use crate::dom::bindings::codegen::Bindings::AudioScheduledSourceNodeBinding::AudioScheduledSourceNodeMethods;
use crate::dom::bindings::error::{Error, Fallible};
use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::num::Finite;
use crate::dom::bindings::reflector::reflect_dom_object;
use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom};
use crate::dom::window::Window;
use dom_struct::dom_struct;
use servo_media::audio::buffer_source_node::AudioBufferSourceNodeMessage;
use servo_media::audio::buffer_source_node::AudioBufferSourceNodeOptions;
use servo_media::audio::node::{AudioNodeInit, AudioNodeMessage};
use servo_media::audio::param::ParamType;
use std::cell::Cell;
use std::f32;

#[dom_struct]
pub struct AudioBufferSourceNode {
    source_node: AudioScheduledSourceNode,
    buffer: MutNullableDom<AudioBuffer>,
    buffer_set: Cell<bool>,
    playback_rate: Dom<AudioParam>,
    detune: Dom<AudioParam>,
    loop_enabled: Cell<bool>,
    loop_start: Cell<f64>,
    loop_end: Cell<f64>,
}

impl AudioBufferSourceNode {
    #[allow(unrooted_must_root)]
    fn new_inherited(
        window: &Window,
        context: &BaseAudioContext,
        options: &AudioBufferSourceOptions,
    ) -> Fallible<AudioBufferSourceNode> {
        let node_options = Default::default();
        let source_node = AudioScheduledSourceNode::new_inherited(
            AudioNodeInit::AudioBufferSourceNode(options.into()),
            context,
            node_options,
            0, /* inputs */
            1, /* outputs */
        )?;
        let node_id = source_node.node().node_id();
        let playback_rate = AudioParam::new(
            &window,
            context,
            node_id,
            ParamType::PlaybackRate,
            AutomationRate::K_rate,
            *options.playbackRate,
            f32::MIN,
            f32::MAX,
        );
        let detune = AudioParam::new(
            &window,
            context,
            node_id,
            ParamType::Detune,
            AutomationRate::K_rate,
            *options.detune,
            f32::MIN,
            f32::MAX,
        );
        let node = AudioBufferSourceNode {
            source_node,
            buffer: Default::default(),
            buffer_set: Cell::new(false),
            playback_rate: Dom::from_ref(&playback_rate),
            detune: Dom::from_ref(&detune),
            loop_enabled: Cell::new(options.loop_),
            loop_start: Cell::new(*options.loopStart),
            loop_end: Cell::new(*options.loopEnd),
        };
        if let Some(ref buffer) = options.buffer {
            if let Some(ref buffer) = buffer {
                if let Err(err) = node.SetBuffer(Some(&**buffer)) {
                    return Err(err);
                }
            }
        }
        Ok(node)
    }

    #[allow(unrooted_must_root)]
    pub fn new(
        window: &Window,
        context: &BaseAudioContext,
        options: &AudioBufferSourceOptions,
    ) -> Fallible<DomRoot<AudioBufferSourceNode>> {
        let node = AudioBufferSourceNode::new_inherited(window, context, options)?;
        Ok(reflect_dom_object(
            Box::new(node),
            window,
            AudioBufferSourceNodeBinding::Wrap,
        ))
    }

    pub fn Constructor(
        window: &Window,
        context: &BaseAudioContext,
        options: &AudioBufferSourceOptions,
    ) -> Fallible<DomRoot<AudioBufferSourceNode>> {
        AudioBufferSourceNode::new(window, context, options)
    }
}

impl AudioBufferSourceNodeMethods for AudioBufferSourceNode {
    // https://webaudio.github.io/web-audio-api/#dom-audiobuffersourcenode-buffer
    fn GetBuffer(&self) -> Fallible<Option<DomRoot<AudioBuffer>>> {
        Ok(self.buffer.get())
    }

    // https://webaudio.github.io/web-audio-api/#dom-audiobuffersourcenode-buffer
    fn SetBuffer(&self, new_buffer: Option<&AudioBuffer>) -> Fallible<()> {
        if new_buffer.is_some() {
            if self.buffer_set.get() {
                // Step 2.
                return Err(Error::InvalidState);
            }
            // Step 3.
            self.buffer_set.set(true);
        }

        // Step 4.
        self.buffer.set(new_buffer);

        // Step 5.
        if self.source_node.has_start() {
            if let Some(buffer) = self.buffer.get() {
                let buffer = buffer.get_channels();
                if buffer.is_some() {
                    self.source_node
                        .node()
                        .message(AudioNodeMessage::AudioBufferSourceNode(
                            AudioBufferSourceNodeMessage::SetBuffer((*buffer).clone()),
                        ));
                }
            }
        }

        Ok(())
    }

    // https://webaudio.github.io/web-audio-api/#dom-audiobuffersourcenode-playbackrate
    fn PlaybackRate(&self) -> DomRoot<AudioParam> {
        DomRoot::from_ref(&self.playback_rate)
    }

    // https://webaudio.github.io/web-audio-api/#dom-audiobuffersourcenode-detune
    fn Detune(&self) -> DomRoot<AudioParam> {
        DomRoot::from_ref(&self.detune)
    }

    // https://webaudio.github.io/web-audio-api/#dom-audiobuffersourcenode-loop
    fn Loop(&self) -> bool {
        self.loop_enabled.get()
    }

    // https://webaudio.github.io/web-audio-api/#dom-audiobuffersourcenode-loop
    fn SetLoop(&self, should_loop: bool) {
        self.loop_enabled.set(should_loop);
    }

    // https://webaudio.github.io/web-audio-api/#dom-audiobuffersourcenode-loopstart
    fn LoopStart(&self) -> Finite<f64> {
        Finite::wrap(self.loop_start.get())
    }

    // https://webaudio.github.io/web-audio-api/#dom-audiobuffersourcenode-loopstart
    fn SetLoopStart(&self, loop_start: Finite<f64>) {
        self.loop_start.set(*loop_start);
    }

    // https://webaudio.github.io/web-audio-api/#dom-audiobuffersourcenode-loopend
    fn LoopEnd(&self) -> Finite<f64> {
        Finite::wrap(self.loop_end.get())
    }

    // https://webaudio.github.io/web-audio-api/#dom-audiobuffersourcenode-loopend
    fn SetLoopEnd(&self, loop_end: Finite<f64>) {
        self.loop_end.set(*loop_end)
    }

    // https://webaudio.github.io/web-audio-api/#dom-audiobuffersourcenode-start
    fn Start(
        &self,
        when: Finite<f64>,
        offset: Option<Finite<f64>>,
        duration: Option<Finite<f64>>,
    ) -> Fallible<()> {
        if let Some(offset) = offset {
            if *offset < 0. {
                return Err(Error::Range("'offset' must be a positive value".to_owned()));
            }
        }

        if let Some(duration) = duration {
            if *duration < 0. {
                return Err(Error::Range(
                    "'duration' must be a positive value".to_owned(),
                ));
            }
        }

        if let Some(buffer) = self.buffer.get() {
            let buffer = buffer.get_channels();
            if buffer.is_some() {
                self.source_node
                    .node()
                    .message(AudioNodeMessage::AudioBufferSourceNode(
                        AudioBufferSourceNodeMessage::SetBuffer((*buffer).clone()),
                    ));
            }
        }

        self.source_node
            .upcast::<AudioScheduledSourceNode>()
            .Start(when)
    }
}

impl<'a> From<&'a AudioBufferSourceOptions> for AudioBufferSourceNodeOptions {
    fn from(options: &'a AudioBufferSourceOptions) -> Self {
        Self {
            buffer: if let Some(ref buffer) = options.buffer {
                if let Some(ref buffer) = buffer {
                    (*buffer.get_channels()).clone()
                } else {
                    None
                }
            } else {
                None
            },
            detune: *options.detune,
            loop_enabled: options.loop_,
            loop_end: Some(*options.loopEnd),
            loop_start: Some(*options.loopStart),
            playback_rate: *options.playbackRate,
        }
    }
}
<commit_msg>AudioBufferSourceNode: Support start parameters (offset, duration)<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */

use crate::dom::audiobuffer::AudioBuffer;
use crate::dom::audioparam::AudioParam;
use crate::dom::audioscheduledsourcenode::AudioScheduledSourceNode;
use crate::dom::baseaudiocontext::BaseAudioContext;
use crate::dom::bindings::codegen::Bindings::AudioBufferSourceNodeBinding;
use crate::dom::bindings::codegen::Bindings::AudioBufferSourceNodeBinding::AudioBufferSourceNodeMethods;
use crate::dom::bindings::codegen::Bindings::AudioBufferSourceNodeBinding::AudioBufferSourceOptions;
use crate::dom::bindings::codegen::Bindings::AudioParamBinding::AutomationRate;
use crate::dom::bindings::codegen::Bindings::AudioScheduledSourceNodeBinding::AudioScheduledSourceNodeMethods;
use crate::dom::bindings::error::{Error, Fallible};
use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::num::Finite;
use crate::dom::bindings::reflector::reflect_dom_object;
use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom};
use crate::dom::window::Window;
use dom_struct::dom_struct;
use servo_media::audio::buffer_source_node::AudioBufferSourceNodeMessage;
use servo_media::audio::buffer_source_node::AudioBufferSourceNodeOptions;
use servo_media::audio::node::{AudioNodeInit, AudioNodeMessage};
use servo_media::audio::param::ParamType;
use std::cell::Cell;
use std::f32;

#[dom_struct]
pub struct AudioBufferSourceNode {
    source_node: AudioScheduledSourceNode,
    buffer: MutNullableDom<AudioBuffer>,
    buffer_set: Cell<bool>,
    playback_rate: Dom<AudioParam>,
    detune: Dom<AudioParam>,
    loop_enabled: Cell<bool>,
    loop_start: Cell<f64>,
    loop_end: Cell<f64>,
}

impl AudioBufferSourceNode {
    #[allow(unrooted_must_root)]
    fn new_inherited(
        window: &Window,
        context: &BaseAudioContext,
        options: &AudioBufferSourceOptions,
    ) -> Fallible<AudioBufferSourceNode> {
        let node_options = Default::default();
        let source_node = AudioScheduledSourceNode::new_inherited(
            AudioNodeInit::AudioBufferSourceNode(options.into()),
            context,
            node_options,
            0, /* inputs */
            1, /* outputs */
        )?;
        let node_id = source_node.node().node_id();
        let playback_rate = AudioParam::new(
            &window,
            context,
            node_id,
            ParamType::PlaybackRate,
            AutomationRate::K_rate,
            *options.playbackRate,
            f32::MIN,
            f32::MAX,
        );
        let detune = AudioParam::new(
            &window,
            context,
            node_id,
            ParamType::Detune,
            AutomationRate::K_rate,
            *options.detune,
            f32::MIN,
            f32::MAX,
        );
        let node = AudioBufferSourceNode {
            source_node,
            buffer: Default::default(),
            buffer_set: Cell::new(false),
            playback_rate: Dom::from_ref(&playback_rate),
            detune: Dom::from_ref(&detune),
            loop_enabled: Cell::new(options.loop_),
            loop_start: Cell::new(*options.loopStart),
            loop_end: Cell::new(*options.loopEnd),
        };
        if let Some(ref buffer) = options.buffer {
            if let Some(ref buffer) = buffer {
                if let Err(err) = node.SetBuffer(Some(&**buffer)) {
                    return Err(err);
                }
            }
        }
        Ok(node)
    }

    #[allow(unrooted_must_root)]
    pub fn new(
        window: &Window,
        context: &BaseAudioContext,
        options: &AudioBufferSourceOptions,
    ) -> Fallible<DomRoot<AudioBufferSourceNode>> {
        let node = AudioBufferSourceNode::new_inherited(window, context, options)?;
        Ok(reflect_dom_object(
            Box::new(node),
            window,
            AudioBufferSourceNodeBinding::Wrap,
        ))
    }

    pub fn Constructor(
        window: &Window,
        context: &BaseAudioContext,
        options: &AudioBufferSourceOptions,
    ) -> Fallible<DomRoot<AudioBufferSourceNode>> {
        AudioBufferSourceNode::new(window, context, options)
    }
}

impl AudioBufferSourceNodeMethods for AudioBufferSourceNode {
    // https://webaudio.github.io/web-audio-api/#dom-audiobuffersourcenode-buffer
    fn GetBuffer(&self) -> Fallible<Option<DomRoot<AudioBuffer>>> {
        Ok(self.buffer.get())
    }

    // https://webaudio.github.io/web-audio-api/#dom-audiobuffersourcenode-buffer
    fn SetBuffer(&self, new_buffer: Option<&AudioBuffer>) -> Fallible<()> {
        if new_buffer.is_some() {
            if self.buffer_set.get() {
                // Step 2.
                return Err(Error::InvalidState);
            }
            // Step 3.
            self.buffer_set.set(true);
        }

        // Step 4.
        self.buffer.set(new_buffer);

        // Step 5.
        if self.source_node.has_start() {
            if let Some(buffer) = self.buffer.get() {
                let buffer = buffer.get_channels();
                if buffer.is_some() {
                    self.source_node
                        .node()
                        .message(AudioNodeMessage::AudioBufferSourceNode(
                            AudioBufferSourceNodeMessage::SetBuffer((*buffer).clone()),
                        ));
                }
            }
        }

        Ok(())
    }

    // https://webaudio.github.io/web-audio-api/#dom-audiobuffersourcenode-playbackrate
    fn PlaybackRate(&self) -> DomRoot<AudioParam> {
        DomRoot::from_ref(&self.playback_rate)
    }

    // https://webaudio.github.io/web-audio-api/#dom-audiobuffersourcenode-detune
    fn Detune(&self) -> DomRoot<AudioParam> {
        DomRoot::from_ref(&self.detune)
    }

    // https://webaudio.github.io/web-audio-api/#dom-audiobuffersourcenode-loop
    fn Loop(&self) -> bool {
        self.loop_enabled.get()
    }

    // https://webaudio.github.io/web-audio-api/#dom-audiobuffersourcenode-loop
    fn SetLoop(&self, should_loop: bool) {
        self.loop_enabled.set(should_loop);
    }

    // https://webaudio.github.io/web-audio-api/#dom-audiobuffersourcenode-loopstart
    fn LoopStart(&self) -> Finite<f64> {
        Finite::wrap(self.loop_start.get())
    }

    // https://webaudio.github.io/web-audio-api/#dom-audiobuffersourcenode-loopstart
    fn SetLoopStart(&self, loop_start: Finite<f64>) {
        self.loop_start.set(*loop_start);
    }

    // https://webaudio.github.io/web-audio-api/#dom-audiobuffersourcenode-loopend
    fn LoopEnd(&self) -> Finite<f64> {
        Finite::wrap(self.loop_end.get())
    }

    // https://webaudio.github.io/web-audio-api/#dom-audiobuffersourcenode-loopend
    fn SetLoopEnd(&self, loop_end: Finite<f64>) {
        self.loop_end.set(*loop_end)
    }

    // https://webaudio.github.io/web-audio-api/#dom-audiobuffersourcenode-start
    fn Start(
        &self,
        when: Finite<f64>,
        offset: Option<Finite<f64>>,
        duration: Option<Finite<f64>>,
    ) -> Fallible<()> {
        if let Some(offset) = offset {
            if *offset < 0. {
                return Err(Error::Range("'offset' must be a positive value".to_owned()));
            }
        }

        if let Some(duration) = duration {
            if *duration < 0. {
                return Err(Error::Range(
                    "'duration' must be a positive value".to_owned(),
                ));
            }
        }

        if let Some(buffer) = self.buffer.get() {
            let buffer = buffer.get_channels();
            if buffer.is_some() {
                self.source_node
                    .node()
                    .message(AudioNodeMessage::AudioBufferSourceNode(
                        AudioBufferSourceNodeMessage::SetBuffer((*buffer).clone()),
                    ));
            }
        }

        self.source_node
            .node()
            .message(AudioNodeMessage::AudioBufferSourceNode(
                AudioBufferSourceNodeMessage::Start(
                    *when,
                    offset.map(|f| *f),
                    duration.map(|f| *f),
                ),
            ));

        self.source_node
            .upcast::<AudioScheduledSourceNode>()
            .Start(when)
    }
}

impl<'a> From<&'a AudioBufferSourceOptions> for AudioBufferSourceNodeOptions {
    fn from(options: &'a AudioBufferSourceOptions) -> Self {
        Self {
            buffer: if let Some(ref buffer) = options.buffer {
                if let Some(ref buffer) = buffer {
                    (*buffer.get_channels()).clone()
                } else {
                    None
                }
            } else {
                None
            },
            detune: *options.detune,
            loop_enabled: options.loop_,
            loop_end: Some(*options.loopEnd),
            loop_start: Some(*options.loopStart),
            playback_rate: *options.playbackRate,
        }
    }
}
<|endoftext|>


<commit_before>//! Common Subexpression Elimination (CSE)
//!
//! This module implements methods and structs to perform CSE.
//! However, this implement doesn't consider much about commutative
//! opcodes. On the other hand, considering too much will cause
//! a huge memory consume. Thus, a balanced solution should be
//! improved.

use std::collections::HashMap;
use std::marker::PhantomData;

use middle::ssa::ssa_traits::{NodeType, SSA, SSAMod, SSAWalk};
use middle::ir::MOpcode;
use super::ssasort::Sorter;

#[derive(Debug)]
pub struct CSE<'a, I, S>
    where I: Iterator<Item = S::ValueRef>,
          S: 'a + SSAMod + SSA + SSAWalk<I>
{
    exprs: HashMap<String, Vec<S::ValueRef>>,
    hashed: HashMap<S::ValueRef, String>,
    ssa: &'a mut S,
    foo: PhantomData<I>,
}

impl<'a, I, S> CSE<'a, I, S>
where I: Iterator<Item = S::ValueRef>,
      S: 'a + SSAMod + SSA + SSAWalk<I>
{
    pub fn new(ssa: &'a mut S) -> CSE<'a, I, S> {
        CSE {
            exprs: HashMap::new(),
            hashed: HashMap::new(),
            ssa: ssa,
            foo: PhantomData,
        }
    }

    fn hash_args(&self, args: &[S::ValueRef]) -> String {
        let mut result = String::new();
        for arg in args {
            if let Ok(node_data) = self.ssa.node_data(*arg) {
                match node_data.nt {
                    NodeType::Op(opc) => {
                        match opc {
                            MOpcode::OpConst(val) => result.push_str(&format!("{}", val)),
                            _ => result.push_str(self.hashed.get(arg).expect("Hash value not found!")),
                        }
                    }
                    _ => {
                        // NOTE: Phi functions and Comment node should be considered, otherwise,
                        // CSE will replace different op nodes, which only use phi/comment as
                        // operands, causing wrong analysis. Meanwhile, we take Invalid node
                        // into account, for it may not influence the answer. Actually, till
                        // now, there is no possibility to have an Invalid node.
                        // Attantion: any call node will cause a lot of Comment nodes as it return,
                        // to symbolize every register.
                        result.push_str(&format!("{:?}", arg));
                    }
                }
            }
        }
        result
    }

    // NOTE: Because we have sorted the operands, it's unnecessary to consider commutative opcodes.
    fn hash_string(&self, idx: &S::ValueRef) -> Option<String> {
        if let Ok(node_data) = self.ssa.node_data(*idx) {
            if let NodeType::Op(opc) = node_data.nt {
                let args = self.ssa.operands_of(*idx);
                let hashed_args = self.hash_args(&args);
                let hs = format!("{}{}", opc, hashed_args);
                return Some(hs);
            }
        }
        None
    }

    pub fn run(&mut self) {
        {
            let mut sorter = Sorter::new(self.ssa);
            sorter.run();
        }
        // Sort the operands for commutative opcode first;

        for expr in self.ssa.inorder_walk() {
            let hs = match self.hash_string(&expr) {
                Some(hs) => hs,
                None => continue,
            };
            let mut replaced = false;
            if let Some(ex_idxs) = self.exprs.get(&hs).cloned() {
                // NOTE: Though we can eliminate expression even if the two aren't in the same
                // block, we do not do so cause it requires that block b1 (block of the
                // replacer) must dominate block b2 (the replacee). Since we are currently not
                // using the dominator information, this replacement may not be safe. Hence we
                // restrict outselves to the case where both the expressions belong to the same
                // block.
                for ex_idx in &ex_idxs {
                    if self.ssa.block_for(*ex_idx) == self.ssa.block_for(expr) {
                        self.ssa.replace_value(expr, *ex_idx);
                        replaced = true;
                        break;
                    }
                }
            }

            if !replaced {
                self.exprs.entry(hs.clone()).or_insert_with(Vec::new).push(expr);
                self.hashed.insert(expr, hs.clone());
            }
        }
    }
}
<commit_msg>Fix panic<commit_after>//! Common Subexpression Elimination (CSE)
//!
//! This module implements methods and structs to perform CSE.
//! However, this implement doesn't consider much about commutative
//! opcodes. On the other hand, considering too much will cause
//! a huge memory consume. Thus, a balanced solution should be
//! improved.

use std::fmt::Display;
use std::collections::HashMap;
use std::marker::PhantomData;

use middle::ssa::ssa_traits::{NodeType, SSA, SSAMod, SSAWalk};
use middle::ir::MOpcode;
use super::ssasort::Sorter;

#[derive(Debug)]
pub struct CSE<'a, I, S>
    where I: Iterator<Item = S::ValueRef>,
          S: 'a + SSAMod + SSA + SSAWalk<I>
{
    exprs: HashMap<String, Vec<S::ValueRef>>,
    hashed: HashMap<S::ValueRef, String>,
    ssa: &'a mut S,
    foo: PhantomData<I>,
}

impl<'a, I, S> CSE<'a, I, S>
where I: Iterator<Item = S::ValueRef>,
      S: 'a + SSAMod + SSA + SSAWalk<I>
{
    pub fn new(ssa: &'a mut S) -> CSE<'a, I, S> {
        CSE {
            exprs: HashMap::new(),
            hashed: HashMap::new(),
            ssa: ssa,
            foo: PhantomData,
        }
    }

    fn hash_args(&self, args: &[S::ValueRef]) -> String {
        let mut result = String::new();
        for arg in args {
            if let Ok(node_data) = self.ssa.node_data(*arg) {
                match node_data.nt {
                    NodeType::Op(opc) => {
                        match opc {
                            MOpcode::OpConst(val) => result.push_str(&format!("{}", val)),
                            _ => {
                                if let Some(hash) = self.hashed.get(arg) {
                                    result.push_str(hash);
                                } else {
                                    radeco_err!("Hash value not found!");
                                    result.push_str(&format!("{:?}", arg));
                                }
                            },
                        }
                    }
                    _ => {
                        // NOTE: Phi functions and Comment node should be considered, otherwise,
                        // CSE will replace different op nodes, which only use phi/comment as
                        // operands, causing wrong analysis. Meanwhile, we take Invalid node
                        // into account, for it may not influence the answer. Actually, till
                        // now, there is no possibility to have an Invalid node.
                        // Attantion: any call node will cause a lot of Comment nodes as it return,
                        // to symbolize every register.
                        result.push_str(&format!("{:?}", arg));
                    }
                }
            }
        }
        result
    }

    // NOTE: Because we have sorted the operands, it's unnecessary to consider commutative opcodes.
    fn hash_string(&self, idx: &S::ValueRef) -> Option<String> {
        if let Ok(node_data) = self.ssa.node_data(*idx) {
            if let NodeType::Op(opc) = node_data.nt {
                let args = self.ssa.operands_of(*idx);
                let hashed_args = self.hash_args(&args);
                let hs = format!("{}{}", opc, hashed_args);
                return Some(hs);
            }
        }
        None
    }

    pub fn run(&mut self) {
        {
            let mut sorter = Sorter::new(self.ssa);
            sorter.run();
        }
        // Sort the operands for commutative opcode first;

        for expr in self.ssa.inorder_walk() {
            let hs = match self.hash_string(&expr) {
                Some(hs) => hs,
                None => continue,
            };
            let mut replaced = false;
            if let Some(ex_idxs) = self.exprs.get(&hs).cloned() {
                // NOTE: Though we can eliminate expression even if the two aren't in the same
                // block, we do not do so cause it requires that block b1 (block of the
                // replacer) must dominate block b2 (the replacee). Since we are currently not
                // using the dominator information, this replacement may not be safe. Hence we
                // restrict outselves to the case where both the expressions belong to the same
                // block.
                for ex_idx in &ex_idxs {
                    if self.ssa.block_for(*ex_idx) == self.ssa.block_for(expr) {
                        self.ssa.replace_value(expr, *ex_idx);
                        replaced = true;
                        break;
                    }
                }
            }

            if !replaced {
                self.exprs.entry(hs.clone()).or_insert_with(Vec::new).push(expr);
                self.hashed.insert(expr, hs.clone());
            }
        }
    }
}
<|endoftext|>


<commit_before>use super::{imports::Imports, trampolines};
use crate::{
    analysis::trampolines::Trampoline,
    config::{self, gobjects::GObject},
    env::Env,
    library, nameutil,
    traits::*,
    version::Version,
};

#[derive(Debug)]
pub struct Info {
    pub connect_name: String,
    pub signal_name: String,
    pub action_emit_name: Option<String>,
    pub trampoline: Result<Trampoline, Vec<String>>,
    pub version: Option<Version>,
    pub deprecated_version: Option<Version>,
    pub doc_hidden: bool,
}

pub fn analyze(
    env: &Env,
    signals: &[library::Signal],
    type_tid: library::TypeId,
    in_trait: bool,
    obj: &GObject,
    imports: &mut Imports,
) -> Vec<Info> {
    let mut sns = Vec::new();

    for signal in signals {
        let configured_signals = obj.signals.matched(&signal.name);
        if !configured_signals.iter().all(|f| f.status.need_generate()) {
            continue;
        }
        if env.is_totally_deprecated(signal.deprecated_version) {
            continue;
        }

        let info = analyze_signal(
            env,
            signal,
            type_tid,
            in_trait,
            &configured_signals,
            obj,
            imports,
        );
        sns.push(info);
    }

    sns
}

fn analyze_signal(
    env: &Env,
    signal: &library::Signal,
    type_tid: library::TypeId,
    in_trait: bool,
    configured_signals: &[&config::signals::Signal],
    obj: &GObject,
    imports: &mut Imports,
) -> Info {
    let mut used_types: Vec<String> = Vec::with_capacity(4);
    let version = configured_signals
        .iter()
        .filter_map(|f| f.version)
        .min()
        .or(signal.version);
    let deprecated_version = signal.deprecated_version;
    let doc_hidden = configured_signals.iter().any(|f| f.doc_hidden);

    let imports = &mut imports.with_defaults(version, &None);

    let connect_name = format!("connect_{}", nameutil::signal_to_snake(&signal.name));
    let trampoline = trampolines::analyze(
        env,
        signal,
        type_tid,
        in_trait,
        configured_signals,
        obj,
        &mut used_types,
        version,
    );

    let action_emit_name = if signal.is_action {
        imports.add("glib::object::ObjectExt");
        Some(format!("emit_{}", nameutil::signal_to_snake(&signal.name)))
    } else {
        None
    };

    if trampoline.is_ok() {
        imports.add_used_types(&used_types);
        if in_trait {
            imports.add("glib::object::Cast");
        } else {
            //To resolve a conflict with OSTree::ObjectType
            imports.add("glib::object::ObjectType as ObjectType_");
        }
        imports.add("glib::signal::connect_raw");
        imports.add("glib::signal::SignalHandlerId");
        imports.add("std::mem::transmute");
        imports.add("std::boxed::Box as Box_");
    }

    let info = Info {
        connect_name,
        signal_name: signal.name.clone(),
        trampoline,
        action_emit_name,
        version,
        deprecated_version,
        doc_hidden,
    };

    info
}
<commit_msg>analysis/signals: Explicitly import glib::translate<commit_after>use super::{imports::Imports, trampolines};
use crate::{
    analysis::trampolines::Trampoline,
    config::{self, gobjects::GObject},
    env::Env,
    library, nameutil,
    traits::*,
    version::Version,
};

#[derive(Debug)]
pub struct Info {
    pub connect_name: String,
    pub signal_name: String,
    pub action_emit_name: Option<String>,
    pub trampoline: Result<Trampoline, Vec<String>>,
    pub version: Option<Version>,
    pub deprecated_version: Option<Version>,
    pub doc_hidden: bool,
}

pub fn analyze(
    env: &Env,
    signals: &[library::Signal],
    type_tid: library::TypeId,
    in_trait: bool,
    obj: &GObject,
    imports: &mut Imports,
) -> Vec<Info> {
    let mut sns = Vec::new();

    for signal in signals {
        let configured_signals = obj.signals.matched(&signal.name);
        if !configured_signals.iter().all(|f| f.status.need_generate()) {
            continue;
        }
        if env.is_totally_deprecated(signal.deprecated_version) {
            continue;
        }

        let info = analyze_signal(
            env,
            signal,
            type_tid,
            in_trait,
            &configured_signals,
            obj,
            imports,
        );
        sns.push(info);
    }

    sns
}

fn analyze_signal(
    env: &Env,
    signal: &library::Signal,
    type_tid: library::TypeId,
    in_trait: bool,
    configured_signals: &[&config::signals::Signal],
    obj: &GObject,
    imports: &mut Imports,
) -> Info {
    let mut used_types: Vec<String> = Vec::with_capacity(4);
    let version = configured_signals
        .iter()
        .filter_map(|f| f.version)
        .min()
        .or(signal.version);
    let deprecated_version = signal.deprecated_version;
    let doc_hidden = configured_signals.iter().any(|f| f.doc_hidden);

    let imports = &mut imports.with_defaults(version, &None);
    imports.add("glib::translate::*");

    let connect_name = format!("connect_{}", nameutil::signal_to_snake(&signal.name));
    let trampoline = trampolines::analyze(
        env,
        signal,
        type_tid,
        in_trait,
        configured_signals,
        obj,
        &mut used_types,
        version,
    );

    let action_emit_name = if signal.is_action {
        imports.add("glib::object::ObjectExt");
        Some(format!("emit_{}", nameutil::signal_to_snake(&signal.name)))
    } else {
        None
    };

    if trampoline.is_ok() {
        imports.add_used_types(&used_types);
        if in_trait {
            imports.add("glib::object::Cast");
        } else {
            //To resolve a conflict with OSTree::ObjectType
            imports.add("glib::object::ObjectType as ObjectType_");
        }
        imports.add("glib::signal::connect_raw");
        imports.add("glib::signal::SignalHandlerId");
        imports.add("std::mem::transmute");
        imports.add("std::boxed::Box as Box_");
    }

    let info = Info {
        connect_name,
        signal_name: signal.name.clone(),
        trampoline,
        action_emit_name,
        version,
        deprecated_version,
        doc_hidden,
    };

    info
}
<|endoftext|>


<commit_before>use std::iter::Peekable;

use ::opcodes::{AddressingMode, OpCode};
use assembler::token::Token;

#[derive(Debug, PartialEq)]
pub struct ParserError {
    message: String,
}

impl ParserError {
    fn expected_instruction(line: u32) -> ParserError {
        ParserError::from(format!("Instruction expected. Line {}", line))
    }

    fn invalid_opcode_addressing_mode_combination(line: u32) -> ParserError {
        ParserError::from(format!("Invalid addressing mode for opcode. Line {}", line))
    }

    fn unexpected_eol(line: u32) -> ParserError {
        ParserError::from(format!("Unexpected end of line. Line {}", line))
    }

    fn expected_eol(line: u32) -> ParserError {
        ParserError::from(format!("Expected end of line. Line {}", line))
    }
}

impl From<String> for ParserError {
    fn from(error: String) -> ParserError {
        ParserError { message: error }
    }
}

impl<'a> From<&'a str> for ParserError {
    fn from(error: &str) -> ParserError {
        ParserError { message: error.into() }
    }
}

pub struct Parser {
    tokens: Vec<Vec<Token>>,
    line: u32,
}

/// Parser processes a list of 6502 Assembly tokens
impl Parser {
    pub fn new(tokens: Vec<Vec<Token>>) -> Parser {
        Parser {
            tokens: tokens,
            line: 0,
        }
    }

    /// Processes its tokens and either returns them to the caller
    /// or produces an error
    pub fn parse(&mut self) -> Result<Vec<Vec<Token>>, ParserError> {
        let successful_result = self.tokens.iter().map(|v| v.clone()).collect();

        for line in &self.tokens {
            self.line += 1;
            let mut peeker = line.iter().peekable();
            // Check what starts the line
            let token = peeker.peek().unwrap().clone();

            match *token {
                Token::Label(_) => {
                    // if its a label, consume it and move on
                    peeker.next();
                    if let None = peeker.peek() {
                        // If its just a label, thats fine
                        return Ok(successful_result);
                    }
                    let next = *peeker.peek().unwrap();
                    if let &Token::OpCode(ref mnemonic) = next {
                        peeker.next();
                        Self::handle_opcode(&mut peeker, mnemonic, self.line)?;
                    } else {
                        return Err(ParserError::expected_instruction(self.line));
                    }
                }
                Token::OpCode(ref mnemonic) => {
                    peeker.next();
                    Self::handle_opcode(&mut peeker, mnemonic, self.line)?;
                }
                _ => (),
            }
        }

        Ok(successful_result)
    }

    fn handle_opcode<'a, I>(mut peeker: &mut Peekable<I>,
                            mnemonic: &str,
                            line: u32)
                            -> Result<(), ParserError>
        where I: Iterator<Item = &'a Token>
    {
        // Determine an addressing mode that was attempted,
        let addressing_mode = if let None = peeker.peek() {
            // if there was no token, then its implied
            AddressingMode::Implied
        } else {
            // Otherwise, lets try and convert
            // the next token to a mode
            let next = *peeker.peek().unwrap();
            // Skip the argument
            peeker.next();
            next.to_addressing_mode()
        };

        if let Some(opcode) = OpCode::from_mnemonic_and_addressing_mode(mnemonic.clone(),
                                                                        addressing_mode) {
            // There should be nothing else now:
            if let None = peeker.peek() {
                Ok(())
            } else {
                Err(ParserError::expected_eol(line))
            }
        } else {
            Err(ParserError::invalid_opcode_addressing_mode_combination(line))
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use ::assembler::token::{ImmediateBase, Token};

    #[test]
    fn errors_on_multiple_labels() {
        let mut parser = Parser::new(vec![vec![Token::Label("MAIN".into()),
                                               Token::Label("METHOD".into()),
                                               Token::OpCode("LDA".into()),
                                               Token::Immediate("10".into(),
                                                                ImmediateBase::Base16)]]);

        assert_eq!(Err(ParserError::expected_instruction(1)), parser.parse());
    }

    #[test]
    fn does_not_error_on_single_label() {
        let mut parser = Parser::new(vec![vec![Token::Label("MAIN".into()),
                                               Token::OpCode("LDA".into()),
                                               Token::Immediate("10".into(),
                                                                ImmediateBase::Base16)]]);

        assert_eq!(&[Token::Label("MAIN".into()),
                     Token::OpCode("LDA".into()),
                     Token::Immediate("10".into(), ImmediateBase::Base16)],
                   &parser.parse().unwrap()[0][..]);
    }

    #[test]
    fn can_detect_invalid_addressing_modes() {
        let mut parser = Parser::new(vec![vec![Token::Label("MAIN".into()),
                                               Token::OpCode("LDX".into()),
                                               Token::IndirectY("10".into())]]);

        assert_eq!(Err(ParserError::invalid_opcode_addressing_mode_combination(1)),
                   parser.parse());
    }

    #[test]
    fn does_not_error_on_valid_addressing_modes() {
        let mut parser = Parser::new(vec![vec![Token::Label("MAIN".into()),
                                               Token::OpCode("LDA".into()),
                                               Token::IndirectY("10".into())]]);

        let result = parser.parse().unwrap();
        assert_eq!(&[Token::Label("MAIN".into()),
                     Token::OpCode("LDA".into()),
                     Token::IndirectY("10".into())],
                   &result[0][..]);
    }

    #[test]
    fn does_not_error_on_label_only_line() {
        let mut parser = Parser::new(vec![vec![Token::Label("MAIN".into())],
                                          vec![Token::OpCode("LDA".into()),
                                               Token::Absolute("4400".into())]]);

        let result = parser.parse().unwrap();

        assert_eq!(&[Token::Label("MAIN".into())], &result[0][..]);
        assert_eq!(&[Token::OpCode("LDA".into()), Token::Absolute("4400".into())],
                   &result[1][..]);
    }

    #[test]
    fn does_not_error_on_implied_addressing_mode() {
        let mut parser = Parser::new(vec![vec![Token::OpCode("NOP".into())],
                                          vec![Token::OpCode("BRK".into())],
                                          vec![Token::OpCode("CLC".into())]]);

        let result = parser.parse().unwrap();

        assert_eq!(&[Token::OpCode("NOP".into())], &result[0][..]);
        assert_eq!(&[Token::OpCode("BRK".into())], &result[1][..]);
        assert_eq!(&[Token::OpCode("CLC".into())], &result[2][..]);
    }

    #[test]
    fn does_error_on_implied_addressing_mode_opcodes_that_have_arguments() {
        let mut parser = Parser::new(vec![vec![Token::OpCode("NOP".into()),
                                               Token::Absolute("4400".into())]]);

        assert_eq!(Err(ParserError::invalid_opcode_addressing_mode_combination(1)),
                   parser.parse());
    }

    #[test]
    fn does_error_when_multiple_instructions_are_not_split_across_lines() {
        let mut parser = Parser::new(vec![vec![Token::OpCode("LDA".into()),
                                               Token::Absolute("4400".into()),
                                               Token::OpCode("LDA".into()),
                                               Token::Absolute("4400".into()),
                                            ]]);

        assert_eq!(Err(ParserError::expected_eol(1)), parser.parse());
    }
}<commit_msg>Rename 'handle_opcode' to 'validate_opcode'<commit_after>use std::iter::Peekable;

use ::opcodes::{AddressingMode, OpCode};
use assembler::token::Token;

#[derive(Debug, PartialEq)]
pub struct ParserError {
    message: String,
}

impl ParserError {
    fn expected_instruction(line: u32) -> ParserError {
        ParserError::from(format!("Instruction expected. Line {}", line))
    }

    fn invalid_opcode_addressing_mode_combination(line: u32) -> ParserError {
        ParserError::from(format!("Invalid addressing mode for opcode. Line {}", line))
    }

    fn unexpected_eol(line: u32) -> ParserError {
        ParserError::from(format!("Unexpected end of line. Line {}", line))
    }

    fn expected_eol(line: u32) -> ParserError {
        ParserError::from(format!("Expected end of line. Line {}", line))
    }
}

impl From<String> for ParserError {
    fn from(error: String) -> ParserError {
        ParserError { message: error }
    }
}

impl<'a> From<&'a str> for ParserError {
    fn from(error: &str) -> ParserError {
        ParserError { message: error.into() }
    }
}

pub struct Parser {
    tokens: Vec<Vec<Token>>,
    line: u32,
}

/// Parser processes a list of 6502 Assembly tokens
impl Parser {
    pub fn new(tokens: Vec<Vec<Token>>) -> Parser {
        Parser {
            tokens: tokens,
            line: 0,
        }
    }

    /// Processes its tokens and either returns them to the caller
    /// or produces an error
    pub fn parse(&mut self) -> Result<Vec<Vec<Token>>, ParserError> {
        let successful_result = self.tokens.iter().map(|v| v.clone()).collect();

        for line in &self.tokens {
            self.line += 1;
            let mut peeker = line.iter().peekable();
            // Check what starts the line
            let token = peeker.peek().unwrap().clone();

            match *token {
                Token::Label(_) => {
                    // if its a label, consume it and move on
                    peeker.next();
                    if let None = peeker.peek() {
                        // If its just a label, thats fine
                        return Ok(successful_result);
                    }
                    let next = *peeker.peek().unwrap();
                    if let &Token::OpCode(ref mnemonic) = next {
                        peeker.next();
                        Self::validate_opcode(&mut peeker, mnemonic, self.line)?;
                    } else {
                        return Err(ParserError::expected_instruction(self.line));
                    }
                }
                Token::OpCode(ref mnemonic) => {
                    peeker.next();
                    Self::validate_opcode(&mut peeker, mnemonic, self.line)?;
                }
                _ => (),
            }
        }

        Ok(successful_result)
    }

    fn validate_opcode<'a, I>(mut peeker: &mut Peekable<I>,
                              mnemonic: &str,
                              line: u32)
                              -> Result<(), ParserError>
        where I: Iterator<Item = &'a Token>
    {
        // Determine an addressing mode that was attempted,
        let addressing_mode = if let None = peeker.peek() {
            // if there was no token, then its implied
            AddressingMode::Implied
        } else {
            // Otherwise, lets try and convert
            // the next token to a mode
            let next = *peeker.peek().unwrap();
            // Skip the argument
            peeker.next();
            next.to_addressing_mode()
        };

        if let Some(opcode) = OpCode::from_mnemonic_and_addressing_mode(mnemonic.clone(),
                                                                        addressing_mode) {
            // There should be nothing else now:
            if let None = peeker.peek() {
                Ok(())
            } else {
                Err(ParserError::expected_eol(line))
            }
        } else {
            Err(ParserError::invalid_opcode_addressing_mode_combination(line))
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use ::assembler::token::{ImmediateBase, Token};

    #[test]
    fn errors_on_multiple_labels() {
        let mut parser = Parser::new(vec![vec![Token::Label("MAIN".into()),
                                               Token::Label("METHOD".into()),
                                               Token::OpCode("LDA".into()),
                                               Token::Immediate("10".into(),
                                                                ImmediateBase::Base16)]]);

        assert_eq!(Err(ParserError::expected_instruction(1)), parser.parse());
    }

    #[test]
    fn does_not_error_on_single_label() {
        let mut parser = Parser::new(vec![vec![Token::Label("MAIN".into()),
                                               Token::OpCode("LDA".into()),
                                               Token::Immediate("10".into(),
                                                                ImmediateBase::Base16)]]);

        assert_eq!(&[Token::Label("MAIN".into()),
                     Token::OpCode("LDA".into()),
                     Token::Immediate("10".into(), ImmediateBase::Base16)],
                   &parser.parse().unwrap()[0][..]);
    }

    #[test]
    fn can_detect_invalid_addressing_modes() {
        let mut parser = Parser::new(vec![vec![Token::Label("MAIN".into()),
                                               Token::OpCode("LDX".into()),
                                               Token::IndirectY("10".into())]]);

        assert_eq!(Err(ParserError::invalid_opcode_addressing_mode_combination(1)),
                   parser.parse());
    }

    #[test]
    fn does_not_error_on_valid_addressing_modes() {
        let mut parser = Parser::new(vec![vec![Token::Label("MAIN".into()),
                                               Token::OpCode("LDA".into()),
                                               Token::IndirectY("10".into())]]);

        let result = parser.parse().unwrap();
        assert_eq!(&[Token::Label("MAIN".into()),
                     Token::OpCode("LDA".into()),
                     Token::IndirectY("10".into())],
                   &result[0][..]);
    }

    #[test]
    fn does_not_error_on_label_only_line() {
        let mut parser = Parser::new(vec![vec![Token::Label("MAIN".into())],
                                          vec![Token::OpCode("LDA".into()),
                                               Token::Absolute("4400".into())]]);

        let result = parser.parse().unwrap();

        assert_eq!(&[Token::Label("MAIN".into())], &result[0][..]);
        assert_eq!(&[Token::OpCode("LDA".into()), Token::Absolute("4400".into())],
                   &result[1][..]);
    }

    #[test]
    fn does_not_error_on_implied_addressing_mode() {
        let mut parser = Parser::new(vec![vec![Token::OpCode("NOP".into())],
                                          vec![Token::OpCode("BRK".into())],
                                          vec![Token::OpCode("CLC".into())]]);

        let result = parser.parse().unwrap();

        assert_eq!(&[Token::OpCode("NOP".into())], &result[0][..]);
        assert_eq!(&[Token::OpCode("BRK".into())], &result[1][..]);
        assert_eq!(&[Token::OpCode("CLC".into())], &result[2][..]);
    }

    #[test]
    fn does_error_on_implied_addressing_mode_opcodes_that_have_arguments() {
        let mut parser = Parser::new(vec![vec![Token::OpCode("NOP".into()),
                                               Token::Absolute("4400".into())]]);

        assert_eq!(Err(ParserError::invalid_opcode_addressing_mode_combination(1)),
                   parser.parse());
    }

    #[test]
    fn does_error_when_multiple_instructions_are_not_split_across_lines() {
        let mut parser = Parser::new(vec![vec![Token::OpCode("LDA".into()),
                                               Token::Absolute("4400".into()),
                                               Token::OpCode("LDA".into()),
                                               Token::Absolute("4400".into()),
                                            ]]);

        assert_eq!(Err(ParserError::expected_eol(1)), parser.parse());
    }
}<|endoftext|>


<commit_before>//! Implementation of input backend trait for types provided by `libinput`

use backend::SeatInternal;
use backend::input::{InputBackend, InputHandler, Seat, SeatCapabilities};
use input::{Libinput, Device, Seat as LibinputSeat, DeviceCapability};
use input::event::*;

use std::io::Error as IoError;
use std::collections::hash_map::{DefaultHasher, HashMap};
use std::hash::{Hash, Hasher};

pub struct LibinputInputBackend {
    context: Libinput,
    devices: Vec<Device>,
    seats: HashMap<LibinputSeat, Seat>,
    handler: Option<Box<InputHandler<LibinputInputBackend> + 'static>>,
}

impl InputBackend for LibinputInputBackend {
    type InputConfig = [Device];
    type EventError = IoError;

    fn set_handler<H: InputHandler<Self> + 'static>(&mut self, mut handler: H) {
        if self.handler.is_some() {
            self.clear_handler();
        }
        for seat in self.seats.values() {
            handler.on_seat_created(&seat);
        }
        self.handler = Some(Box::new(handler));
    }

    fn get_handler(&mut self) -> Option<&mut InputHandler<Self>> {
        self.handler
            .as_mut()
            .map(|handler| handler as &mut InputHandler<Self>)
    }

    fn clear_handler(&mut self) {
        if let Some(mut handler) = self.handler.take() {
            for seat in self.seats.values() {
                handler.on_seat_destroyed(&seat);
            }
        }
    }

    fn input_config(&mut self) -> &mut Self::InputConfig {
        &mut self.devices
    }

    fn set_cursor_position(&mut self, _x: u32, _y: u32) -> Result<(), ()> {
        // FIXME later.
        // This will be doable with the hardware cursor api and probably some more cases
        Err(())
    }

    fn dispatch_new_events(&mut self) -> Result<(), IoError> {
        self.context.dispatch()?;
        for event in &mut self.context {
            match event {
                Event::Device(device_event) => {
                    use input::event::device::*;
                    match device_event {
                        DeviceEvent::Added(device_added_event) => {
                            let added = device_added_event.into_event().device();

                            let new_caps = SeatCapabilities {
                                pointer: added.has_capability(DeviceCapability::Pointer),
                                keyboard: added.has_capability(DeviceCapability::Keyboard),
                                touch: added.has_capability(DeviceCapability::Touch),
                            };

                            let device_seat = added.seat();
                            self.devices.push(added);

                            let contains = self.seats.contains_key(&device_seat);
                            if contains {
                                let old_seat = self.seats.get_mut(&device_seat).unwrap();
                                {
                                    let caps = old_seat.capabilities_mut();
                                    caps.pointer = new_caps.pointer || caps.pointer;
                                    caps.keyboard = new_caps.keyboard || caps.keyboard;
                                    caps.touch = new_caps.touch || caps.touch;
                                }
                                if let Some(ref mut handler) = self.handler {
                                    handler.on_seat_changed(old_seat);
                                }
                            } else {
                                let mut hasher = DefaultHasher::default();
                                device_seat.hash(&mut hasher);
                                self.seats.insert(device_seat.clone(), Seat::new(hasher.finish(), new_caps));
                                if let Some(ref mut handler) = self.handler {
                                    handler.on_seat_created(self.seats.get(&device_seat).unwrap());
                                }
                            }
                        },
                        DeviceEvent::Removed(device_removed_event) => {
                            let removed = device_removed_event.into_event().device();

                            // remove device
                            self.devices.retain(|dev| *dev == removed);

                            let device_seat = removed.seat();

                            // update capabilities, so they appear correctly on `on_seat_changed` and `on_seat_destroyed`.
                            if let Some(seat) = self.seats.get_mut(&device_seat) {
                                let caps = seat.capabilities_mut();
                                caps.pointer = self.devices.iter().any(|x| x.has_capability(DeviceCapability::Pointer));
                                caps.keyboard = self.devices.iter().any(|x| x.has_capability(DeviceCapability::Keyboard));
                                caps.touch = self.devices.iter().any(|x| x.has_capability(DeviceCapability::Touch));
                            } else {
                                //TODO is this the best we can do?
                                panic!("Seat changed that was never created")
                            }

                            // check if the seat has any other devices
                            if !self.devices.iter().any(|x| x.seat() == device_seat) {
                                // it has not, lets destroy it
                                if let Some(seat) = self.seats.remove(&device_seat) {
                                    if let Some(ref mut handler) = self.handler {
                                        handler.on_seat_destroyed(&seat);
                                    }
                                } else {
                                    //TODO is this the best we can do?
                                    panic!("Seat destroyed that was never created");
                                }
                            } else {
                                // it has, notify about updates
                                if let Some(ref mut handler) = self.handler {
                                    handler.on_seat_changed(self.seats.get(&device_seat).unwrap());
                                }
                            }
                        },
                    }
                    if let Some(ref mut handler) = self.handler {
                       handler.on_input_config_changed(&mut self.devices);
                    }
                },
                Event::Touch(touch_event) => {},
                Event::Keyboard(keyboard_event) => {},
                Event::Pointer(pointer_event) => {},
                _ => {}, //FIXME: What to do with the rest.
            }
        };
        Ok(())
    }
}
<commit_msg>Use entry api for `DeviceAddedEvent`s seat creation/update<commit_after>//! Implementation of input backend trait for types provided by `libinput`

use backend::SeatInternal;
use backend::input::{InputBackend, InputHandler, Seat, SeatCapabilities};
use input::{Libinput, Device, Seat as LibinputSeat, DeviceCapability};
use input::event::*;

use std::io::Error as IoError;
use std::collections::hash_map::{DefaultHasher, Entry, HashMap};
use std::hash::{Hash, Hasher};

pub struct LibinputInputBackend {
    context: Libinput,
    devices: Vec<Device>,
    seats: HashMap<LibinputSeat, Seat>,
    handler: Option<Box<InputHandler<LibinputInputBackend> + 'static>>,
}

impl InputBackend for LibinputInputBackend {
    type InputConfig = [Device];
    type EventError = IoError;

    fn set_handler<H: InputHandler<Self> + 'static>(&mut self, mut handler: H) {
        if self.handler.is_some() {
            self.clear_handler();
        }
        for seat in self.seats.values() {
            handler.on_seat_created(&seat);
        }
        self.handler = Some(Box::new(handler));
    }

    fn get_handler(&mut self) -> Option<&mut InputHandler<Self>> {
        self.handler
            .as_mut()
            .map(|handler| handler as &mut InputHandler<Self>)
    }

    fn clear_handler(&mut self) {
        if let Some(mut handler) = self.handler.take() {
            for seat in self.seats.values() {
                handler.on_seat_destroyed(&seat);
            }
        }
    }

    fn input_config(&mut self) -> &mut Self::InputConfig {
        &mut self.devices
    }

    fn set_cursor_position(&mut self, _x: u32, _y: u32) -> Result<(), ()> {
        // FIXME later.
        // This will be doable with the hardware cursor api and probably some more cases
        Err(())
    }

    fn dispatch_new_events(&mut self) -> Result<(), IoError> {
        self.context.dispatch()?;
        for event in &mut self.context {
            match event {
                Event::Device(device_event) => {
                    use input::event::device::*;
                    match device_event {
                        DeviceEvent::Added(device_added_event) => {
                            let added = device_added_event.into_event().device();

                            let new_caps = SeatCapabilities {
                                pointer: added.has_capability(DeviceCapability::Pointer),
                                keyboard: added.has_capability(DeviceCapability::Keyboard),
                                touch: added.has_capability(DeviceCapability::Touch),
                            };

                            let device_seat = added.seat();
                            self.devices.push(added);

                            match self.seats.entry(device_seat) {
                                Entry::Occupied(mut seat_entry) => {
                                    let old_seat = seat_entry.get_mut();
                                    {
                                        let caps = old_seat.capabilities_mut();
                                        caps.pointer = new_caps.pointer || caps.pointer;
                                        caps.keyboard = new_caps.keyboard || caps.keyboard;
                                        caps.touch = new_caps.touch || caps.touch;
                                    }
                                    if let Some(ref mut handler) = self.handler {
                                        handler.on_seat_changed(old_seat);
                                    }
                                },
                                Entry::Vacant(seat_entry) => {
                                    let mut hasher = DefaultHasher::default();
                                    seat_entry.key().hash(&mut hasher);
                                    let seat = seat_entry.insert(Seat::new(hasher.finish(), new_caps));
                                    if let Some(ref mut handler) = self.handler {
                                        handler.on_seat_created(seat);
                                    }
                                }
                            }
                        },
                        DeviceEvent::Removed(device_removed_event) => {
                            let removed = device_removed_event.into_event().device();

                            // remove device
                            self.devices.retain(|dev| *dev == removed);

                            let device_seat = removed.seat();

                            // update capabilities, so they appear correctly on `on_seat_changed` and `on_seat_destroyed`.
                            if let Some(seat) = self.seats.get_mut(&device_seat) {
                                let caps = seat.capabilities_mut();
                                caps.pointer = self.devices.iter().any(|x| x.has_capability(DeviceCapability::Pointer));
                                caps.keyboard = self.devices.iter().any(|x| x.has_capability(DeviceCapability::Keyboard));
                                caps.touch = self.devices.iter().any(|x| x.has_capability(DeviceCapability::Touch));
                            } else {
                                //TODO is this the best we can do?
                                panic!("Seat changed that was never created")
                            }

                            // check if the seat has any other devices
                            if !self.devices.iter().any(|x| x.seat() == device_seat) {
                                // it has not, lets destroy it
                                if let Some(seat) = self.seats.remove(&device_seat) {
                                    if let Some(ref mut handler) = self.handler {
                                        handler.on_seat_destroyed(&seat);
                                    }
                                } else {
                                    //TODO is this the best we can do?
                                    panic!("Seat destroyed that was never created");
                                }
                            } else {
                                // it has, notify about updates
                                if let Some(ref mut handler) = self.handler {
                                    handler.on_seat_changed(self.seats.get(&device_seat).unwrap());
                                }
                            }
                        },
                    }
                    if let Some(ref mut handler) = self.handler {
                       handler.on_input_config_changed(&mut self.devices);
                    }
                },
                Event::Touch(touch_event) => {},
                Event::Keyboard(keyboard_event) => {},
                Event::Pointer(pointer_event) => {},
                _ => {}, //FIXME: What to do with the rest.
            }
        };
        Ok(())
    }
}
<|endoftext|>


<commit_before>extern crate rayon;
use self::rayon::prelude::*;

use decoders::{Orientation, RawImage};
use imageops::OpBuffer;

/// Mirror an OpBuffer horizontally
pub fn flip_horizontal(buf: &OpBuffer) -> OpBuffer {
  let mut out = OpBuffer::new(buf.width, buf.height, buf.colors);
  out.data.par_chunks_mut(out.width * out.colors).enumerate().for_each(|(row, line)| {
    let offset = buf.width * row * buf.colors;
    for col in 0 .. buf.width {
      for c in 0 .. buf.colors {
        line[col * buf.colors + c] = buf.data[offset + (buf.width - 1 - col) * buf.colors + c];
      }
    }
  });

  out
}

/// Mirror an OpBuffer vertically
pub fn flip_vertical(buf: &OpBuffer) -> OpBuffer {
  let mut out = OpBuffer::new(buf.width, buf.height, buf.colors);
  out.data.par_chunks_mut(out.width * out.colors).enumerate().for_each(|(row, line)| {
    let offset = (buf.height - 1 - row) * buf.width * buf.colors;
    for col in 0 .. buf.width * buf.colors {
      line[col] = buf.data[offset + col];
    }
  });

  out
}

/// Transpose an OpBuffer
pub fn transpose(buf: &OpBuffer) -> OpBuffer {
  let mut out = OpBuffer::new(buf.height, buf.width, buf.colors);

  out.data.par_chunks_mut(out.width * out.colors).enumerate().for_each(|(row, line)| {
    for col in 0 .. buf.height {
      let target = col * buf.colors;
      let source = (col * buf.width + row) * buf.colors;
      for c in 0 .. buf.colors {
        line[target + c] = buf.data[source + c];
      }
    }
  });

  out
}

fn rotate_buffer(buf: &OpBuffer, orientation: &Orientation) -> OpBuffer {
  match orientation.to_flips() {
    (false, false, false) => buf.clone(),
    (false, false, true) => flip_vertical(buf),
    (false, true, false) => flip_horizontal(buf),
    (false, true, true) => flip_horizontal(&flip_vertical(buf)),
    (true, false, false) => transpose(buf),
    (true, false, true) => flip_vertical(&transpose(buf)),
    (true, true, false) => flip_horizontal(&transpose(buf)),
    (true, true, true) => flip_vertical(&flip_horizontal(&transpose(buf))),
  }
}

/// Rotate an OpBuffer based on the given RawImage's orientation
pub fn rotate(img: &RawImage, buf: &OpBuffer) -> OpBuffer {
  rotate_buffer(buf, &img.orientation)
}

#[cfg(test)]
mod tests {
  use decoders::Orientation;
  use imageops::OpBuffer;
  use super::rotate_buffer;

  // Store a colorful capital F as a constant, since it is used in all tests
  lazy_static! {
      static ref F: OpBuffer = {
        OpBuffer::from_rgb_str_vec(vec![
          "        ",
          " RRRRRR ",
          " GG     ",
          " BBBB   ",
          " GG     ",
          " GG     ",
          "        ",
        ])
      };
  }

  #[test]
  fn rotate_unknown() {
    assert_eq!(rotate_buffer(&F.clone(), &Orientation::Unknown), F.clone());
  }

  #[test]
  fn rotate_normal() {
    assert_eq!(rotate_buffer(&F.clone(), &Orientation::Normal), F.clone());
  }

  #[test]
  fn rotate_flip_x() {
    let output = OpBuffer::from_rgb_str_vec(vec![
      "        ",
      " RRRRRR ",
      "     GG ",
      "   BBBB ",
      "     GG ",
      "     GG ",
      "        ",
    ]);

    assert_eq!(rotate_buffer(&F.clone(), &Orientation::HorizontalFlip), output);
  }

  #[test]
  fn rotate_flip_y() {
    let output = OpBuffer::from_rgb_str_vec(vec![
      "        ",
      " GG     ",
      " GG     ",
      " BBBB   ",
      " GG     ",
      " RRRRRR ",
      "        ",
    ]);
    assert_eq!(rotate_buffer(&F.clone(), &Orientation::VerticalFlip), output);
  }

  #[test]
  fn rotate_rotate90_cw() {
    let output = OpBuffer::from_rgb_str_vec(vec![
      "       ",
      " GGBGR ",
      " GGBGR ",
      "   B R ",
      "   B R ",
      "     R ",
      "     R ",
      "       ",
    ]);
    assert_eq!(rotate_buffer(&F.clone(), &Orientation::Rotate90), output);
  }

  #[test]
  fn rotate_rotate270_cw() {
    let output = OpBuffer::from_rgb_str_vec(vec![
      "       ",
      " R     ",
      " R     ",
      " R B   ",
      " R B   ",
      " RGBGG ",
      " RGBGG ",
      "       ",
    ]);
    assert_eq!(rotate_buffer(&F.clone(), &Orientation::Rotate270), output);
  }

  #[test]
  fn rotate_transpose() {
    let output = OpBuffer::from_rgb_str_vec(vec![
      "       ",
      " RGBGG ",
      " RGBGG ",
      " R B   ",
      " R B   ",
      " R     ",
      " R     ",
      "       ",
    ]);
    assert_eq!(rotate_buffer(&F.clone(), &Orientation::Transpose), output);
  }

  #[test]
  fn rotate_transverse() {
    let output = OpBuffer::from_rgb_str_vec(vec![
      "       ",
      "     R ",
      "     R ",
      "   B R ",
      "   B R ",
      " GGBGR ",
      " GGBGR ",
      "       ",
    ]);
    assert_eq!(rotate_buffer(&F.clone(), &Orientation::Transverse), output);
  }
}
<commit_msg>Added test for 180 degree rotation<commit_after>extern crate rayon;
use self::rayon::prelude::*;

use decoders::{Orientation, RawImage};
use imageops::OpBuffer;

/// Mirror an OpBuffer horizontally
pub fn flip_horizontal(buf: &OpBuffer) -> OpBuffer {
  let mut out = OpBuffer::new(buf.width, buf.height, buf.colors);
  out.data.par_chunks_mut(out.width * out.colors).enumerate().for_each(|(row, line)| {
    let offset = buf.width * row * buf.colors;
    for col in 0 .. buf.width {
      for c in 0 .. buf.colors {
        line[col * buf.colors + c] = buf.data[offset + (buf.width - 1 - col) * buf.colors + c];
      }
    }
  });

  out
}

/// Mirror an OpBuffer vertically
pub fn flip_vertical(buf: &OpBuffer) -> OpBuffer {
  let mut out = OpBuffer::new(buf.width, buf.height, buf.colors);
  out.data.par_chunks_mut(out.width * out.colors).enumerate().for_each(|(row, line)| {
    let offset = (buf.height - 1 - row) * buf.width * buf.colors;
    for col in 0 .. buf.width * buf.colors {
      line[col] = buf.data[offset + col];
    }
  });

  out
}

/// Transpose an OpBuffer
pub fn transpose(buf: &OpBuffer) -> OpBuffer {
  let mut out = OpBuffer::new(buf.height, buf.width, buf.colors);

  out.data.par_chunks_mut(out.width * out.colors).enumerate().for_each(|(row, line)| {
    for col in 0 .. buf.height {
      let target = col * buf.colors;
      let source = (col * buf.width + row) * buf.colors;
      for c in 0 .. buf.colors {
        line[target + c] = buf.data[source + c];
      }
    }
  });

  out
}

fn rotate_buffer(buf: &OpBuffer, orientation: &Orientation) -> OpBuffer {
  match orientation.to_flips() {
    (false, false, false) => buf.clone(),
    (false, false, true) => flip_vertical(buf),
    (false, true, false) => flip_horizontal(buf),
    (false, true, true) => flip_horizontal(&flip_vertical(buf)),
    (true, false, false) => transpose(buf),
    (true, false, true) => flip_vertical(&transpose(buf)),
    (true, true, false) => flip_horizontal(&transpose(buf)),
    (true, true, true) => flip_vertical(&flip_horizontal(&transpose(buf))),
  }
}

/// Rotate an OpBuffer based on the given RawImage's orientation
pub fn rotate(img: &RawImage, buf: &OpBuffer) -> OpBuffer {
  rotate_buffer(buf, &img.orientation)
}

#[cfg(test)]
mod tests {
  use decoders::Orientation;
  use imageops::OpBuffer;
  use super::rotate_buffer;

  // Store a colorful capital F as a constant, since it is used in all tests
  lazy_static! {
      static ref F: OpBuffer = {
        OpBuffer::from_rgb_str_vec(vec![
          "        ",
          " RRRRRR ",
          " GG     ",
          " BBBB   ",
          " GG     ",
          " GG     ",
          "        ",
        ])
      };
  }

  #[test]
  fn rotate_unknown() {
    assert_eq!(rotate_buffer(&F.clone(), &Orientation::Unknown), F.clone());
  }

  #[test]
  fn rotate_normal() {
    assert_eq!(rotate_buffer(&F.clone(), &Orientation::Normal), F.clone());
  }

  #[test]
  fn rotate_flip_x() {
    let output = OpBuffer::from_rgb_str_vec(vec![
      "        ",
      " RRRRRR ",
      "     GG ",
      "   BBBB ",
      "     GG ",
      "     GG ",
      "        ",
    ]);

    assert_eq!(rotate_buffer(&F.clone(), &Orientation::HorizontalFlip), output);
  }

  #[test]
  fn rotate_flip_y() {
    let output = OpBuffer::from_rgb_str_vec(vec![
      "        ",
      " GG     ",
      " GG     ",
      " BBBB   ",
      " GG     ",
      " RRRRRR ",
      "        ",
    ]);
    assert_eq!(rotate_buffer(&F.clone(), &Orientation::VerticalFlip), output);
  }

  #[test]
  fn rotate_rotate90_cw() {
    let output = OpBuffer::from_rgb_str_vec(vec![
      "       ",
      " GGBGR ",
      " GGBGR ",
      "   B R ",
      "   B R ",
      "     R ",
      "     R ",
      "       ",
    ]);
    assert_eq!(rotate_buffer(&F.clone(), &Orientation::Rotate90), output);
  }

  #[test]
  fn rotate_rotate270_cw() {
    let output = OpBuffer::from_rgb_str_vec(vec![
      "       ",
      " R     ",
      " R     ",
      " R B   ",
      " R B   ",
      " RGBGG ",
      " RGBGG ",
      "       ",
    ]);
    assert_eq!(rotate_buffer(&F.clone(), &Orientation::Rotate270), output);
  }

  #[test]
  fn rotate_rotate180() {
    let output = OpBuffer::from_rgb_str_vec(vec![
      "        ",
      "     GG ",
      "     GG ",
      "   BBBB ",
      "     GG ",
      " RRRRRR ",
      "        ",
    ]);
    assert_eq!(rotate_buffer(&F.clone(), &Orientation::Rotate180), output);
  }

  #[test]
  fn rotate_transpose() {
    let output = OpBuffer::from_rgb_str_vec(vec![
      "       ",
      " RGBGG ",
      " RGBGG ",
      " R B   ",
      " R B   ",
      " R     ",
      " R     ",
      "       ",
    ]);
    assert_eq!(rotate_buffer(&F.clone(), &Orientation::Transpose), output);
  }

  #[test]
  fn rotate_transverse() {
    let output = OpBuffer::from_rgb_str_vec(vec![
      "       ",
      "     R ",
      "     R ",
      "   B R ",
      "   B R ",
      " GGBGR ",
      " GGBGR ",
      "       ",
    ]);
    assert_eq!(rotate_buffer(&F.clone(), &Orientation::Transverse), output);
  }
}
<|endoftext|>


<commit_before>// Copyright (c) 2013-2015 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

//! Echo server with a bounded buffer pool.

extern crate gj;
use std::net::ToSocketAddrs;
use std::cell::RefCell;
use std::rc::Rc;
use gj::io::{AsyncRead, AsyncWrite};

fn echo<S,B>(stream: S, buf: B) -> gj::Promise<(S, B), gj::io::Error<(S, B)>>
    where S: AsyncRead + AsyncWrite, B: ::std::ops::DerefMut<Target=[u8]> + 'static
{
    stream.try_read(buf, 1).then(move |(stream, buf, n)| {
        if n == 0 {
            // EOF
            Ok(gj::Promise::fulfilled((stream, buf)))
        } else {
            Ok(stream.write(gj::io::Slice::new(buf, n)).then_else(move |r| match r {
                Ok((stream, slice)) => Ok(echo(stream, slice.buf)),
                Err(gj::io::Error {state: (stream, slice), error}) =>
                    Err(gj::io::Error::new((stream, slice.buf), error)),
            }))
        }
    })
}

type ThreadedState = (::gj::io::tcp::Stream, Vec<u8>);

fn accept_loop(receiver: gj::io::tcp::Listener,
               mut task_set: gj::TaskSet<ThreadedState, gj::io::Error<ThreadedState>>,
               buffer_pool: Rc<RefCell<Vec<Vec<u8>>>>)
               -> gj::Promise<(), ::std::io::Error>
{
    receiver.accept().lift().then(move |(receiver, stream)| {
        match buffer_pool.borrow_mut().pop() {
            None => println!("Buffer pool has no free buffers. Dropping connection."),
            Some(buf) => task_set.add(echo(stream, buf)),
        }
        Ok(accept_loop(receiver, task_set, buffer_pool))
    })
}

struct Reaper {
    buffer_pool: Rc<RefCell<Vec<Vec<u8>>>>,
}

impl gj::TaskReaper<ThreadedState, gj::io::Error<ThreadedState>> for Reaper {
    fn task_succeeded(&mut self, (_, buf): ThreadedState) {
        self.buffer_pool.borrow_mut().push(buf);
    }
    fn task_failed(&mut self, error: gj::io::Error<ThreadedState>) {
        self.buffer_pool.borrow_mut().push(error.state.1);
        println!("Task failed: {}", error.error);
    }
}

pub fn main() {
    let args: Vec<String> = ::std::env::args().collect();
    if args.len() != 2 {
        println!("usage: {} HOST:PORT", args[0]);
        return;
    }
    const NUM_BUFFERS: usize = 64;

    gj::EventLoop::top_level(move |wait_scope| {
        let mut buffers = Vec::with_capacity(NUM_BUFFERS);
        for _ in 0..NUM_BUFFERS {
            buffers.push(vec![0; 1024]);
        }
        let buffer_pool = Rc::new(RefCell::new(buffers));

        let addr = try!(args[1].to_socket_addrs()).next().expect("could not parse address");
        let listener = try!(::gj::io::tcp::Listener::bind(addr));
        let reaper = Box::new(Reaper { buffer_pool: buffer_pool.clone() });
        accept_loop(listener, gj::TaskSet::new(reaper), buffer_pool).lift().wait(wait_scope)
    }).unwrap();
}
<commit_msg>Improve the echo example.<commit_after>// Copyright (c) 2013-2015 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

//! Echo server with a bounded buffer pool.

extern crate gj;
use std::net::ToSocketAddrs;
use std::cell::RefCell;
use std::rc::Rc;
use gj::io::{AsyncRead, AsyncWrite};

struct BufferPool {
    buffers: Vec<Vec<u8>>,
    fulfiller: Option<Box<gj::PromiseFulfiller<Vec<u8>, ::std::io::Error>>>,
}

impl BufferPool {
    pub fn new(num_buffers: usize) -> BufferPool {
        let mut buffers = Vec::with_capacity(num_buffers);
        for _ in 0..num_buffers {
            buffers.push(vec![0; 1024]);
        }
        BufferPool { buffers: buffers, fulfiller: None }
    }

    pub fn pop(&mut self) -> gj::Promise<Vec<u8>, ::std::io::Error> {
        match self.buffers.pop() {
            Some(buf) => gj::Promise::fulfilled(buf),
            None => {
                let (promise, fulfiller) = gj::new_promise_and_fulfiller();
                assert!(self.fulfiller.is_none(), "only supports one waiting task");
                self.fulfiller = Some(fulfiller);
                promise
            }
        }
    }

    pub fn push(&mut self, buf: Vec<u8>) {
        match self.fulfiller.take() {
            Some(fulfiller) => fulfiller.fulfill(buf),
            None => self.buffers.push(buf),
        }
    }
}

struct Reaper {
    buffer_pool: Rc<RefCell<BufferPool>>,
}

impl gj::TaskReaper<ThreadedState, gj::io::Error<ThreadedState>> for Reaper {
    fn task_succeeded(&mut self, (_, buf): ThreadedState) {
        self.buffer_pool.borrow_mut().push(buf);
    }
    fn task_failed(&mut self, error: gj::io::Error<ThreadedState>) {
        self.buffer_pool.borrow_mut().push(error.state.1);
        println!("Task failed: {}", error.error);
    }
}

type ThreadedState = (::gj::io::tcp::Stream, Vec<u8>);

fn echo<S,B>(stream: S, buf: B) -> gj::Promise<(S, B), gj::io::Error<(S, B)>>
    where S: AsyncRead + AsyncWrite, B: ::std::ops::DerefMut<Target=[u8]> + 'static
{
    stream.try_read(buf, 1).then(move |(stream, buf, n)| {
        if n == 0 {
            // EOF
            Ok(gj::Promise::fulfilled((stream, buf)))
        } else {
            Ok(stream.write(gj::io::Slice::new(buf, n)).then_else(move |r| match r {
                Ok((stream, slice)) => Ok(echo(stream, slice.buf)),
                Err(gj::io::Error {state: (stream, slice), error}) =>
                    Err(gj::io::Error::new((stream, slice.buf), error)),
            }))
        }
    })
}

fn accept_loop(receiver: gj::io::tcp::Listener,
               mut task_set: gj::TaskSet<ThreadedState, gj::io::Error<ThreadedState>>,
               buffer_pool: Rc<RefCell<BufferPool>>)
               -> gj::Promise<(), ::std::io::Error>
{
    let buf_promise = buffer_pool.borrow_mut().pop();
    buf_promise.then(move |buf| {
        Ok(receiver.accept().lift().then(move |(receiver, stream)| {
            task_set.add(echo(stream, buf));
            Ok(accept_loop(receiver, task_set, buffer_pool))
        }))
    })
}

pub fn main() {
    let args: Vec<String> = ::std::env::args().collect();
    if args.len() != 2 {
        println!("usage: {} HOST:PORT", args[0]);
        return;
    }
    let buffer_pool = Rc::new(RefCell::new(BufferPool::new(1)));

    gj::EventLoop::top_level(move |wait_scope| {

        let addr = try!(args[1].to_socket_addrs()).next().expect("could not parse address");
        let listener = try!(::gj::io::tcp::Listener::bind(addr));
        let reaper = Box::new(Reaper { buffer_pool: buffer_pool.clone() });
        accept_loop(listener, gj::TaskSet::new(reaper), buffer_pool).lift().wait(wait_scope)
    }).unwrap();
}
<|endoftext|>


<commit_before>/*! Interactive grid projection demo */

extern crate calx;

use calx::{noise, color, V2, Rgba, Rect, Projection, Anchor};
use calx::backend::{CanvasBuilder, Canvas, CanvasUtil, Event, Key};

static PROJECTIONS: [(V2<f32>, V2<f32>); 3] = [
    (V2(16.0, 8.0), V2(-16.0, 8.0)),  // Isometric
    (V2(16.0, 0.0), V2(0.0, 16.0)),   // Topdown
    (V2(16.0, 0.0), V2(-6.0, 12.0)),  // Cavalier
];

fn draw_cell(ctx: &mut Canvas, proj: &Projection, cell: V2<i32>, dim: bool) {
    let n = cell.0 + 3329 * cell.1;
    // Procedural color.
    let mut color = Rgba::new(
        noise(n * 3) / 2.0 + 0.5,
        noise(n * 3 + 1) / 2.0 + 0.5,
        noise(n * 3 + 2) / 2.0 + 0.5,
        1.0);
    if dim {
        color = color.to_monochrome();
    }

    let tex = ctx.solid_tex_coord();
    let ind0 = ctx.num_vertices();

    // Project world-space cell base into screen space.
    let base = Rect(cell.map(|x| x as f32), V2(1.0, 1.0));
    for &p in [Anchor::TopLeft, Anchor::TopRight, Anchor::BottomRight, Anchor::BottomLeft].iter() {
        ctx.push_vertex(proj.project(base.point(p)), 0.5, tex, &color, &color::BLACK);
    }

    ctx.push_triangle(ind0, ind0 + 1, ind0 + 2);
    ctx.push_triangle(ind0, ind0 + 2, ind0 + 3);
}

fn main() {
    let mut projection = 0;
    let scroll_speed = 2f32;
    let mut mouse_pos = V2(-1i32, -1i32);
    let mut screen_offset = V2(0.0f32, 0.0f32);
    let mut scroll_delta = V2(0.0f32, 0.0f32);
    let mut ctx = CanvasBuilder::new().build();

    let screen_rect = Rect(V2(0.0f32, 0.0f32), V2(640.0f32, 360.0f32));

    loop {
        match ctx.next_event() {
            Event::Quit => { return; }
            Event::RenderFrame => {
                ctx.clear();

                screen_offset = screen_offset - scroll_delta;
                let proj = Projection::new(PROJECTIONS[projection].0, PROJECTIONS[projection].1,
                                           screen_offset).unwrap();

                let mouse_rect = Rect(mouse_pos.map(|x| x as f32) - V2(160.0, 90.0), V2(320.0, 180.0));
                let mouse_cell = proj.inv_project(mouse_pos.map(|x| x as f32)).map(|x| x as i32);

                for pt in proj.inv_project_rectangle(&screen_rect).iter() {
                    let cell = pt.map(|x| x as i32);
                    if cell != mouse_cell { draw_cell(&mut ctx, &proj, cell, true); }
                }

                for pt in proj.inv_project_rectangle(&mouse_rect).iter() {
                    let cell = pt.map(|x| x as i32);
                    if cell != mouse_cell { draw_cell(&mut ctx, &proj, cell, false); }
                }

                ctx.draw_rect(&mouse_rect, 0.5, &"cyan");
            }

            Event::KeyPressed(Key::Escape) => { return; }

            Event::KeyPressed(Key::Tab) => {
                projection += 1;
                projection %= PROJECTIONS.len();
            }

            Event::KeyPressed(Key::F12) => {
                ctx.save_screenshot(&"grid");
            }

            Event::KeyPressed(k) => {
                match k {
                    Key::A => { scroll_delta.0 = -1.0 * scroll_speed; }
                    Key::D => { scroll_delta.0 =  1.0 * scroll_speed; }
                    Key::W => { scroll_delta.1 = -1.0 * scroll_speed; }
                    Key::S => { scroll_delta.1 =  1.0 * scroll_speed; }
                    _ => {}
                }
            }

            Event::KeyReleased(k) => {
                match k {
                    Key::A => { scroll_delta.0 = 0.0; }
                    Key::D => { scroll_delta.0 = 0.0; }
                    Key::W => { scroll_delta.1 = 0.0; }
                    Key::S => { scroll_delta.1 = 0.0; }
                    _ => {}
                }
            }
            Event::MouseMoved((x, y)) => {
                mouse_pos = V2(x, y);
            }
            _ => {}
        }
    }
}
<commit_msg>Fix inverse projection with negative coordinates<commit_after>/*! Interactive grid projection demo */

extern crate calx;

use calx::{noise, color, V2, Rgba, Rect, Projection, Anchor};
use calx::backend::{CanvasBuilder, Canvas, CanvasUtil, Event, Key};

static PROJECTIONS: [(V2<f32>, V2<f32>); 3] = [
    (V2(16.0, 8.0), V2(-16.0, 8.0)),  // Isometric
    (V2(16.0, 0.0), V2(0.0, 16.0)),   // Topdown
    (V2(16.0, 0.0), V2(-6.0, 12.0)),  // Cavalier
];

fn draw_cell(ctx: &mut Canvas, proj: &Projection, cell: V2<i32>, dim: bool) {
    let n = cell.0 + 3329 * cell.1;
    // Procedural color.
    let mut color = Rgba::new(
        noise(n * 3) / 2.0 + 0.5,
        noise(n * 3 + 1) / 2.0 + 0.5,
        noise(n * 3 + 2) / 2.0 + 0.5,
        1.0);
    if dim {
        color = color.to_monochrome();
    }

    let tex = ctx.solid_tex_coord();
    let ind0 = ctx.num_vertices();

    // Project world-space cell base into screen space.
    let base = Rect(cell.map(|x| x as f32), V2(1.0, 1.0));
    for &p in [Anchor::TopLeft, Anchor::TopRight, Anchor::BottomRight, Anchor::BottomLeft].iter() {
        ctx.push_vertex(proj.project(base.point(p)), 0.5, tex, &color, &color::BLACK);
    }

    ctx.push_triangle(ind0, ind0 + 1, ind0 + 2);
    ctx.push_triangle(ind0, ind0 + 2, ind0 + 3);
}

fn main() {
    let mut projection = 0;
    let scroll_speed = 2f32;
    let mut mouse_pos = V2(-1i32, -1i32);
    let mut screen_offset = V2(0.0f32, 0.0f32);
    let mut scroll_delta = V2(0.0f32, 0.0f32);
    let mut ctx = CanvasBuilder::new().build();

    let screen_rect = Rect(V2(0.0f32, 0.0f32), V2(640.0f32, 360.0f32));

    loop {
        match ctx.next_event() {
            Event::Quit => { return; }
            Event::RenderFrame => {
                ctx.clear();

                screen_offset = screen_offset - scroll_delta;
                let proj = Projection::new(PROJECTIONS[projection].0, PROJECTIONS[projection].1,
                                           screen_offset).unwrap();

                let mouse_rect = Rect(mouse_pos.map(|x| x as f32) - V2(160.0, 90.0), V2(320.0, 180.0));
                let mouse_cell = proj.inv_project(mouse_pos.map(|x| x as f32)).map(|x| x.floor() as i32);

                for pt in proj.inv_project_rectangle(&screen_rect).iter() {
                    let cell = pt.map(|x| x as i32);
                    if cell != mouse_cell { draw_cell(&mut ctx, &proj, cell, true); }
                }

                for pt in proj.inv_project_rectangle(&mouse_rect).iter() {
                    let cell = pt.map(|x| x as i32);
                    if cell != mouse_cell { draw_cell(&mut ctx, &proj, cell, false); }
                }

                ctx.draw_rect(&mouse_rect, 0.5, &"cyan");
            }

            Event::KeyPressed(Key::Escape) => { return; }

            Event::KeyPressed(Key::Tab) => {
                projection += 1;
                projection %= PROJECTIONS.len();
            }

            Event::KeyPressed(Key::F12) => {
                ctx.save_screenshot(&"grid");
            }

            Event::KeyPressed(k) => {
                match k {
                    Key::A => { scroll_delta.0 = -1.0 * scroll_speed; }
                    Key::D => { scroll_delta.0 =  1.0 * scroll_speed; }
                    Key::W => { scroll_delta.1 = -1.0 * scroll_speed; }
                    Key::S => { scroll_delta.1 =  1.0 * scroll_speed; }
                    _ => {}
                }
            }

            Event::KeyReleased(k) => {
                match k {
                    Key::A => { scroll_delta.0 = 0.0; }
                    Key::D => { scroll_delta.0 = 0.0; }
                    Key::W => { scroll_delta.1 = 0.0; }
                    Key::S => { scroll_delta.1 = 0.0; }
                    _ => {}
                }
            }
            Event::MouseMoved((x, y)) => {
                mouse_pos = V2(x, y);
            }
            _ => {}
        }
    }
}
<|endoftext|>