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

use core::default::Default;
use cssparser::Parser;
use dom::bindings::codegen::Bindings::MediaListBinding;
use dom::bindings::codegen::Bindings::MediaListBinding::MediaListMethods;
use dom::bindings::js::{JS, Root};
use dom::bindings::reflector::{DomObject, Reflector, reflect_dom_object};
use dom::bindings::str::DOMString;
use dom::cssstylesheet::CSSStyleSheet;
use dom::window::Window;
use dom_struct::dom_struct;
use std::sync::Arc;
use style::media_queries::{MediaQuery, parse_media_query_list};
use style::media_queries::MediaList as StyleMediaList;
use style::parser::{LengthParsingMode, ParserContext};
use style::shared_lock::{SharedRwLock, Locked};
use style::stylesheets::CssRuleType;
use style_traits::ToCss;

#[dom_struct]
pub struct MediaList {
    reflector_: Reflector,
    parent_stylesheet: JS<CSSStyleSheet>,
    #[ignore_heap_size_of = "Arc"]
    media_queries: Arc<Locked<StyleMediaList>>,
}

impl MediaList {
    #[allow(unrooted_must_root)]
    pub fn new_inherited(parent_stylesheet: &CSSStyleSheet,
                         media_queries: Arc<Locked<StyleMediaList>>) -> MediaList {
        MediaList {
            parent_stylesheet: JS::from_ref(parent_stylesheet),
            reflector_: Reflector::new(),
            media_queries: media_queries,
        }
    }

    #[allow(unrooted_must_root)]
    pub fn new(window: &Window, parent_stylesheet: &CSSStyleSheet,
               media_queries: Arc<Locked<StyleMediaList>>)
        -> Root<MediaList> {
        reflect_dom_object(box MediaList::new_inherited(parent_stylesheet, media_queries),
                           window,
                           MediaListBinding::Wrap)
    }

    fn shared_lock(&self) -> &SharedRwLock {
        &self.parent_stylesheet.style_stylesheet().shared_lock
    }
}

impl MediaListMethods for MediaList {
    // https://drafts.csswg.org/cssom/#dom-medialist-mediatext
    fn MediaText(&self) -> DOMString {
        let guard = self.shared_lock().read();
        DOMString::from(self.media_queries.read_with(&guard).to_css_string())
    }

    // https://drafts.csswg.org/cssom/#dom-medialist-mediatext
    fn SetMediaText(&self, value: DOMString) {
        let mut guard = self.shared_lock().write();
        let mut media_queries = self.media_queries.write_with(&mut guard);
        // Step 2
        if value.is_empty() {
            // Step 1
            *media_queries = StyleMediaList::empty();
            return;
        }
        // Step 3
        let mut parser = Parser::new(&value);
        let global = self.global();
        let win = global.as_window();
        let url = win.get_url();
        let context = ParserContext::new_for_cssom(&url, win.css_error_reporter(), Some(CssRuleType::Media),
                                                   LengthParsingMode::Default);
        *media_queries = parse_media_query_list(&context, &mut parser);
    }

    // https://drafts.csswg.org/cssom/#dom-medialist-length
    fn Length(&self) -> u32 {
        let guard = self.shared_lock().read();
        self.media_queries.read_with(&guard).media_queries.len() as u32
    }

    // https://drafts.csswg.org/cssom/#dom-medialist-item
    fn Item(&self, index: u32) -> Option<DOMString> {
        let guard = self.shared_lock().read();
        self.media_queries.read_with(&guard).media_queries
        .get(index as usize).and_then(|query| {
            let mut s = String::new();
            query.to_css(&mut s).unwrap();
            Some(DOMString::from_string(s))
        })
    }

    // https://drafts.csswg.org/cssom/#dom-medialist-item
    fn IndexedGetter(&self, index: u32) -> Option<DOMString> {
        self.Item(index)
    }

    // https://drafts.csswg.org/cssom/#dom-medialist-appendmedium
    fn AppendMedium(&self, medium: DOMString) {
        // Step 1
        let mut parser = Parser::new(&medium);
        let global = self.global();
        let win = global.as_window();
        let url = win.get_url();
        let context = ParserContext::new_for_cssom(&url, win.css_error_reporter(), Some(CssRuleType::Media),
                                                   LengthParsingMode::Default);
        let m = MediaQuery::parse(&context, &mut parser);
        // Step 2
        if let Err(_) = m {
            return;
        }
        // Step 3
        let m_serialized = m.clone().unwrap().to_css_string();
        let mut guard = self.shared_lock().write();
        let mq = self.media_queries.write_with(&mut guard);
        let any = mq.media_queries.iter().any(|q| m_serialized == q.to_css_string());
        if any {
            return;
        }
        // Step 4
        mq.media_queries.push(m.unwrap());
    }

    // https://drafts.csswg.org/cssom/#dom-medialist-deletemedium
    fn DeleteMedium(&self, medium: DOMString) {
        // Step 1
        let mut parser = Parser::new(&medium);
        let global = self.global();
        let win = global.as_window();
        let url = win.get_url();
        let context = ParserContext::new_for_cssom(&url, win.css_error_reporter(), Some(CssRuleType::Media),
                                                   LengthParsingMode::Default);
        let m = MediaQuery::parse(&context, &mut parser);
        // Step 2
        if let Err(_) = m {
            return;
        }
        // Step 3
        let m_serialized = m.unwrap().to_css_string();
        let mut guard = self.shared_lock().write();
        let mut media_list = self.media_queries.write_with(&mut guard);
        let new_vec = media_list.media_queries.drain(..)
                                .filter(|q| m_serialized != q.to_css_string())
                                .collect();
        media_list.media_queries = new_vec;
    }
}
<commit_msg>Auto merge of #16519 - emilio:unused-import, r=emilio<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use cssparser::Parser;
use dom::bindings::codegen::Bindings::MediaListBinding;
use dom::bindings::codegen::Bindings::MediaListBinding::MediaListMethods;
use dom::bindings::js::{JS, Root};
use dom::bindings::reflector::{DomObject, Reflector, reflect_dom_object};
use dom::bindings::str::DOMString;
use dom::cssstylesheet::CSSStyleSheet;
use dom::window::Window;
use dom_struct::dom_struct;
use std::sync::Arc;
use style::media_queries::{MediaQuery, parse_media_query_list};
use style::media_queries::MediaList as StyleMediaList;
use style::parser::{LengthParsingMode, ParserContext};
use style::shared_lock::{SharedRwLock, Locked};
use style::stylesheets::CssRuleType;
use style_traits::ToCss;

#[dom_struct]
pub struct MediaList {
    reflector_: Reflector,
    parent_stylesheet: JS<CSSStyleSheet>,
    #[ignore_heap_size_of = "Arc"]
    media_queries: Arc<Locked<StyleMediaList>>,
}

impl MediaList {
    #[allow(unrooted_must_root)]
    pub fn new_inherited(parent_stylesheet: &CSSStyleSheet,
                         media_queries: Arc<Locked<StyleMediaList>>) -> MediaList {
        MediaList {
            parent_stylesheet: JS::from_ref(parent_stylesheet),
            reflector_: Reflector::new(),
            media_queries: media_queries,
        }
    }

    #[allow(unrooted_must_root)]
    pub fn new(window: &Window, parent_stylesheet: &CSSStyleSheet,
               media_queries: Arc<Locked<StyleMediaList>>)
        -> Root<MediaList> {
        reflect_dom_object(box MediaList::new_inherited(parent_stylesheet, media_queries),
                           window,
                           MediaListBinding::Wrap)
    }

    fn shared_lock(&self) -> &SharedRwLock {
        &self.parent_stylesheet.style_stylesheet().shared_lock
    }
}

impl MediaListMethods for MediaList {
    // https://drafts.csswg.org/cssom/#dom-medialist-mediatext
    fn MediaText(&self) -> DOMString {
        let guard = self.shared_lock().read();
        DOMString::from(self.media_queries.read_with(&guard).to_css_string())
    }

    // https://drafts.csswg.org/cssom/#dom-medialist-mediatext
    fn SetMediaText(&self, value: DOMString) {
        let mut guard = self.shared_lock().write();
        let mut media_queries = self.media_queries.write_with(&mut guard);
        // Step 2
        if value.is_empty() {
            // Step 1
            *media_queries = StyleMediaList::empty();
            return;
        }
        // Step 3
        let mut parser = Parser::new(&value);
        let global = self.global();
        let win = global.as_window();
        let url = win.get_url();
        let context = ParserContext::new_for_cssom(&url, win.css_error_reporter(), Some(CssRuleType::Media),
                                                   LengthParsingMode::Default);
        *media_queries = parse_media_query_list(&context, &mut parser);
    }

    // https://drafts.csswg.org/cssom/#dom-medialist-length
    fn Length(&self) -> u32 {
        let guard = self.shared_lock().read();
        self.media_queries.read_with(&guard).media_queries.len() as u32
    }

    // https://drafts.csswg.org/cssom/#dom-medialist-item
    fn Item(&self, index: u32) -> Option<DOMString> {
        let guard = self.shared_lock().read();
        self.media_queries.read_with(&guard).media_queries
        .get(index as usize).and_then(|query| {
            let mut s = String::new();
            query.to_css(&mut s).unwrap();
            Some(DOMString::from_string(s))
        })
    }

    // https://drafts.csswg.org/cssom/#dom-medialist-item
    fn IndexedGetter(&self, index: u32) -> Option<DOMString> {
        self.Item(index)
    }

    // https://drafts.csswg.org/cssom/#dom-medialist-appendmedium
    fn AppendMedium(&self, medium: DOMString) {
        // Step 1
        let mut parser = Parser::new(&medium);
        let global = self.global();
        let win = global.as_window();
        let url = win.get_url();
        let context = ParserContext::new_for_cssom(&url, win.css_error_reporter(), Some(CssRuleType::Media),
                                                   LengthParsingMode::Default);
        let m = MediaQuery::parse(&context, &mut parser);
        // Step 2
        if let Err(_) = m {
            return;
        }
        // Step 3
        let m_serialized = m.clone().unwrap().to_css_string();
        let mut guard = self.shared_lock().write();
        let mq = self.media_queries.write_with(&mut guard);
        let any = mq.media_queries.iter().any(|q| m_serialized == q.to_css_string());
        if any {
            return;
        }
        // Step 4
        mq.media_queries.push(m.unwrap());
    }

    // https://drafts.csswg.org/cssom/#dom-medialist-deletemedium
    fn DeleteMedium(&self, medium: DOMString) {
        // Step 1
        let mut parser = Parser::new(&medium);
        let global = self.global();
        let win = global.as_window();
        let url = win.get_url();
        let context = ParserContext::new_for_cssom(&url, win.css_error_reporter(), Some(CssRuleType::Media),
                                                   LengthParsingMode::Default);
        let m = MediaQuery::parse(&context, &mut parser);
        // Step 2
        if let Err(_) = m {
            return;
        }
        // Step 3
        let m_serialized = m.unwrap().to_css_string();
        let mut guard = self.shared_lock().write();
        let mut media_list = self.media_queries.write_with(&mut guard);
        let new_vec = media_list.media_queries.drain(..)
                                .filter(|q| m_serialized != q.to_css_string())
                                .collect();
        media_list.media_queries = new_vec;
    }
}
<|endoftext|>


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

use dom::bindings::cell::DOMRefCell;
use dom::bindings::codegen::Bindings::WebSocketBinding;
use dom::bindings::codegen::Bindings::WebSocketBinding::WebSocketMethods;
use dom::bindings::codegen::Bindings::EventHandlerBinding::EventHandlerNonNull;
use dom::bindings::codegen::InheritTypes::EventTargetCast;
use dom::bindings::codegen::InheritTypes::EventCast;
use dom::bindings::error::{Error, Fallible};
use dom::bindings::error::Error::{InvalidAccess, Syntax};
use dom::bindings::global::{GlobalField, GlobalRef};
use dom::bindings::js::Root;
use dom::bindings::refcounted::Trusted;
use dom::bindings::str::USVString;
use dom::bindings::trace::JSTraceable;
use dom::bindings::utils::reflect_dom_object;
use dom::closeevent::CloseEvent;
use dom::event::{Event, EventBubbles, EventCancelable, EventHelpers};
use dom::eventtarget::{EventTarget, EventTargetHelpers, EventTargetTypeId};
use script_task::Runnable;
use script_task::ScriptMsg;
use std::cell::{Cell, RefCell};
use std::borrow::ToOwned;
use util::str::DOMString;

use hyper::header::Host;
use websocket::Message;
use websocket::ws::sender::Sender as Sender_Object;
use websocket::client::sender::Sender;
use websocket::client::receiver::Receiver;
use websocket::stream::WebSocketStream;
use websocket::client::request::Url;
use websocket::Client;
use websocket::header::Origin;
use websocket::result::WebSocketResult;
use websocket::ws::util::url::parse_url;

#[derive(JSTraceable, PartialEq, Copy, Clone)]
enum WebSocketRequestState {
    Connecting = 0,
    Open = 1,
    Closing = 2,
    Closed = 3,
}

no_jsmanaged_fields!(Sender<WebSocketStream>);
no_jsmanaged_fields!(Receiver<WebSocketStream>);

#[dom_struct]
pub struct WebSocket {
    eventtarget: EventTarget,
    url: Url,
    global: GlobalField,
    ready_state: Cell<WebSocketRequestState>,
    sender: RefCell<Option<Sender<WebSocketStream>>>,
    receiver: RefCell<Option<Receiver<WebSocketStream>>>,
    failed: Cell<bool>, //Flag to tell if websocket was closed due to failure
    full: Cell<bool>, //Flag to tell if websocket queue is full
    clean_close: Cell<bool>, //Flag to tell if the websocket closed cleanly (not due to full or fail)
    code: Cell<u16>, //Closing code
    reason: DOMRefCell<DOMString>, //Closing reason
    data: DOMRefCell<DOMString>, //Data from send - TODO: Remove after buffer is added.
}

/// *Establish a WebSocket Connection* as defined in RFC 6455.
fn establish_a_websocket_connection(url: (Host, String, bool), origin: String)
    -> WebSocketResult<(Sender<WebSocketStream>, Receiver<WebSocketStream>)> {
    let mut request = try!(Client::connect(url));
    request.headers.set(Origin(origin));

    let response = try!(request.send());
    try!(response.validate());

    Ok(response.begin().split())
}

impl WebSocket {
    pub fn new_inherited(global: GlobalRef, url: Url) -> WebSocket {
        WebSocket {
            eventtarget: EventTarget::new_inherited(EventTargetTypeId::WebSocket),
            url: url,
            global: GlobalField::from_rooted(&global),
            ready_state: Cell::new(WebSocketRequestState::Connecting),
            failed: Cell::new(false),
            sender: RefCell::new(None),
            receiver: RefCell::new(None),
            full: Cell::new(false),
            clean_close: Cell::new(true),
            code: Cell::new(0),
            reason: DOMRefCell::new("".to_owned()),
            data: DOMRefCell::new("".to_owned()),
        }

    }

    pub fn new(global: GlobalRef,
               url: DOMString,
               protocols: Option<DOMString>)
               -> Fallible<Root<WebSocket>> {
        // Step 1.
        let parsed_url = try!(Url::parse(&url).map_err(|_| Error::Syntax));
        let url = try!(parse_url(&parsed_url).map_err(|_| Error::Syntax));

        // Step 4.
        let protocols = protocols.as_slice();

        // Step 5.
        for (i, protocol) in protocols.iter().enumerate() {
            // https://tools.ietf.org/html/rfc6455#section-4.1
            // Handshake requirements, step 10
            if protocol.is_empty() {
                return Err(Syntax);
            }

            if protocols[i+1..].iter().any(|p| p == protocol) {
                return Err(Syntax);
            }

            if protocol.chars().any(|c| c < '\u{0021}' || c > '\u{007E}') {
                return Err(Syntax);
            }
        }

        /*TODO: This constructor is only a prototype, it does not accomplish the specs
          defined here:
          http://html.spec.whatwg.org
          The remaining 8 items must be satisfied.
          TODO: This constructor should be responsible for spawning a thread for the
          receive loop after ws.r().Open() - See comment
        */
        let ws = reflect_dom_object(box WebSocket::new_inherited(global, parsed_url),
                                    global,
                                    WebSocketBinding::Wrap);

        let channel = establish_a_websocket_connection(url, global.get_url().serialize());
        let (temp_sender, temp_receiver) = match channel {
            Ok(channel) => channel,
            Err(e) => {
                debug!("Failed to establish a WebSocket connection: {:?}", e);
                let global_root = ws.r().global.root();
                let address = Trusted::new(global_root.r().get_cx(), ws.r(), global_root.r().script_chan().clone());
                let task = box WebSocketTaskHandler::new(address, WebSocketTask::Close);
                global_root.r().script_chan().send(ScriptMsg::RunnableMsg(task)).unwrap();
                return Ok(ws);
            }
        };

        *ws.r().sender.borrow_mut() = Some(temp_sender);
        *ws.r().receiver.borrow_mut() = Some(temp_receiver);

        //Create everything necessary for starting the open asynchronous task, then begin the task.
        let global_root = ws.r().global.root();
        let addr: Trusted<WebSocket> =
            Trusted::new(global_root.r().get_cx(), ws.r(), global_root.r().script_chan().clone());
        let open_task = box WebSocketTaskHandler::new(addr, WebSocketTask::ConnectionEstablished);
        global_root.r().script_chan().send(ScriptMsg::RunnableMsg(open_task)).unwrap();
        //TODO: Spawn thread here for receive loop
        /*TODO: Add receive loop here and make new thread run this
          Receive is an infinite loop "similiar" the one shown here:
          https://github.com/cyderize/rust-websocket/blob/master/examples/client.rs#L64
          TODO: The receive loop however does need to follow the spec. These are outlined here
          under "WebSocket message has been received" items 1-5:
          https://github.com/cyderize/rust-websocket/blob/master/examples/client.rs#L64
          TODO: The receive loop also needs to dispatch an asynchronous event as stated here:
          https://github.com/cyderize/rust-websocket/blob/master/examples/client.rs#L64
          TODO: When the receive loop receives a close message from the server,
          it confirms the websocket is now closed. This requires the close event
          to be fired (dispatch_close fires the close event - see implementation below)
        */
        Ok(ws)
    }

    pub fn Constructor(global: GlobalRef,
                       url: DOMString,
                       protocols: Option<DOMString>)
                       -> Fallible<Root<WebSocket>> {
        WebSocket::new(global, url, protocols)
    }
}

impl<'a> WebSocketMethods for &'a WebSocket {
    event_handler!(open, GetOnopen, SetOnopen);
    event_handler!(close, GetOnclose, SetOnclose);
    event_handler!(error, GetOnerror, SetOnerror);

    fn Url(self) -> DOMString {
        self.url.serialize()
    }

    fn ReadyState(self) -> u16 {
        self.ready_state.get() as u16
    }

    fn Send(self, data: Option<USVString>) -> Fallible<()> {
        match self.ready_state.get() {
            WebSocketRequestState::Connecting => {
                return Err(Error::InvalidState);
            },
            WebSocketRequestState::Open => (),
            WebSocketRequestState::Closing | WebSocketRequestState::Closed => {
                // TODO: Update bufferedAmount.
                return Ok(());
            }
        }

        /*TODO: This is not up to spec see http://html.spec.whatwg.org/multipage/comms.html search for
                "If argument is a string"
          TODO: Need to buffer data
          TODO: bufferedAmount attribute returns the size of the buffer in bytes -
                this is a required attribute defined in the websocket.webidl file
          TODO: The send function needs to flag when full by using the following
          self.full.set(true). This needs to be done when the buffer is full
        */
        let mut other_sender = self.sender.borrow_mut();
        let my_sender = other_sender.as_mut().unwrap();
        let _ = my_sender.send_message(Message::Text(data.unwrap().0));
        return Ok(())
    }

    fn Close(self, code: Option<u16>, reason: Option<USVString>) -> Fallible<()>{
        fn send_close(this: &WebSocket) {
            this.ready_state.set(WebSocketRequestState::Closing);

            let mut sender = this.sender.borrow_mut();
            //TODO: Also check if the buffer is full
            if let Some(sender) = sender.as_mut() {
                let _ = sender.send_message(Message::Close(None));
            }
        }

        if let Some(code) = code {
            //Check code is NOT 1000 NOR in the range of 3000-4999 (inclusive)
            if  code != 1000 && (code < 3000 || code > 4999) {
                return Err(Error::InvalidAccess);
            }
        }
        if let Some(ref reason) = reason {
            if reason.0.as_bytes().len() > 123 { //reason cannot be larger than 123 bytes
                return Err(Error::Syntax);
            }
        }

        match self.ready_state.get() {
            WebSocketRequestState::Closing | WebSocketRequestState::Closed  => {} //Do nothing
            WebSocketRequestState::Connecting => { //Connection is not yet established
                /*By setting the state to closing, the open function
                  will abort connecting the websocket*/
                self.failed.set(true);
                send_close(self);
                //Note: After sending the close message, the receive loop confirms a close message from the server and
                //      must fire a close event
            }
            WebSocketRequestState::Open => {
                //Closing handshake not started - still in open
                //Start the closing by setting the code and reason if they exist
                if let Some(code) = code {
                    self.code.set(code);
                }
                if let Some(reason) = reason {
                    *self.reason.borrow_mut() = reason.0;
                }
                send_close(self);
                //Note: After sending the close message, the receive loop confirms a close message from the server and
                //      must fire a close event
            }
        }
        Ok(()) //Return Ok
    }
}

pub enum WebSocketTask {
    /// Task queued when *the WebSocket connection is established*.
    ConnectionEstablished,
    Close,
}

pub struct WebSocketTaskHandler {
    addr: Trusted<WebSocket>,
    task: WebSocketTask,
}

impl WebSocketTaskHandler {
    pub fn new(addr: Trusted<WebSocket>, task: WebSocketTask) -> WebSocketTaskHandler {
        WebSocketTaskHandler {
            addr: addr,
            task: task,
        }
    }

    fn connection_established(&self) {
        /*TODO: Items 1, 3, 4, & 5 under "WebSocket connection is established" as specified here:
          https://html.spec.whatwg.org/multipage/#feedback-from-the-protocol
        */
        let ws = self.addr.root();

        // Step 1: Protocols.

        // Step 2.
        ws.ready_state.set(WebSocketRequestState::Open);

        // Step 3: Extensions.
        // Step 4: Protocols.
        // Step 5: Cookies.

        // Step 6.
        let global = ws.global.root();
        let event = Event::new(global.r(), "open".to_owned(),
                               EventBubbles::DoesNotBubble,
                               EventCancelable::NotCancelable);
        event.fire(EventTargetCast::from_ref(ws.r()));
    }

    fn dispatch_close(&self) {
        let ws = self.addr.root();
        let ws = ws.r();
        let global = ws.global.root();
        ws.ready_state.set(WebSocketRequestState::Closed);
        //If failed or full, fire error event
        if ws.failed.get() || ws.full.get() {
            ws.failed.set(false);
            ws.full.set(false);
            //A Bad close
            ws.clean_close.set(false);
            let event = Event::new(global.r(),
                                   "error".to_owned(),
                                   EventBubbles::DoesNotBubble,
                                   EventCancelable::Cancelable);
            let target = EventTargetCast::from_ref(ws);
            event.r().fire(target);
        }
        let rsn = ws.reason.borrow();
        let rsn_clone = rsn.clone();
        /*In addition, we also have to fire a close even if error event fired
         https://html.spec.whatwg.org/multipage/#closeWebSocket
        */
        let close_event = CloseEvent::new(global.r(),
                                          "close".to_owned(),
                                          EventBubbles::DoesNotBubble,
                                          EventCancelable::NotCancelable,
                                          ws.clean_close.get(),
                                          ws.code.get(),
                                          rsn_clone);
        let target = EventTargetCast::from_ref(ws);
        let event = EventCast::from_ref(close_event.r());
        event.fire(target);
    }
}

impl Runnable for WebSocketTaskHandler {
    fn handler(self: Box<WebSocketTaskHandler>) {
        match self.task {
            WebSocketTask::ConnectionEstablished => {
                self.connection_established();
            }
            WebSocketTask::Close => {
                self.dispatch_close();
            }
        }
    }
}

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

use dom::bindings::cell::DOMRefCell;
use dom::bindings::codegen::Bindings::WebSocketBinding;
use dom::bindings::codegen::Bindings::WebSocketBinding::WebSocketMethods;
use dom::bindings::codegen::Bindings::EventHandlerBinding::EventHandlerNonNull;
use dom::bindings::codegen::InheritTypes::EventTargetCast;
use dom::bindings::codegen::InheritTypes::EventCast;
use dom::bindings::error::{Error, Fallible};
use dom::bindings::error::Error::{InvalidAccess, Syntax};
use dom::bindings::global::{GlobalField, GlobalRef};
use dom::bindings::js::Root;
use dom::bindings::refcounted::Trusted;
use dom::bindings::str::USVString;
use dom::bindings::trace::JSTraceable;
use dom::bindings::utils::reflect_dom_object;
use dom::closeevent::CloseEvent;
use dom::event::{Event, EventBubbles, EventCancelable, EventHelpers};
use dom::eventtarget::{EventTarget, EventTargetHelpers, EventTargetTypeId};
use script_task::Runnable;
use script_task::ScriptMsg;
use std::cell::{Cell, RefCell};
use std::borrow::ToOwned;
use util::str::DOMString;

use hyper::header::Host;
use websocket::Message;
use websocket::ws::sender::Sender as Sender_Object;
use websocket::client::sender::Sender;
use websocket::client::receiver::Receiver;
use websocket::stream::WebSocketStream;
use websocket::client::request::Url;
use websocket::Client;
use websocket::header::Origin;
use websocket::result::WebSocketResult;
use websocket::ws::util::url::parse_url;

#[derive(JSTraceable, PartialEq, Copy, Clone)]
enum WebSocketRequestState {
    Connecting = 0,
    Open = 1,
    Closing = 2,
    Closed = 3,
}

no_jsmanaged_fields!(Sender<WebSocketStream>);
no_jsmanaged_fields!(Receiver<WebSocketStream>);

#[dom_struct]
pub struct WebSocket {
    eventtarget: EventTarget,
    url: Url,
    global: GlobalField,
    ready_state: Cell<WebSocketRequestState>,
    sender: RefCell<Option<Sender<WebSocketStream>>>,
    receiver: RefCell<Option<Receiver<WebSocketStream>>>,
    failed: Cell<bool>, //Flag to tell if websocket was closed due to failure
    full: Cell<bool>, //Flag to tell if websocket queue is full
    clean_close: Cell<bool>, //Flag to tell if the websocket closed cleanly (not due to full or fail)
    code: Cell<u16>, //Closing code
    reason: DOMRefCell<DOMString>, //Closing reason
    data: DOMRefCell<DOMString>, //Data from send - TODO: Remove after buffer is added.
}

/// *Establish a WebSocket Connection* as defined in RFC 6455.
fn establish_a_websocket_connection(url: (Host, String, bool), origin: String)
    -> WebSocketResult<(Sender<WebSocketStream>, Receiver<WebSocketStream>)> {
    let mut request = try!(Client::connect(url));
    request.headers.set(Origin(origin));

    let response = try!(request.send());
    try!(response.validate());

    Ok(response.begin().split())
}

impl WebSocket {
    pub fn new_inherited(global: GlobalRef, url: Url) -> WebSocket {
        WebSocket {
            eventtarget: EventTarget::new_inherited(EventTargetTypeId::WebSocket),
            url: url,
            global: GlobalField::from_rooted(&global),
            ready_state: Cell::new(WebSocketRequestState::Connecting),
            failed: Cell::new(false),
            sender: RefCell::new(None),
            receiver: RefCell::new(None),
            full: Cell::new(false),
            clean_close: Cell::new(true),
            code: Cell::new(0),
            reason: DOMRefCell::new("".to_owned()),
            data: DOMRefCell::new("".to_owned()),
        }

    }

    pub fn new(global: GlobalRef,
               url: DOMString,
               protocols: Option<DOMString>)
               -> Fallible<Root<WebSocket>> {
        // Step 1.
        let parsed_url = try!(Url::parse(&url).map_err(|_| Error::Syntax));
        let url = try!(parse_url(&parsed_url).map_err(|_| Error::Syntax));

        // Step 4.
        let protocols = protocols.as_slice();

        // Step 5.
        for (i, protocol) in protocols.iter().enumerate() {
            // https://tools.ietf.org/html/rfc6455#section-4.1
            // Handshake requirements, step 10
            if protocol.is_empty() {
                return Err(Syntax);
            }

            if protocols[i+1..].iter().any(|p| p == protocol) {
                return Err(Syntax);
            }

            if protocol.chars().any(|c| c < '\u{0021}' || c > '\u{007E}') {
                return Err(Syntax);
            }
        }

        /*TODO: This constructor is only a prototype, it does not accomplish the specs
          defined here:
          http://html.spec.whatwg.org
          The remaining 8 items must be satisfied.
          TODO: This constructor should be responsible for spawning a thread for the
          receive loop after ws.r().Open() - See comment
        */
        let ws = reflect_dom_object(box WebSocket::new_inherited(global, parsed_url),
                                    global,
                                    WebSocketBinding::Wrap);

        let channel = establish_a_websocket_connection(url, global.get_url().serialize());
        let (temp_sender, temp_receiver) = match channel {
            Ok(channel) => channel,
            Err(e) => {
                debug!("Failed to establish a WebSocket connection: {:?}", e);
                let global_root = ws.r().global.root();
                let address = Trusted::new(global_root.r().get_cx(), ws.r(), global_root.r().script_chan().clone());
                let task = box CloseTask {
                    addr: address,
                };
                global_root.r().script_chan().send(ScriptMsg::RunnableMsg(task)).unwrap();
                return Ok(ws);
            }
        };

        *ws.r().sender.borrow_mut() = Some(temp_sender);
        *ws.r().receiver.borrow_mut() = Some(temp_receiver);

        //Create everything necessary for starting the open asynchronous task, then begin the task.
        let global_root = ws.r().global.root();
        let addr: Trusted<WebSocket> =
            Trusted::new(global_root.r().get_cx(), ws.r(), global_root.r().script_chan().clone());
        let open_task = box ConnectionEstablishedTask {
            addr: addr,
        };
        global_root.r().script_chan().send(ScriptMsg::RunnableMsg(open_task)).unwrap();
        //TODO: Spawn thread here for receive loop
        /*TODO: Add receive loop here and make new thread run this
          Receive is an infinite loop "similiar" the one shown here:
          https://github.com/cyderize/rust-websocket/blob/master/examples/client.rs#L64
          TODO: The receive loop however does need to follow the spec. These are outlined here
          under "WebSocket message has been received" items 1-5:
          https://github.com/cyderize/rust-websocket/blob/master/examples/client.rs#L64
          TODO: The receive loop also needs to dispatch an asynchronous event as stated here:
          https://github.com/cyderize/rust-websocket/blob/master/examples/client.rs#L64
          TODO: When the receive loop receives a close message from the server,
          it confirms the websocket is now closed. This requires the close event
          to be fired (dispatch_close fires the close event - see implementation below)
        */
        Ok(ws)
    }

    pub fn Constructor(global: GlobalRef,
                       url: DOMString,
                       protocols: Option<DOMString>)
                       -> Fallible<Root<WebSocket>> {
        WebSocket::new(global, url, protocols)
    }
}

impl<'a> WebSocketMethods for &'a WebSocket {
    event_handler!(open, GetOnopen, SetOnopen);
    event_handler!(close, GetOnclose, SetOnclose);
    event_handler!(error, GetOnerror, SetOnerror);

    fn Url(self) -> DOMString {
        self.url.serialize()
    }

    fn ReadyState(self) -> u16 {
        self.ready_state.get() as u16
    }

    fn Send(self, data: Option<USVString>) -> Fallible<()> {
        match self.ready_state.get() {
            WebSocketRequestState::Connecting => {
                return Err(Error::InvalidState);
            },
            WebSocketRequestState::Open => (),
            WebSocketRequestState::Closing | WebSocketRequestState::Closed => {
                // TODO: Update bufferedAmount.
                return Ok(());
            }
        }

        /*TODO: This is not up to spec see http://html.spec.whatwg.org/multipage/comms.html search for
                "If argument is a string"
          TODO: Need to buffer data
          TODO: bufferedAmount attribute returns the size of the buffer in bytes -
                this is a required attribute defined in the websocket.webidl file
          TODO: The send function needs to flag when full by using the following
          self.full.set(true). This needs to be done when the buffer is full
        */
        let mut other_sender = self.sender.borrow_mut();
        let my_sender = other_sender.as_mut().unwrap();
        let _ = my_sender.send_message(Message::Text(data.unwrap().0));
        return Ok(())
    }

    fn Close(self, code: Option<u16>, reason: Option<USVString>) -> Fallible<()>{
        fn send_close(this: &WebSocket) {
            this.ready_state.set(WebSocketRequestState::Closing);

            let mut sender = this.sender.borrow_mut();
            //TODO: Also check if the buffer is full
            if let Some(sender) = sender.as_mut() {
                let _ = sender.send_message(Message::Close(None));
            }
        }

        if let Some(code) = code {
            //Check code is NOT 1000 NOR in the range of 3000-4999 (inclusive)
            if  code != 1000 && (code < 3000 || code > 4999) {
                return Err(Error::InvalidAccess);
            }
        }
        if let Some(ref reason) = reason {
            if reason.0.as_bytes().len() > 123 { //reason cannot be larger than 123 bytes
                return Err(Error::Syntax);
            }
        }

        match self.ready_state.get() {
            WebSocketRequestState::Closing | WebSocketRequestState::Closed  => {} //Do nothing
            WebSocketRequestState::Connecting => { //Connection is not yet established
                /*By setting the state to closing, the open function
                  will abort connecting the websocket*/
                self.failed.set(true);
                send_close(self);
                //Note: After sending the close message, the receive loop confirms a close message from the server and
                //      must fire a close event
            }
            WebSocketRequestState::Open => {
                //Closing handshake not started - still in open
                //Start the closing by setting the code and reason if they exist
                if let Some(code) = code {
                    self.code.set(code);
                }
                if let Some(reason) = reason {
                    *self.reason.borrow_mut() = reason.0;
                }
                send_close(self);
                //Note: After sending the close message, the receive loop confirms a close message from the server and
                //      must fire a close event
            }
        }
        Ok(()) //Return Ok
    }
}

/// Task queued when *the WebSocket connection is established*.
struct ConnectionEstablishedTask {
    addr: Trusted<WebSocket>,
}

impl Runnable for ConnectionEstablishedTask {
    fn handler(self: Box<Self>) {
        let ws = self.addr.root();

        // Step 1: Protocols.

        // Step 2.
        ws.ready_state.set(WebSocketRequestState::Open);

        // Step 3: Extensions.
        // Step 4: Protocols.
        // Step 5: Cookies.

        // Step 6.
        let global = ws.global.root();
        let event = Event::new(global.r(), "open".to_owned(),
                               EventBubbles::DoesNotBubble,
                               EventCancelable::NotCancelable);
        event.fire(EventTargetCast::from_ref(ws.r()));
    }
}

struct CloseTask {
    addr: Trusted<WebSocket>,
}

impl Runnable for CloseTask {
    fn handler(self: Box<Self>) {
        let ws = self.addr.root();
        let ws = ws.r();
        let global = ws.global.root();
        ws.ready_state.set(WebSocketRequestState::Closed);
        //If failed or full, fire error event
        if ws.failed.get() || ws.full.get() {
            ws.failed.set(false);
            ws.full.set(false);
            //A Bad close
            ws.clean_close.set(false);
            let event = Event::new(global.r(),
                                   "error".to_owned(),
                                   EventBubbles::DoesNotBubble,
                                   EventCancelable::Cancelable);
            let target = EventTargetCast::from_ref(ws);
            event.r().fire(target);
        }
        let rsn = ws.reason.borrow();
        let rsn_clone = rsn.clone();
        /*In addition, we also have to fire a close even if error event fired
         https://html.spec.whatwg.org/multipage/#closeWebSocket
        */
        let close_event = CloseEvent::new(global.r(),
                                          "close".to_owned(),
                                          EventBubbles::DoesNotBubble,
                                          EventCancelable::NotCancelable,
                                          ws.clean_close.get(),
                                          ws.code.get(),
                                          rsn_clone);
        let target = EventTargetCast::from_ref(ws);
        let event = EventCast::from_ref(close_event.r());
        event.fire(target);
    }
}
<|endoftext|>


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

use dom::bindings::codegen::Bindings::WebSocketBinding;
use dom::bindings::codegen::Bindings::WebSocketBinding::WebSocketMethods;
use dom::bindings::error::Fallible;
use dom::bindings::global::GlobalRef;
use dom::bindings::js::{Temporary, JSRef};
use dom::bindings::utils::reflect_dom_object;
use dom::eventtarget::{EventTarget, EventTargetTypeId};
use util::str::DOMString;

use websocket::{Message, Sender, Receiver};
use websocket::client::request::Url;
use websocket::Client;
use std::cell::Cell;

#[derive(PartialEq, Copy)]
#[jstraceable]
enum WebsocketRequestState {
	Unsent = 0,
	Opened = 1,
	Sending = 2,
	Receiving = 3,
}

#[dom_struct]
pub struct WebSocket {
    eventtarget: EventTarget,
    url: DOMString,
	ready_state: Cell<WebsocketRequestState>
}

impl WebSocket {
    pub fn new_inherited(url: DOMString) -> WebSocket {
        WebSocket {
            eventtarget: EventTarget::new_inherited(EventTargetTypeId::WebSocket),
            url: url,
		ready_state: Cell::new(WebsocketRequestState::Unsent)
        }
    }

    pub fn new(global: GlobalRef, url: DOMString) -> Temporary<WebSocket> {
        reflect_dom_object(box WebSocket::new_inherited(url),
                           global,
                           WebSocketBinding::Wrap)
    }

    pub fn Constructor(global: GlobalRef, url: DOMString) -> Fallible<Temporary<WebSocket>> {
        Ok(WebSocket::new(global, url))
    }
}

impl<'a> WebSocketMethods for JSRef<'a, WebSocket> {
    // https://html.spec.whatwg.org/#dom-websocket-url
    fn Url(self) -> DOMString {
       self.url.clone()
    }

   fn ReadyState(self) -> u16 {
   	self.ready_state.get() as u16
   }
}
<commit_msg>Websocket creation is occurring. Opening still needs to take place, however, and tracking where stuff stalls.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use dom::bindings::codegen::Bindings::WebSocketBinding;
use dom::bindings::codegen::Bindings::WebSocketBinding::WebSocketMethods;
use dom::bindings::error::Fallible;
use dom::bindings::global::GlobalRef;
use dom::bindings::js::{Temporary, JSRef};
use dom::bindings::utils::reflect_dom_object;
use dom::eventtarget::{EventTarget, EventTargetTypeId};
use util::str::DOMString;

use websocket::{Message, Sender, Receiver};
use websocket::client::request::Url;
use websocket::Client;
use std::cell::Cell;

#[derive(PartialEq, Copy)]
#[jstraceable]
enum WebsocketRequestState {
	Unsent = 0,
	Opened = 1,
	Sending = 2,
	Receiving = 3,
	Closing = 4,
	Closed = 5,
}

#[dom_struct]
pub struct WebSocket {
    eventtarget: EventTarget,
    url: DOMString,
	ready_state: Cell<WebsocketRequestState>
}

impl WebSocket {
    pub fn new_inherited(url: DOMString) -> WebSocket {
        println!("Creating websocket...");
	WebSocket {
            eventtarget: EventTarget::new_inherited(EventTargetTypeId::WebSocket),
            url: url,
		ready_state: Cell::new(WebsocketRequestState::Unsent)
        }
    }

    pub fn new(global: GlobalRef, url: DOMString) -> Temporary<WebSocket> {
        reflect_dom_object(box WebSocket::new_inherited(url),
                           global,
                           WebSocketBinding::Wrap)
    }

    pub fn Constructor(global: GlobalRef, url: DOMString) -> Fallible<Temporary<WebSocket>> {
        Ok(WebSocket::new(global, url))
    }
}

impl<'a> WebSocketMethods for JSRef<'a, WebSocket> {
    // https://html.spec.whatwg.org/#dom-websocket-url
    fn Url(self) -> DOMString {
	println!("Setting URL");
       self.url.clone()
    }

   fn ReadyState(self) -> u16 {
   	self.ready_state.get() as u16
   }
}
<|endoftext|>


<commit_before>use std::sync::{Arc, Mutex};
use std::io::{self, Read, Write};
use std::io::ErrorKind::WouldBlock;
use std::net::Shutdown;

use rustls;
use rustls::Session;

use soio::Token;
use soio::tcp::TcpStream;
use soio::channel;
use soio::Evented;
use soio::Poll;
use soio::Ready;
use soio::PollOpt;

use threading::Pool;

use super::Handle;
use super::stream::Stream;

pub enum Event {
    Write(Token),
    Read(Token),
    WriteTls(Token),
}

pub struct Connection {
    socket: TcpStream,
    token: Token,
    thread_pool: Pool,
    tx: channel::Sender<Event>,
    stream: Arc<Mutex<Stream>>,
    pub closing: bool,
    handle: Arc<Handle>,
    tls_session: Option<rustls::ServerSession>,
}

impl Connection {
    pub fn new(socket: TcpStream, token: Token, thread_pool: Pool, tx: channel::Sender<Event>, handle: Arc<Handle>, tls_session: Option<rustls::ServerSession>) -> Connection {
        Connection {
            socket: socket,
            token: token,
            thread_pool: thread_pool,
            tx: tx,
            stream: Arc::new(Mutex::new(Stream::new(Vec::new(), Vec::with_capacity(1024)))),
            closing: false,
            handle: handle,
            tls_session: tls_session,
        }
    }

    pub fn reader(&mut self) {
        match self.tls_session {
            Some(ref mut tls_session) => {

                match tls_session.read_tls(&mut self.socket) {
                    Ok(size) => {
                        if size == 0 {
                            self.closing = true;
                        }
                    },
                    Err(err) => {
                        if let WouldBlock = err.kind() {
                            return;
                        }

                        self.closing = true;
                        return;
                    }
                }

                if let Err(_) = tls_session.process_new_packets() {
                    self.closing = true;
                    return;
                }

                let mut buf = Vec::new();

                if let Err(_) = tls_session.read_to_end(&mut buf) {
                    self.closing = true;
                    return;
                }

                if !buf.is_empty() {

                    let mut stream = self.stream.lock().unwrap();

                    stream.reader = buf;

                    stream.remote_addr = self.socket.peer_addr().unwrap();

                    let tx = self.tx.clone();
                    let token = self.token.clone();

                    let handle = self.handle.clone();

                    let stream = self.stream.clone();

                    self.thread_pool.spawn(move || {

                        handle(stream);

                        tx.send(Event::WriteTls(token)).is_ok();

                    });

                } else {
                    let rd = tls_session.wants_read();
                    let wr = tls_session.wants_write();

                    if rd && wr {
                        self.tx.send(Event::Read(self.token)).is_ok();
                        self.tx.send(Event::Write(self.token)).is_ok();
                    } else if wr {
                        self.tx.send(Event::Write(self.token)).is_ok();
                    } else {
                        self.tx.send(Event::Read(self.token)).is_ok();
                    }
                }

            },
            None => {

                let mut stream = self.stream.lock().unwrap();

                loop {

                    let mut buf = [0; 1024];

                    match self.socket.read(&mut buf) {
                        Ok(size) => {
                            if size == 0 {
                                self.closing = true;
                                return;
                            } else {
                                stream.reader.extend_from_slice(&buf[0..size]);
                            }
                        },
                        Err(err) => {
                            if let WouldBlock = err.kind() {
                                break;
                            } else {
                                self.closing = true;
                                return;
                            }
                        }
                    }

                }

                stream.remote_addr = self.socket.peer_addr().unwrap();

                let tx = self.tx.clone();
                let token = self.token.clone();

                let handle = self.handle.clone();

                let stream = self.stream.clone();

                self.thread_pool.spawn(move || {

                    handle(stream);

                    tx.send(Event::Write(token)).is_ok();

                });

            },
        }
    }

    pub fn writer(&mut self) {
        match self.tls_session {
            Some(ref mut tls_session) => {

                if let Err(_) = tls_session.write_tls(&mut self.socket) {
                    self.closing = true;
                    return;
                }

                let rd = tls_session.wants_read();
                let wr = tls_session.wants_write();

                if rd && wr {
                    self.tx.send(Event::Read(self.token)).is_ok();
                    self.tx.send(Event::Write(self.token)).is_ok();
                } else if wr {
                    self.tx.send(Event::Write(self.token)).is_ok();
                } else {
                    self.tx.send(Event::Read(self.token)).is_ok();
                }
            },
            None => {
                let ref mut writer = self.stream.lock().unwrap().writer;

                match self.socket.write(writer) {
                    Ok(_) => {
                        writer.clear();
                    },
                    Err(_) => {
                        self.closing = true;
                        return;
                    }
                }

                self.tx.send(Event::Read(self.token)).is_ok();
            }
        }
    }

    pub fn write_to_tls(&mut self) {
        let ref mut writer = self.stream.lock().unwrap().writer;

        match self.tls_session {
            Some(ref mut tls_session) => {
                tls_session.write_all(writer).unwrap();

                let rd = tls_session.wants_read();
                let wr = tls_session.wants_write();

                if rd && wr {
                    self.tx.send(Event::Read(self.token)).is_ok();
                    self.tx.send(Event::Write(self.token)).is_ok();
                } else if wr {
                    self.tx.send(Event::Write(self.token)).is_ok();
                } else {
                    self.tx.send(Event::Read(self.token)).is_ok();
                }
            },
            None => ()
        }

        writer.clear();

    }

    pub fn shutdown(&self) {
        let _ = self.socket.shutdown(Shutdown::Both);
    }
}

impl Evented for Connection {
    fn register(&self, poll: &Poll, token: Token, interest: Ready, opts: PollOpt)
         -> io::Result<()>
    {
        self.socket.register(poll, token, interest, opts)
    }

    fn reregister(&self, poll: &Poll, token: Token, interest: Ready, opts: PollOpt)
         -> io::Result<()>
    {
        self.socket.reregister(poll, token, interest, opts)
    }

    fn deregister(&self, poll: &Poll) -> io::Result<()> {
        self.socket.deregister(poll)
    }
}
<commit_msg>optimeze reader buf<commit_after>use std::sync::{Arc, Mutex};
use std::io::{self, Read, Write};
use std::io::ErrorKind::WouldBlock;
use std::net::Shutdown;

use rustls;
use rustls::Session;

use soio::Token;
use soio::tcp::TcpStream;
use soio::channel;
use soio::Evented;
use soio::Poll;
use soio::Ready;
use soio::PollOpt;

use threading::Pool;

use super::Handle;
use super::stream::Stream;

pub enum Event {
    Write(Token),
    Read(Token),
    WriteTls(Token),
}

pub struct Connection {
    socket: TcpStream,
    token: Token,
    thread_pool: Pool,
    tx: channel::Sender<Event>,
    stream: Arc<Mutex<Stream>>,
    pub closing: bool,
    handle: Arc<Handle>,
    tls_session: Option<rustls::ServerSession>,
}

impl Connection {
    pub fn new(socket: TcpStream, token: Token, thread_pool: Pool, tx: channel::Sender<Event>, handle: Arc<Handle>, tls_session: Option<rustls::ServerSession>) -> Connection {
        Connection {
            socket: socket,
            token: token,
            thread_pool: thread_pool,
            tx: tx,
            stream: Arc::new(Mutex::new(Stream::new(Vec::with_capacity(1024), Vec::with_capacity(1024)))),
            closing: false,
            handle: handle,
            tls_session: tls_session,
        }
    }

    pub fn reader(&mut self) {
        match self.tls_session {
            Some(ref mut tls_session) => {

                match tls_session.read_tls(&mut self.socket) {
                    Ok(size) => {
                        if size == 0 {
                            self.closing = true;
                        }
                    },
                    Err(err) => {
                        if let WouldBlock = err.kind() {
                            return;
                        }

                        self.closing = true;
                        return;
                    }
                }

                if let Err(_) = tls_session.process_new_packets() {
                    self.closing = true;
                    return;
                }

                let mut stream = self.stream.lock().unwrap();

                stream.remote_addr = self.socket.peer_addr().unwrap();

                if let Err(_) = tls_session.read_to_end(&mut stream.reader) {
                    self.closing = true;
                    return;
                }

                if stream.len() != 0 {

                    let tx = self.tx.clone();
                    let token = self.token.clone();

                    let handle = self.handle.clone();

                    let stream = self.stream.clone();

                    self.thread_pool.spawn(move || {

                        handle(stream);

                        tx.send(Event::WriteTls(token)).is_ok();

                    });

                } else {
                    let rd = tls_session.wants_read();
                    let wr = tls_session.wants_write();

                    if rd && wr {
                        self.tx.send(Event::Read(self.token)).is_ok();
                        self.tx.send(Event::Write(self.token)).is_ok();
                    } else if wr {
                        self.tx.send(Event::Write(self.token)).is_ok();
                    } else {
                        self.tx.send(Event::Read(self.token)).is_ok();
                    }
                }

            },
            None => {

                let mut stream = self.stream.lock().unwrap();

                loop {

                    let mut buf = [0; 1024];

                    match self.socket.read(&mut buf) {
                        Ok(size) => {
                            if size == 0 {
                                self.closing = true;
                                return;
                            } else {
                                stream.reader.extend_from_slice(&buf[0..size]);
                            }
                        },
                        Err(err) => {
                            if let WouldBlock = err.kind() {
                                break;
                            } else {
                                self.closing = true;
                                return;
                            }
                        }
                    }

                }

                stream.remote_addr = self.socket.peer_addr().unwrap();

                let tx = self.tx.clone();
                let token = self.token.clone();

                let handle = self.handle.clone();

                let stream = self.stream.clone();

                self.thread_pool.spawn(move || {

                    handle(stream);

                    tx.send(Event::Write(token)).is_ok();

                });

            },
        }
    }

    pub fn writer(&mut self) {
        match self.tls_session {
            Some(ref mut tls_session) => {

                if let Err(_) = tls_session.write_tls(&mut self.socket) {
                    self.closing = true;
                    return;
                }

                let rd = tls_session.wants_read();
                let wr = tls_session.wants_write();

                if rd && wr {
                    self.tx.send(Event::Read(self.token)).is_ok();
                    self.tx.send(Event::Write(self.token)).is_ok();
                } else if wr {
                    self.tx.send(Event::Write(self.token)).is_ok();
                } else {
                    self.tx.send(Event::Read(self.token)).is_ok();
                }
            },
            None => {
                let ref mut writer = self.stream.lock().unwrap().writer;

                match self.socket.write(writer) {
                    Ok(_) => {
                        writer.clear();
                    },
                    Err(_) => {
                        self.closing = true;
                        return;
                    }
                }

                self.tx.send(Event::Read(self.token)).is_ok();
            }
        }
    }

    pub fn write_to_tls(&mut self) {
        let ref mut writer = self.stream.lock().unwrap().writer;

        match self.tls_session {
            Some(ref mut tls_session) => {
                tls_session.write_all(writer).unwrap();

                let rd = tls_session.wants_read();
                let wr = tls_session.wants_write();

                if rd && wr {
                    self.tx.send(Event::Read(self.token)).is_ok();
                    self.tx.send(Event::Write(self.token)).is_ok();
                } else if wr {
                    self.tx.send(Event::Write(self.token)).is_ok();
                } else {
                    self.tx.send(Event::Read(self.token)).is_ok();
                }
            },
            None => ()
        }

        writer.clear();

    }

    pub fn shutdown(&self) {
        let _ = self.socket.shutdown(Shutdown::Both);
    }
}

impl Evented for Connection {
    fn register(&self, poll: &Poll, token: Token, interest: Ready, opts: PollOpt)
         -> io::Result<()>
    {
        self.socket.register(poll, token, interest, opts)
    }

    fn reregister(&self, poll: &Poll, token: Token, interest: Ready, opts: PollOpt)
         -> io::Result<()>
    {
        self.socket.reregister(poll, token, interest, opts)
    }

    fn deregister(&self, poll: &Poll) -> io::Result<()> {
        self.socket.deregister(poll)
    }
}
<|endoftext|>


<commit_before>// bench command:
//
// ```
// $ rustup run nightly cargo bench
// ```

#![feature(test)]
extern crate rand;
extern crate test;
extern crate quicksort;
extern crate rust_quicksort;

#[cfg(test)]
mod benches {
    use rand::{Rng, SeedableRng, StdRng};
    use test;
    use quicksort;
    use rust_quicksort;

    static LENGTH: usize = 10000;
    static SEED: [usize; 4] = [1, 2, 3, 4];

    #[bench]
    fn bench_rust_quicksort(b: &mut test::Bencher) {
        let mut rng: StdRng = SeedableRng::from_seed(&SEED as &[_]);
	    let mut v: Vec<isize> = rng.gen_iter::<isize>().take(LENGTH).collect();

        b.iter(|| rust_quicksort::quicksort(&mut v))
    }

    #[bench]
    fn bench_quicksort(b: &mut test::Bencher) {
        let mut rng: StdRng = SeedableRng::from_seed(&SEED as &[_]);
	    let mut v: Vec<isize> = rng.gen_iter::<isize>().take(LENGTH).collect();

        b.iter(|| quicksort::quicksort(&mut v))
    }
}<commit_msg>add bench_sort()<commit_after>// bench command:
//
// ```
// $ rustup run nightly cargo bench
// ```

#![feature(test)]
extern crate rand;
extern crate test;
extern crate quicksort;
extern crate rust_quicksort;

#[cfg(test)]
mod benches {
    use rand::{Rng, SeedableRng, StdRng};
    use test;
    use quicksort;
    use rust_quicksort;

    static LENGTH: usize = 100000;
    static SEED: [usize; 4] = [1, 2, 3, 4];

    #[bench]
    fn bench_rust_quicksort(b: &mut test::Bencher) {
        let mut rng: StdRng = SeedableRng::from_seed(&SEED as &[_]);
	    let mut v: Vec<isize> = rng.gen_iter::<isize>().take(LENGTH).collect();

        b.iter(|| rust_quicksort::quicksort(&mut v))
    }

    #[bench]
    fn bench_quicksort(b: &mut test::Bencher) {
        let mut rng: StdRng = SeedableRng::from_seed(&SEED as &[_]);
	    let mut v: Vec<isize> = rng.gen_iter::<isize>().take(LENGTH).collect();

        b.iter(|| quicksort::quicksort(&mut v))
    }

    #[bench]
    fn bench_sort(b: &mut test::Bencher) {
        let mut rng: StdRng = SeedableRng::from_seed(&SEED as &[_]);
	    let mut v: Vec<isize> = rng.gen_iter::<isize>().take(LENGTH).collect();

        b.iter(|| v.sort())
    }
}<|endoftext|>


<commit_before>use definitions::Value;
use definitions::config::VMConfig;
use definitions::program::*;
use definitions::typedef::*;
use error::*;
use std::cmp::Ordering;
use std::collections::{BTreeMap, LinkedList};
use std::sync::mpsc::{Receiver, Sender, TryRecvError};
use std::thread::{self, JoinHandle};

pub fn start(program: Program, sender: Sender<Frame>, receiver: Receiver<Interrupt>)
    -> JoinHandle<()> {
    thread::spawn(
        move || {
            if let Err(ref e) = VM::default().exec(program, sender, receiver) {
                println!("error: {}", e);

                for e in e.iter().skip(1) {
                    println!("caused by: {}", e);
                }

                // The backtrace is not always generated. Try to run this example
                // with `RUST_BACKTRACE=1`.
                if let Some(backtrace) = e.backtrace() {
                    println!("backtrace: {:?}", backtrace);
                }
            }
        },
    )
}

/// The whole state of the VM
#[derive(Default, Debug)]
pub struct VM {
    /// The instructions that are currently executed
    image_data: Vec<Instruction>,
    /// The current program counter
    pc: Address,
    stack: LinkedList<Value>,
    val_index: BTreeMap<Address, Value>,
    interrupt_register: BTreeMap<usize, Address>,
    framebuffer: Frame,
    framebuffer_invalid: bool,
    /// A register for holding infomation about a recent comparison
    cmp_register: Option<Ordering>,
    /// A stack to hold the return addresses of function calls
    call_stack: LinkedList<Address>,
    /// A boolean used for locking the program counter
    pc_locked: bool,
    /// The configuration of the VM
    config: VMConfig,
    halted: bool,
}

impl VM {
    // # Maintainance functions

    /// Executes the given program
    pub fn exec(&mut self, program: Program, sender: Sender<Frame>, receiver: Receiver<Interrupt>)
        -> VMResult<()> {
        self.reset();
        self.load_program(program)?;
        self.build_framebuffer();

        while (self.pc < self.image_data.len()) && !self.halted {
            self.external_interrupt(&receiver)?;

            let current_instruction = self.image_data[self.pc].clone();

            match current_instruction {
                Instruction::Halt => break,
                Instruction::Int(interrupt) => self.internal_interrupt(interrupt),

                Instruction::Add(dest, src) => self.add(&dest, &src)?,
                Instruction::Sub(dest, src) => self.sub(&dest, &src)?,
                Instruction::Div(dest, src) => self.div(&dest, &src)?,
                Instruction::Mul(dest, src) => self.mul(&dest, &src)?,
                Instruction::Rem(dest, src) => self.rem(&dest, &src)?,

                Instruction::Cmp(target_a, target_b) => self.cmp(&target_a, &target_b)?,
                Instruction::Jmp(addr) => self.jmp(&addr),
                Instruction::JmpLt(addr) => self.jmp_lt(&addr),
                Instruction::JmpGt(addr) => self.jmp_gt(&addr),
                Instruction::JmpEq(addr) => self.jmp_eq(&addr),
                Instruction::JmpLtEq(addr) => self.jmp_lt_eq(&addr),
                Instruction::JmpGtEq(addr) => self.jmp_gt_eq(&addr),

                Instruction::Push(dest, value) => self.push(&dest, value)?,
                Instruction::Mov(dest, src) => self.mov(&dest, &src)?,
                Instruction::Swp(target_a, target_b) => self.swp(&target_a, &target_b)?,

                Instruction::Call(addr) => self.call(&addr),
                Instruction::Ret => self.ret()?,
            }

            self.flush_framebuffer(&sender)?;

            self.advance_pc();
        }

        Ok(())
    }

    /// Loads the instructions of the given program to the VM's state
    fn load_program(&mut self, program: Program) -> VMResult<()> {
        if program.preamble != String::from(PREAMBLE) {
            bail!("invalid preamble");
        } else if program.version != String::from(env!("CARGO_PKG_VERSION")) {
            bail!("invalid version");
        } else {
            self.image_data = program.instructions;
            self.config = program.config;
            Ok(())
        }
    }

    /// Aborts the execution of the current image
    fn abort(&mut self) {
        self.halted = true;
    }

    /// Handles an internal interrupt
    fn internal_interrupt(&mut self, interrupt: InternalInterrupt) {
        match interrupt {
            InternalInterrupt::FlushFramebuffer => self.invalidate(),
        }
    }

    /// Handles incoming interrupts or moves along
    fn external_interrupt(&mut self, receiver: &Receiver<Interrupt>) -> VMResult<()> {
        match receiver.try_recv() {
            Ok(interrupt) => {
                if interrupt.signal_id == 0 {
                    self.abort();
                    return Ok(());
                }

                let call_addr = if let Some(call_addr) = self.interrupt_register
                       .get(&interrupt.signal_id) {
                    call_addr.clone()
                } else {
                    bail!(
                        "no registered interrupt found at signal_id {}",
                        &interrupt.signal_id
                    );
                };

                for value in interrupt.args {
                    self.push(&Target::Stack, value)?;
                }

                self.call(&call_addr);

                Ok(())
            }
            Err(TryRecvError::Disconnected) => bail!("interrupt receiver disconnected"),
            _ => Ok(()),
        }
    }

    /// Flushes the internal framebuffer using the given sender
    fn flush_framebuffer(&mut self, sender: &Sender<Frame>) -> VMResult<()> {
        if self.framebuffer_invalid {
            sender.send(self.framebuffer.clone()).chain_err(|| "unable to flush framebuffer")?;

            self.framebuffer_invalid = false;
        }

        Ok(())
    }

    /// Allocates all the needed space in the framebuffer
    fn build_framebuffer(&mut self) {
        let ref resolution = self.config.display.resolution;
        let allocation_space = resolution.width * resolution.height;
        self.framebuffer = vec![(0, 0, 0); allocation_space];
    }

    /// Resets the VM to a clean state
    fn reset(&mut self) {
        *self = VM::default();
    }

    /// Locks the program counter in place
    fn lock_pc(&mut self) {
        self.pc_locked = true;
    }

    /// Advances the program counter
    fn advance_pc(&mut self) {
        if self.pc_locked {
            self.pc_locked = false;
        } else {
            self.pc += 1;
        }
    }

    /// Invalidates the frambuffer causing it to be resent to the display
    /// receiver
    fn invalidate(&mut self) {
        self.framebuffer_invalid = true;
    }

    /// Return the value at the specified target. The value of the target will
    /// be consumed
    fn pop(&mut self, target: &Target) -> VMResult<Value> {
        match target {
            &Target::ValueIndex(index) => {
                if let Some(value) = self.val_index.remove(&index) {
                    Ok(value)
                } else {
                    bail!("no value found at index {}", index);
                }
            }
            &Target::Stack => {
                if let Some(value) = self.stack.pop_front() {
                    Ok(value)
                } else {
                    bail!("unable to pop value off an empty stack");
                }
            }
            &Target::Framebuffer(index) => {
                if let Some(&(r, g, b)) = self.framebuffer.get(index) {
                    Ok(Value::Color(r, g, b))
                } else {
                    bail!("no value found in framebuffer at index {}", index);
                }
            }
        }
    }

    // # Instruction functions

    /// Adds the value of the src target to the value of the dest target
    fn add(&mut self, dest: &Target, src: &Target) -> VMResult<()> {
        let dest_value = self.pop(dest)?;
        let src_value = self.pop(src)?;

        self.push(dest, dest_value + src_value)?;

        Ok(())
    }

    /// Subtracts the value of the src target from the value of the dest target
    fn sub(&mut self, dest: &Target, src: &Target) -> VMResult<()> {
        let dest_value = self.pop(dest)?;
        let src_value = self.pop(src)?;

        self.push(dest, dest_value - src_value)?;

        Ok(())
    }

    /// Divides the value of the dest target through the value of the src target
    fn div(&mut self, dest: &Target, src: &Target) -> VMResult<()> {
        let dest_value = self.pop(dest)?;
        let src_value = self.pop(src)?;

        self.push(dest, dest_value / src_value)?;

        Ok(())
    }

    /// Multiplies the value of the dest target with the value of the src target
    fn mul(&mut self, dest: &Target, src: &Target) -> VMResult<()> {
        let dest_value = self.pop(dest)?;
        let src_value = self.pop(src)?;

        self.push(dest, dest_value * src_value)?;

        Ok(())
    }

    /// Applies the modulo operator on the value of the dest target using the
    /// value of the src target
    fn rem(&mut self, dest: &Target, src: &Target) -> VMResult<()> {
        let dest_value = self.pop(dest)?;
        let src_value = self.pop(src)?;

        self.push(dest, dest_value * src_value)?;

        Ok(())
    }

    /// Compares the top values of the two targets and saves the result to
    /// `self.cmp_register`
    fn cmp(&mut self, target_a: &Target, target_b: &Target) -> VMResult<()> {
        let target_a_value = self.pop(target_a)?;
        let target_b_value = self.pop(target_b)?;

        if target_a_value < target_b_value {
            self.cmp_register = Some(Ordering::Less);
        } else if target_a_value > target_b_value {
            self.cmp_register = Some(Ordering::Greater);
        } else if target_a_value == target_b_value {
            self.cmp_register = Some(Ordering::Equal);
        }

        Ok(())
    }

    /// Jumps unconditionally to the specified address
    fn jmp(&mut self, addr: &Address) {
        self.pc = *addr;
        self.lock_pc();
    }

    /// Jumps if the last compare got the result `Some(Ordering::Less)`
    fn jmp_lt(&mut self, addr: &Address) {
        if self.cmp_register == Some(Ordering::Less) {
            self.jmp(addr);
        }
    }

    /// Jumps if the last compare got the result `Some(Ordering::Greater)`
    fn jmp_gt(&mut self, addr: &Address) {
        if self.cmp_register == Some(Ordering::Greater) {
            self.jmp(addr);
        }
    }

    /// Jumps if the last compare got the result `Some(Ordering::Equal)`
    fn jmp_eq(&mut self, addr: &Address) {
        if self.cmp_register == Some(Ordering::Equal) {
            self.jmp(addr);
        }
    }

    /// Jumps if the last compare got the result `Some(Ordering::Less)` or
    /// `Some(Ordering::Equal)`
    fn jmp_lt_eq(&mut self, addr: &Address) {
        if (self.cmp_register == Some(Ordering::Less)) ||
           (self.cmp_register == Some(Ordering::Equal)) {
            self.jmp(addr);
        }
    }

    /// Jumps if the last compare got the result `Some(Ordering::Greater)` or
    /// `Some(Ordering::Equal)`
    fn jmp_gt_eq(&mut self, addr: &Address) {
        if (self.cmp_register == Some(Ordering::Greater)) ||
           (self.cmp_register == Some(Ordering::Equal)) {
            self.jmp(addr);
        }
    }

    /// Pushes the given value to the given target
    fn push(&mut self, dest: &Target, value: Value) -> VMResult<()> {
        match dest {
            &Target::ValueIndex(index) => {
                self.val_index.entry(index).or_insert(value);
                Ok(())
            }
            &Target::Stack => {
                self.stack.push_front(value);
                Ok(())
            }
            &Target::Framebuffer(index) => {
                if let Value::Color(r, g, b) = value {
                    self.framebuffer[index] = (r, g, b);
                    Ok(())
                } else {
                    bail!("unable push a non-color value to the framebuffer");
                }
            }
        }
    }

    /// Moves the top value of the src target to the dest target
    fn mov(&mut self, dest: &Target, src: &Target) -> VMResult<()> {
        let src_value = self.pop(src)?;
        self.push(dest, src_value)?;

        Ok(())
    }

    /// Swaps the top values of the targets
    fn swp(&mut self, target_a: &Target, target_b: &Target) -> VMResult<()> {
        let a_value = self.pop(target_a)?;
        let b_value = self.pop(target_b)?;

        self.push(target_a, b_value)?;
        self.push(target_b, a_value)?;

        Ok(())
    }

    /// Calls the function at the specified address saving the return address
    /// to the call stack
    fn call(&mut self, addr: &Address) {
        self.call_stack.push_front(self.pc + 1);
        self.jmp(addr);
    }

    /// Returns from an ongoing function call
    fn ret(&mut self) -> VMResult<()> {
        if let Some(retur_addr) = self.call_stack.pop_front() {
            self.jmp(&retur_addr);
        } else {
            bail!("unable to return from an empty call stack");
        }

        Ok(())
    }
}
<commit_msg>Optimized internal interrupts<commit_after>use definitions::Value;
use definitions::config::VMConfig;
use definitions::program::*;
use definitions::typedef::*;
use error::*;
use std::cmp::Ordering;
use std::collections::{BTreeMap, LinkedList};
use std::sync::mpsc::{Receiver, Sender, TryRecvError};
use std::thread::{self, JoinHandle};

pub fn start(program: Program, sender: Sender<Frame>, receiver: Receiver<Interrupt>)
    -> JoinHandle<()> {
    thread::spawn(
        move || {
            if let Err(ref e) = VM::default().exec(program, sender, receiver) {
                println!("error: {}", e);

                for e in e.iter().skip(1) {
                    println!("caused by: {}", e);
                }

                // The backtrace is not always generated. Try to run this example
                // with `RUST_BACKTRACE=1`.
                if let Some(backtrace) = e.backtrace() {
                    println!("backtrace: {:?}", backtrace);
                }
            }
        },
    )
}

/// The whole state of the VM
#[derive(Default, Debug)]
pub struct VM {
    /// The instructions that are currently executed
    image_data: Vec<Instruction>,
    /// The current program counter
    pc: Address,
    stack: LinkedList<Value>,
    val_index: BTreeMap<Address, Value>,
    interrupt_register: BTreeMap<usize, Address>,
    framebuffer: Frame,
    framebuffer_invalid: bool,
    /// A register for holding infomation about a recent comparison
    cmp_register: Option<Ordering>,
    /// A stack to hold the return addresses of function calls
    call_stack: LinkedList<Address>,
    /// A boolean used for locking the program counter
    pc_locked: bool,
    /// The configuration of the VM
    config: VMConfig,
    halted: bool,
}

impl VM {
    // # Maintainance functions

    /// Executes the given program
    pub fn exec(&mut self, program: Program, sender: Sender<Frame>, receiver: Receiver<Interrupt>)
        -> VMResult<()> {
        self.reset();
        self.load_program(program)?;
        self.build_framebuffer();

        while (self.pc < self.image_data.len()) && !self.halted {
            self.external_interrupt(&receiver)?;

            let current_instruction = self.image_data[self.pc].clone();

            match current_instruction {
                Instruction::Halt => break,
                Instruction::Int(interrupt) => self.int(&interrupt),

                Instruction::Add(dest, src) => self.add(&dest, &src)?,
                Instruction::Sub(dest, src) => self.sub(&dest, &src)?,
                Instruction::Div(dest, src) => self.div(&dest, &src)?,
                Instruction::Mul(dest, src) => self.mul(&dest, &src)?,
                Instruction::Rem(dest, src) => self.rem(&dest, &src)?,

                Instruction::Cmp(target_a, target_b) => self.cmp(&target_a, &target_b)?,
                Instruction::Jmp(addr) => self.jmp(&addr),
                Instruction::JmpLt(addr) => self.jmp_lt(&addr),
                Instruction::JmpGt(addr) => self.jmp_gt(&addr),
                Instruction::JmpEq(addr) => self.jmp_eq(&addr),
                Instruction::JmpLtEq(addr) => self.jmp_lt_eq(&addr),
                Instruction::JmpGtEq(addr) => self.jmp_gt_eq(&addr),

                Instruction::Push(dest, value) => self.push(&dest, value)?,
                Instruction::Mov(dest, src) => self.mov(&dest, &src)?,
                Instruction::Swp(target_a, target_b) => self.swp(&target_a, &target_b)?,

                Instruction::Call(addr) => self.call(&addr),
                Instruction::Ret => self.ret()?,
            }

            self.flush_framebuffer(&sender)?;

            self.advance_pc();
        }

        Ok(())
    }

    /// Loads the instructions of the given program to the VM's state
    fn load_program(&mut self, program: Program) -> VMResult<()> {
        if program.preamble != String::from(PREAMBLE) {
            bail!("invalid preamble");
        } else if program.version != String::from(env!("CARGO_PKG_VERSION")) {
            bail!("invalid version");
        } else {
            self.image_data = program.instructions;
            self.config = program.config;
            Ok(())
        }
    }

    /// Aborts the execution of the current image
    fn abort(&mut self) {
        self.halted = true;
    }

    /// Handles an internal interrupt
    fn int(&mut self, interrupt: &InternalInterrupt) {
        match interrupt {
            &InternalInterrupt::FlushFramebuffer => self.invalidate(),
        }
    }

    /// Handles incoming interrupts or moves along
    fn external_interrupt(&mut self, receiver: &Receiver<Interrupt>) -> VMResult<()> {
        match receiver.try_recv() {
            Ok(interrupt) => {
                if interrupt.signal_id == 0 {
                    self.abort();
                    return Ok(());
                }

                let call_addr = if let Some(call_addr) = self.interrupt_register
                       .get(&interrupt.signal_id) {
                    call_addr.clone()
                } else {
                    bail!(
                        "no registered interrupt found at signal_id {}",
                        &interrupt.signal_id
                    );
                };

                for value in interrupt.args {
                    self.push(&Target::Stack, value)?;
                }

                self.call(&call_addr);

                Ok(())
            }
            Err(TryRecvError::Disconnected) => bail!("interrupt receiver disconnected"),
            _ => Ok(()),
        }
    }

    /// Flushes the internal framebuffer using the given sender
    fn flush_framebuffer(&mut self, sender: &Sender<Frame>) -> VMResult<()> {
        if self.framebuffer_invalid {
            sender.send(self.framebuffer.clone()).chain_err(|| "unable to flush framebuffer")?;

            self.framebuffer_invalid = false;
        }

        Ok(())
    }

    /// Allocates all the needed space in the framebuffer
    fn build_framebuffer(&mut self) {
        let ref resolution = self.config.display.resolution;
        let allocation_space = resolution.width * resolution.height;
        self.framebuffer = vec![(0, 0, 0); allocation_space];
    }

    /// Resets the VM to a clean state
    fn reset(&mut self) {
        *self = VM::default();
    }

    /// Locks the program counter in place
    fn lock_pc(&mut self) {
        self.pc_locked = true;
    }

    /// Advances the program counter
    fn advance_pc(&mut self) {
        if self.pc_locked {
            self.pc_locked = false;
        } else {
            self.pc += 1;
        }
    }

    /// Invalidates the frambuffer causing it to be resent to the display
    /// receiver
    fn invalidate(&mut self) {
        self.framebuffer_invalid = true;
    }

    /// Return the value at the specified target. The value of the target will
    /// be consumed
    fn pop(&mut self, target: &Target) -> VMResult<Value> {
        match target {
            &Target::ValueIndex(index) => {
                if let Some(value) = self.val_index.remove(&index) {
                    Ok(value)
                } else {
                    bail!("no value found at index {}", index);
                }
            }
            &Target::Stack => {
                if let Some(value) = self.stack.pop_front() {
                    Ok(value)
                } else {
                    bail!("unable to pop value off an empty stack");
                }
            }
            &Target::Framebuffer(index) => {
                if let Some(&(r, g, b)) = self.framebuffer.get(index) {
                    Ok(Value::Color(r, g, b))
                } else {
                    bail!("no value found in framebuffer at index {}", index);
                }
            }
        }
    }

    // # Instruction functions

    /// Adds the value of the src target to the value of the dest target
    fn add(&mut self, dest: &Target, src: &Target) -> VMResult<()> {
        let dest_value = self.pop(dest)?;
        let src_value = self.pop(src)?;

        self.push(dest, dest_value + src_value)?;

        Ok(())
    }

    /// Subtracts the value of the src target from the value of the dest target
    fn sub(&mut self, dest: &Target, src: &Target) -> VMResult<()> {
        let dest_value = self.pop(dest)?;
        let src_value = self.pop(src)?;

        self.push(dest, dest_value - src_value)?;

        Ok(())
    }

    /// Divides the value of the dest target through the value of the src target
    fn div(&mut self, dest: &Target, src: &Target) -> VMResult<()> {
        let dest_value = self.pop(dest)?;
        let src_value = self.pop(src)?;

        self.push(dest, dest_value / src_value)?;

        Ok(())
    }

    /// Multiplies the value of the dest target with the value of the src target
    fn mul(&mut self, dest: &Target, src: &Target) -> VMResult<()> {
        let dest_value = self.pop(dest)?;
        let src_value = self.pop(src)?;

        self.push(dest, dest_value * src_value)?;

        Ok(())
    }

    /// Applies the modulo operator on the value of the dest target using the
    /// value of the src target
    fn rem(&mut self, dest: &Target, src: &Target) -> VMResult<()> {
        let dest_value = self.pop(dest)?;
        let src_value = self.pop(src)?;

        self.push(dest, dest_value * src_value)?;

        Ok(())
    }

    /// Compares the top values of the two targets and saves the result to
    /// `self.cmp_register`
    fn cmp(&mut self, target_a: &Target, target_b: &Target) -> VMResult<()> {
        let target_a_value = self.pop(target_a)?;
        let target_b_value = self.pop(target_b)?;

        if target_a_value < target_b_value {
            self.cmp_register = Some(Ordering::Less);
        } else if target_a_value > target_b_value {
            self.cmp_register = Some(Ordering::Greater);
        } else if target_a_value == target_b_value {
            self.cmp_register = Some(Ordering::Equal);
        }

        Ok(())
    }

    /// Jumps unconditionally to the specified address
    fn jmp(&mut self, addr: &Address) {
        self.pc = *addr;
        self.lock_pc();
    }

    /// Jumps if the last compare got the result `Some(Ordering::Less)`
    fn jmp_lt(&mut self, addr: &Address) {
        if self.cmp_register == Some(Ordering::Less) {
            self.jmp(addr);
        }
    }

    /// Jumps if the last compare got the result `Some(Ordering::Greater)`
    fn jmp_gt(&mut self, addr: &Address) {
        if self.cmp_register == Some(Ordering::Greater) {
            self.jmp(addr);
        }
    }

    /// Jumps if the last compare got the result `Some(Ordering::Equal)`
    fn jmp_eq(&mut self, addr: &Address) {
        if self.cmp_register == Some(Ordering::Equal) {
            self.jmp(addr);
        }
    }

    /// Jumps if the last compare got the result `Some(Ordering::Less)` or
    /// `Some(Ordering::Equal)`
    fn jmp_lt_eq(&mut self, addr: &Address) {
        if (self.cmp_register == Some(Ordering::Less)) ||
           (self.cmp_register == Some(Ordering::Equal)) {
            self.jmp(addr);
        }
    }

    /// Jumps if the last compare got the result `Some(Ordering::Greater)` or
    /// `Some(Ordering::Equal)`
    fn jmp_gt_eq(&mut self, addr: &Address) {
        if (self.cmp_register == Some(Ordering::Greater)) ||
           (self.cmp_register == Some(Ordering::Equal)) {
            self.jmp(addr);
        }
    }

    /// Pushes the given value to the given target
    fn push(&mut self, dest: &Target, value: Value) -> VMResult<()> {
        match dest {
            &Target::ValueIndex(index) => {
                self.val_index.entry(index).or_insert(value);
                Ok(())
            }
            &Target::Stack => {
                self.stack.push_front(value);
                Ok(())
            }
            &Target::Framebuffer(index) => {
                if let Value::Color(r, g, b) = value {
                    self.framebuffer[index] = (r, g, b);
                    Ok(())
                } else {
                    bail!("unable push a non-color value to the framebuffer");
                }
            }
        }
    }

    /// Moves the top value of the src target to the dest target
    fn mov(&mut self, dest: &Target, src: &Target) -> VMResult<()> {
        let src_value = self.pop(src)?;
        self.push(dest, src_value)?;

        Ok(())
    }

    /// Swaps the top values of the targets
    fn swp(&mut self, target_a: &Target, target_b: &Target) -> VMResult<()> {
        let a_value = self.pop(target_a)?;
        let b_value = self.pop(target_b)?;

        self.push(target_a, b_value)?;
        self.push(target_b, a_value)?;

        Ok(())
    }

    /// Calls the function at the specified address saving the return address
    /// to the call stack
    fn call(&mut self, addr: &Address) {
        self.call_stack.push_front(self.pc + 1);
        self.jmp(addr);
    }

    /// Returns from an ongoing function call
    fn ret(&mut self) -> VMResult<()> {
        if let Some(retur_addr) = self.call_stack.pop_front() {
            self.jmp(&retur_addr);
        } else {
            bail!("unable to return from an empty call stack");
        }

        Ok(())
    }
}
<|endoftext|>


<commit_before>use std::io::{Read, Write};
use std::path::Path;
use dicom_parser::error::Result;
use crate::{DefaultDicomObject, DicomObject};

pub fn from_stream<F>(file: F) -> Result<DefaultDicomObject>
where
    F: Read,
{
    DefaultDicomObject::from_stream(file)
}

pub fn open_file<P>(path: P) -> Result<DefaultDicomObject>
where
    P: AsRef<Path>
{
    DefaultDicomObject::open_file(path)
}

pub fn to_file<F: Write, D: DicomObject>(obj: &D, to: F) -> Result<()> {
    unimplemented!()
}
<commit_msg>[object] Remove unimplemented to_file<commit_after>use std::io::Read;
use std::path::Path;
use dicom_parser::error::Result;
use crate::DefaultDicomObject;

pub fn from_reader<F>(file: F) -> Result<DefaultDicomObject>
where
    F: Read,
{
    DefaultDicomObject::from_reader(file)
}

pub fn open_file<P>(path: P) -> Result<DefaultDicomObject>
where
    P: AsRef<Path>
{
    DefaultDicomObject::open_file(path)
}
<|endoftext|>