<commit_before>// Copyright (c) 2016-2017 Chef Software Inc. and/or applicable contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! The push thread.
//!
//! This is the thread for distributing rumors to members. It distributes to `FANOUT` members, no
//! more often than `Timing::GOSSIP_PERIOD_DEFAULT_MS`.

use std::sync::atomic::Ordering;
use std::thread;
use std::time::Duration;

use protobuf::Message;
use time::SteadyTime;
use zmq;

use member::Member;
use message::swim::{
    Member as ProtoMember, Membership as ProtoMembership, Rumor as ProtoRumor,
    Rumor_Type as ProtoRumor_Type,
};
use rumor::RumorKey;
use server::timing::Timing;
use server::Server;
use trace::TraceKind;
use ZMQ_CONTEXT;

const FANOUT: usize = 5;

/// The Push server
#[derive(Debug)]
pub struct Push {
    pub server: Server,
    pub timing: Timing,
}

impl Push {
    /// Creates a new Push instance from a Server and Timing
    pub fn new(server: Server, timing: Timing) -> Push {
        Push {
            server: server,
            timing: timing,
        }
    }

    /// Executes the Push thread. Gets a list of members to talk to that are not Confirmed; then
    /// proceeds to process the list in `FANOUT` sized chunks. If we finish sending the messages to
    /// all FANOUT targets faster than `Timing::GOSSIP_PERIOD_DEFAULT_MS`, we will block until we
    /// exceed that time.
    pub fn run(&mut self) {
        'send: loop {
            if self.server.pause.load(Ordering::Relaxed) {
                thread::sleep(Duration::from_millis(100));
                continue;
            }

            self.server.update_gossip_round();

            let mut check_list = self.server.member_list.check_list(self.server.member_id());
            let long_wait = self.timing.gossip_timeout();

            'fanout: loop {
                let mut thread_list = Vec::with_capacity(FANOUT);
                if check_list.len() == 0 {
                    break 'fanout;
                }
                let drain_length = if check_list.len() >= FANOUT {
                    FANOUT
                } else {
                    check_list.len()
                };
                let next_gossip = self.timing.gossip_timeout();
                for member in check_list.drain(0..drain_length) {
                    if self.server.is_member_blocked(member.get_id()) {
                        debug!("Not sending rumors to {} - it is blocked", member.get_id());
                        continue;
                    }
                    // Unlike the SWIM mechanism, we don't actually want to send gossip traffic to
                    // persistent members that are confirmed dead. When the failure detector thread
                    // finds them alive again, we'll go ahead and get back to the business at hand.
                    if self.server.member_list.pingable(&member)
                        && !self.server.member_list.persistent_and_confirmed(&member)
                    {
                        let rumors = self.server.rumor_heat.currently_hot_rumors(member.get_id());
                        if rumors.len() > 0 {
                            let sc = self.server.clone();

                            let guard = match thread::Builder::new()
                                .name(String::from("push-worker"))
                                .spawn(move || {
                                    PushWorker::new(sc).send_rumors(member, rumors);
                                }) {
                                Ok(guard) => guard,
                                Err(e) => {
                                    error!("Could not spawn thread: {}", e);
                                    continue;
                                }
                            };
                            thread_list.push(guard);
                        }
                    }
                }
                let num_threads = thread_list.len();
                for guard in thread_list.drain(0..num_threads) {
                    let _ = guard
                        .join()
                        .map_err(|e| println!("Push worker died: {:?}", e));
                }
                if SteadyTime::now() < next_gossip {
                    let wait_time = (next_gossip - SteadyTime::now()).num_milliseconds();
                    if wait_time > 0 {
                        thread::sleep(Duration::from_millis(wait_time as u64));
                    }
                }
            }
            if SteadyTime::now() < long_wait {
                let wait_time = (long_wait - SteadyTime::now()).num_milliseconds();
                if wait_time > 0 {
                    thread::sleep(Duration::from_millis(wait_time as u64));
                }
            }
        }
    }
}

/// A worker thread for pushing messages to a target
struct PushWorker {
    pub server: Server,
}

impl PushWorker {
    /// Create a new PushWorker.
    pub fn new(server: Server) -> PushWorker {
        PushWorker { server: server }
    }

    /// Send the list of rumors to a given member. This method creates an outbound socket and then
    /// closes the connection as soon as we are done sending rumors. ZeroMQ may choose to keep the
    /// connection and socket open for 1 second longer - so it is possible, but unlikely, that this
    /// method can loose messages.
    fn send_rumors(&self, member: Member, rumors: Vec<RumorKey>) {
        let socket = (**ZMQ_CONTEXT)
            .as_mut()
            .socket(zmq::PUSH)
            .expect("Failure to create the ZMQ push socket");
        socket
            .set_linger(1000)
            .expect("Failure to set the ZMQ push socket to not linger");
        socket
            .set_tcp_keepalive(0)
            .expect("Failure to set the ZMQ push socket to not use keepalive");
        socket
            .set_immediate(true)
            .expect("Failure to set the ZMQ push socket to immediate");
        socket
            .set_sndhwm(1000)
            .expect("Failure to set the ZMQ push socket hwm");
        socket
            .set_sndtimeo(500)
            .expect("Failure to set the ZMQ send timeout");
        let to_addr = format!("{}:{}", member.get_address(), member.get_gossip_port());
        match socket.connect(&format!("tcp://{}", to_addr)) {
            Ok(()) => debug!("Connected push socket to {:?}", member),
            Err(e) => {
                println!("Cannot connect push socket to {:?}: {:?}", member, e);
                return;
            }
        }
        'rumorlist: for ref rumor_key in rumors.iter() {
            let rumor_as_bytes = match rumor_key.kind {
                ProtoRumor_Type::Member => {
                    let send_rumor = match self.create_member_rumor(&rumor_key) {
                        Some(rumor) => rumor,
                        None => continue 'rumorlist,
                    };
                    trace_it!(
                        GOSSIP: &self.server,
                        TraceKind::SendRumor,
                        member.get_id(),
                        &send_rumor);
                    match send_rumor.write_to_bytes() {
                        Ok(bytes) => bytes,
                        Err(e) => {
                            println!(
                                "Could not write our own rumor to bytes; abandoning \
                                 sending rumor: {:?}",
                                e
                            );
                            continue 'rumorlist;
                        }
                    }
                }
                ProtoRumor_Type::Service => {
                    // trace_it!(GOSSIP: &self.server,
                    //           TraceKind::SendRumor,
                    //           member.get_id(),
                    //           &send_rumor);
                    match self.server
                        .service_store
                        .write_to_bytes(&rumor_key.key, &rumor_key.id)
                    {
                        Ok(bytes) => bytes,
                        Err(e) => {
                            println!(
                                "Could not write our own rumor to bytes; abandoning \
                                 sending rumor: {:?}",
                                e
                            );
                            continue 'rumorlist;
                        }
                    }
                }
                ProtoRumor_Type::ServiceConfig => {
                    // trace_it!(GOSSIP: &self.server,
                    //           TraceKind::SendRumor,
                    //           member.get_id(),
                    //           &send_rumor);
                    match self.server
                        .service_config_store
                        .write_to_bytes(&rumor_key.key, &rumor_key.id)
                    {
                        Ok(bytes) => bytes,
                        Err(e) => {
                            println!(
                                "Could not write our own rumor to bytes; abandoning \
                                 sending rumor: {:?}",
                                e
                            );
                            continue 'rumorlist;
                        }
                    }
                }
                ProtoRumor_Type::ServiceFile => {
                    // trace_it!(GOSSIP: &self.server,
                    //           TraceKind::SendRumor,
                    //           member.get_id(),
                    //           &send_rumor);
                    match self.server
                        .service_file_store
                        .write_to_bytes(&rumor_key.key, &rumor_key.id)
                    {
                        Ok(bytes) => bytes,
                        Err(e) => {
                            println!(
                                "Could not write our own rumor to bytes; abandoning \
                                 sending rumor: {:?}",
                                e
                            );
                            continue 'rumorlist;
                        }
                    }
                }
                ProtoRumor_Type::Departure => match self.server
                    .departure_store
                    .write_to_bytes(&rumor_key.key, &rumor_key.id)
                {
                    Ok(bytes) => bytes,
                    Err(e) => {
                        println!(
                            "Could not write our own rumor to bytes; abandoning \
                             sending rumor: {:?}",
                            e
                        );
                        continue 'rumorlist;
                    }
                },
                ProtoRumor_Type::Election => {
                    // trace_it!(GOSSIP: &self.server,
                    //           TraceKind::SendRumor,
                    //           member.get_id(),
                    //           &send_rumor);
                    match self.server
                        .election_store
                        .write_to_bytes(&rumor_key.key, &rumor_key.id)
                    {
                        Ok(bytes) => bytes,
                        Err(e) => {
                            println!(
                                "Could not write our own rumor to bytes; abandoning \
                                 sending rumor: {:?}",
                                e
                            );
                            continue 'rumorlist;
                        }
                    }
                }
                ProtoRumor_Type::ElectionUpdate => match self.server
                    .update_store
                    .write_to_bytes(&rumor_key.key, &rumor_key.id)
                {
                    Ok(bytes) => bytes,
                    Err(e) => {
                        println!(
                            "Could not write our own rumor to bytes; abandoning sending \
                             rumor: {:?}",
                            e
                        );
                        continue 'rumorlist;
                    }
                },
                ProtoRumor_Type::Fake | ProtoRumor_Type::Fake2 => {
                    debug!("You have fake rumors; how odd!");
                    continue 'rumorlist;
                }
            };
            let payload = match self.server.generate_wire(rumor_as_bytes) {
                Ok(payload) => payload,
                Err(e) => {
                    error!("Generating protobuf failed: {}", e);
                    continue 'rumorlist;
                }
            };
            match socket.send(&payload, 0) {
                Ok(()) => debug!("Sent rumor {:?} to {:?}", rumor_key, member),
                Err(e) => println!(
                    "Could not send rumor to {:?} @ {:?}; ZMQ said: {:?}",
                    member.get_id(),
                    to_addr,
                    e
                ),
            }
        }
        self.server.rumor_heat.cool_rumors(member.get_id(), &rumors);
    }

    /// Given a rumorkey, creates a protobuf rumor for sharing.
    fn create_member_rumor(&self, rumor_key: &RumorKey) -> Option<ProtoRumor> {
        let mut member: Option<ProtoMember> = None;
        self.server.member_list.with_member(&rumor_key.key(), |m| {
            if let Some(m) = m {
                member = Some(m.proto.clone());
            }
        });
        if member.is_none() {
            return None;
        }
        let mut membership = ProtoMembership::new();
        membership.set_member(member.unwrap());
        membership.set_health(
            self.server
                .member_list
                .health_of_by_id(&rumor_key.key())
                .unwrap()
                .into(),
        );
        let mut rumor = ProtoRumor::new();
        rumor.set_field_type(ProtoRumor_Type::Member);
        rumor.set_member(membership);
        rumor.set_from_id(String::from(self.server.member_id()));
        Some(rumor)
    }
}
<commit_msg>Change spamming message to debug only<commit_after>// Copyright (c) 2016-2017 Chef Software Inc. and/or applicable contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! The push thread.
//!
//! This is the thread for distributing rumors to members. It distributes to `FANOUT` members, no
//! more often than `Timing::GOSSIP_PERIOD_DEFAULT_MS`.

use std::sync::atomic::Ordering;
use std::thread;
use std::time::Duration;

use protobuf::Message;
use time::SteadyTime;
use zmq;

use member::Member;
use message::swim::{
    Member as ProtoMember, Membership as ProtoMembership, Rumor as ProtoRumor,
    Rumor_Type as ProtoRumor_Type,
};
use rumor::RumorKey;
use server::timing::Timing;
use server::Server;
use trace::TraceKind;
use ZMQ_CONTEXT;

const FANOUT: usize = 5;

/// The Push server
#[derive(Debug)]
pub struct Push {
    pub server: Server,
    pub timing: Timing,
}

impl Push {
    /// Creates a new Push instance from a Server and Timing
    pub fn new(server: Server, timing: Timing) -> Push {
        Push {
            server: server,
            timing: timing,
        }
    }

    /// Executes the Push thread. Gets a list of members to talk to that are not Confirmed; then
    /// proceeds to process the list in `FANOUT` sized chunks. If we finish sending the messages to
    /// all FANOUT targets faster than `Timing::GOSSIP_PERIOD_DEFAULT_MS`, we will block until we
    /// exceed that time.
    pub fn run(&mut self) {
        'send: loop {
            if self.server.pause.load(Ordering::Relaxed) {
                thread::sleep(Duration::from_millis(100));
                continue;
            }

            self.server.update_gossip_round();

            let mut check_list = self.server.member_list.check_list(self.server.member_id());
            let long_wait = self.timing.gossip_timeout();

            'fanout: loop {
                let mut thread_list = Vec::with_capacity(FANOUT);
                if check_list.len() == 0 {
                    break 'fanout;
                }
                let drain_length = if check_list.len() >= FANOUT {
                    FANOUT
                } else {
                    check_list.len()
                };
                let next_gossip = self.timing.gossip_timeout();
                for member in check_list.drain(0..drain_length) {
                    if self.server.is_member_blocked(member.get_id()) {
                        debug!("Not sending rumors to {} - it is blocked", member.get_id());
                        continue;
                    }
                    // Unlike the SWIM mechanism, we don't actually want to send gossip traffic to
                    // persistent members that are confirmed dead. When the failure detector thread
                    // finds them alive again, we'll go ahead and get back to the business at hand.
                    if self.server.member_list.pingable(&member)
                        && !self.server.member_list.persistent_and_confirmed(&member)
                    {
                        let rumors = self.server.rumor_heat.currently_hot_rumors(member.get_id());
                        if rumors.len() > 0 {
                            let sc = self.server.clone();

                            let guard = match thread::Builder::new()
                                .name(String::from("push-worker"))
                                .spawn(move || {
                                    PushWorker::new(sc).send_rumors(member, rumors);
                                }) {
                                Ok(guard) => guard,
                                Err(e) => {
                                    error!("Could not spawn thread: {}", e);
                                    continue;
                                }
                            };
                            thread_list.push(guard);
                        }
                    }
                }
                let num_threads = thread_list.len();
                for guard in thread_list.drain(0..num_threads) {
                    let _ = guard
                        .join()
                        .map_err(|e| println!("Push worker died: {:?}", e));
                }
                if SteadyTime::now() < next_gossip {
                    let wait_time = (next_gossip - SteadyTime::now()).num_milliseconds();
                    if wait_time > 0 {
                        thread::sleep(Duration::from_millis(wait_time as u64));
                    }
                }
            }
            if SteadyTime::now() < long_wait {
                let wait_time = (long_wait - SteadyTime::now()).num_milliseconds();
                if wait_time > 0 {
                    thread::sleep(Duration::from_millis(wait_time as u64));
                }
            }
        }
    }
}

/// A worker thread for pushing messages to a target
struct PushWorker {
    pub server: Server,
}

impl PushWorker {
    /// Create a new PushWorker.
    pub fn new(server: Server) -> PushWorker {
        PushWorker { server: server }
    }

    /// Send the list of rumors to a given member. This method creates an outbound socket and then
    /// closes the connection as soon as we are done sending rumors. ZeroMQ may choose to keep the
    /// connection and socket open for 1 second longer - so it is possible, but unlikely, that this
    /// method can loose messages.
    fn send_rumors(&self, member: Member, rumors: Vec<RumorKey>) {
        let socket = (**ZMQ_CONTEXT)
            .as_mut()
            .socket(zmq::PUSH)
            .expect("Failure to create the ZMQ push socket");
        socket
            .set_linger(1000)
            .expect("Failure to set the ZMQ push socket to not linger");
        socket
            .set_tcp_keepalive(0)
            .expect("Failure to set the ZMQ push socket to not use keepalive");
        socket
            .set_immediate(true)
            .expect("Failure to set the ZMQ push socket to immediate");
        socket
            .set_sndhwm(1000)
            .expect("Failure to set the ZMQ push socket hwm");
        socket
            .set_sndtimeo(500)
            .expect("Failure to set the ZMQ send timeout");
        let to_addr = format!("{}:{}", member.get_address(), member.get_gossip_port());
        match socket.connect(&format!("tcp://{}", to_addr)) {
            Ok(()) => debug!("Connected push socket to {:?}", member),
            Err(e) => {
                println!("Cannot connect push socket to {:?}: {:?}", member, e);
                return;
            }
        }
        'rumorlist: for ref rumor_key in rumors.iter() {
            let rumor_as_bytes = match rumor_key.kind {
                ProtoRumor_Type::Member => {
                    let send_rumor = match self.create_member_rumor(&rumor_key) {
                        Some(rumor) => rumor,
                        None => continue 'rumorlist,
                    };
                    trace_it!(
                        GOSSIP: &self.server,
                        TraceKind::SendRumor,
                        member.get_id(),
                        &send_rumor);
                    match send_rumor.write_to_bytes() {
                        Ok(bytes) => bytes,
                        Err(e) => {
                            println!(
                                "Could not write our own rumor to bytes; abandoning \
                                 sending rumor: {:?}",
                                e
                            );
                            continue 'rumorlist;
                        }
                    }
                }
                ProtoRumor_Type::Service => {
                    // trace_it!(GOSSIP: &self.server,
                    //           TraceKind::SendRumor,
                    //           member.get_id(),
                    //           &send_rumor);
                    match self.server
                        .service_store
                        .write_to_bytes(&rumor_key.key, &rumor_key.id)
                    {
                        Ok(bytes) => bytes,
                        Err(e) => {
                            println!(
                                "Could not write our own rumor to bytes; abandoning \
                                 sending rumor: {:?}",
                                e
                            );
                            continue 'rumorlist;
                        }
                    }
                }
                ProtoRumor_Type::ServiceConfig => {
                    // trace_it!(GOSSIP: &self.server,
                    //           TraceKind::SendRumor,
                    //           member.get_id(),
                    //           &send_rumor);
                    match self.server
                        .service_config_store
                        .write_to_bytes(&rumor_key.key, &rumor_key.id)
                    {
                        Ok(bytes) => bytes,
                        Err(e) => {
                            println!(
                                "Could not write our own rumor to bytes; abandoning \
                                 sending rumor: {:?}",
                                e
                            );
                            continue 'rumorlist;
                        }
                    }
                }
                ProtoRumor_Type::ServiceFile => {
                    // trace_it!(GOSSIP: &self.server,
                    //           TraceKind::SendRumor,
                    //           member.get_id(),
                    //           &send_rumor);
                    match self.server
                        .service_file_store
                        .write_to_bytes(&rumor_key.key, &rumor_key.id)
                    {
                        Ok(bytes) => bytes,
                        Err(e) => {
                            println!(
                                "Could not write our own rumor to bytes; abandoning \
                                 sending rumor: {:?}",
                                e
                            );
                            continue 'rumorlist;
                        }
                    }
                }
                ProtoRumor_Type::Departure => match self.server
                    .departure_store
                    .write_to_bytes(&rumor_key.key, &rumor_key.id)
                {
                    Ok(bytes) => bytes,
                    Err(e) => {
                        println!(
                            "Could not write our own rumor to bytes; abandoning \
                             sending rumor: {:?}",
                            e
                        );
                        continue 'rumorlist;
                    }
                },
                ProtoRumor_Type::Election => {
                    // trace_it!(GOSSIP: &self.server,
                    //           TraceKind::SendRumor,
                    //           member.get_id(),
                    //           &send_rumor);
                    match self.server
                        .election_store
                        .write_to_bytes(&rumor_key.key, &rumor_key.id)
                    {
                        Ok(bytes) => bytes,
                        Err(e) => {
                            println!(
                                "Could not write our own rumor to bytes; abandoning \
                                 sending rumor: {:?}",
                                e
                            );
                            continue 'rumorlist;
                        }
                    }
                }
                ProtoRumor_Type::ElectionUpdate => match self.server
                    .update_store
                    .write_to_bytes(&rumor_key.key, &rumor_key.id)
                {
                    Ok(bytes) => bytes,
                    Err(e) => {
                        println!(
                            "Could not write our own rumor to bytes; abandoning sending \
                             rumor: {:?}",
                            e
                        );
                        continue 'rumorlist;
                    }
                },
                ProtoRumor_Type::Fake | ProtoRumor_Type::Fake2 => {
                    debug!("You have fake rumors; how odd!");
                    continue 'rumorlist;
                }
            };
            let payload = match self.server.generate_wire(rumor_as_bytes) {
                Ok(payload) => payload,
                Err(e) => {
                    error!("Generating protobuf failed: {}", e);
                    continue 'rumorlist;
                }
            };
            match socket.send(&payload, 0) {
                Ok(()) => debug!("Sent rumor {:?} to {:?}", rumor_key, member),
                // This case seems to happen quite often, and is not actionable or a blocker
                // Changing output message to debug instead of a println
                Err(e) => debug!(
                    "Could not send rumor to {:?} @ {:?}; ZMQ said: {:?}",
                    member.get_id(),
                    to_addr,
                    e
                ),
            }
        }
        self.server.rumor_heat.cool_rumors(member.get_id(), &rumors);
    }

    /// Given a rumorkey, creates a protobuf rumor for sharing.
    fn create_member_rumor(&self, rumor_key: &RumorKey) -> Option<ProtoRumor> {
        let mut member: Option<ProtoMember> = None;
        self.server.member_list.with_member(&rumor_key.key(), |m| {
            if let Some(m) = m {
                member = Some(m.proto.clone());
            }
        });
        if member.is_none() {
            return None;
        }
        let mut membership = ProtoMembership::new();
        membership.set_member(member.unwrap());
        membership.set_health(
            self.server
                .member_list
                .health_of_by_id(&rumor_key.key())
                .unwrap()
                .into(),
        );
        let mut rumor = ProtoRumor::new();
        rumor.set_field_type(ProtoRumor_Type::Member);
        rumor.set_member(membership);
        rumor.set_from_id(String::from(self.server.member_id()));
        Some(rumor)
    }
}
<|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/. */

//! Utilities to throw exceptions from Rust bindings.

use dom::bindings::codegen::Bindings::DOMExceptionBinding::DOMExceptionMethods;
use dom::bindings::codegen::PrototypeList::proto_id_to_name;
use dom::bindings::conversions::root_from_object;
use dom::bindings::conversions::{ConversionResult, FromJSValConvertible, ToJSValConvertible};
use dom::bindings::global::{GlobalRef, global_root_from_context};
use dom::bindings::str::USVString;
use dom::domexception::{DOMErrorName, DOMException};
use js::error::{throw_range_error, throw_type_error};
use js::jsapi::HandleObject;
use js::jsapi::JSContext;
use js::jsapi::JS_ClearPendingException;
use js::jsapi::JS_ErrorFromException;
use js::jsapi::JS_GetPendingException;
use js::jsapi::JS_IsExceptionPending;
use js::jsapi::JS_SetPendingException;
use js::jsval::UndefinedValue;
use libc::c_uint;
use std::slice::from_raw_parts;

/// DOM exceptions that can be thrown by a native DOM method.
#[derive(Debug, Clone, HeapSizeOf)]
pub enum Error {
    /// IndexSizeError DOMException
    IndexSize,
    /// NotFoundError DOMException
    NotFound,
    /// HierarchyRequestError DOMException
    HierarchyRequest,
    /// WrongDocumentError DOMException
    WrongDocument,
    /// InvalidCharacterError DOMException
    InvalidCharacter,
    /// NotSupportedError DOMException
    NotSupported,
    /// InUseAttributeError DOMException
    InUseAttribute,
    /// InvalidStateError DOMException
    InvalidState,
    /// SyntaxError DOMException
    Syntax,
    /// NamespaceError DOMException
    Namespace,
    /// InvalidAccessError DOMException
    InvalidAccess,
    /// SecurityError DOMException
    Security,
    /// NetworkError DOMException
    Network,
    /// AbortError DOMException
    Abort,
    /// TimeoutError DOMException
    Timeout,
    /// InvalidNodeTypeError DOMException
    InvalidNodeType,
    /// DataCloneError DOMException
    DataClone,
    /// NoModificationAllowedError DOMException
    NoModificationAllowed,
    /// QuotaExceededError DOMException
    QuotaExceeded,
    /// TypeMismatchError DOMException
    TypeMismatch,
    /// InvalidModificationError DOMException
    InvalidModification,

    /// TypeError JavaScript Error
    Type(String),
    /// RangeError JavaScript Error
    Range(String),

    /// A JavaScript exception is already pending.
    JSFailed,
}

/// The return type for IDL operations that can throw DOM exceptions.
pub type Fallible<T> = Result<T, Error>;

/// The return type for IDL operations that can throw DOM exceptions and
/// return `()`.
pub type ErrorResult = Fallible<()>;

/// Set a pending exception for the given `result` on `cx`.
pub unsafe fn throw_dom_exception(cx: *mut JSContext, global: GlobalRef, result: Error) {
    let code = match result {
        Error::IndexSize => DOMErrorName::IndexSizeError,
        Error::NotFound => DOMErrorName::NotFoundError,
        Error::HierarchyRequest => DOMErrorName::HierarchyRequestError,
        Error::WrongDocument => DOMErrorName::WrongDocumentError,
        Error::InvalidCharacter => DOMErrorName::InvalidCharacterError,
        Error::NotSupported => DOMErrorName::NotSupportedError,
        Error::InUseAttribute => DOMErrorName::InUseAttributeError,
        Error::InvalidState => DOMErrorName::InvalidStateError,
        Error::Syntax => DOMErrorName::SyntaxError,
        Error::Namespace => DOMErrorName::NamespaceError,
        Error::InvalidAccess => DOMErrorName::InvalidAccessError,
        Error::Security => DOMErrorName::SecurityError,
        Error::Network => DOMErrorName::NetworkError,
        Error::Abort => DOMErrorName::AbortError,
        Error::Timeout => DOMErrorName::TimeoutError,
        Error::InvalidNodeType => DOMErrorName::InvalidNodeTypeError,
        Error::DataClone => DOMErrorName::DataCloneError,
        Error::NoModificationAllowed => DOMErrorName::NoModificationAllowedError,
        Error::QuotaExceeded => DOMErrorName::QuotaExceededError,
        Error::TypeMismatch => DOMErrorName::TypeMismatchError,
        Error::InvalidModification => DOMErrorName::InvalidModificationError,
        Error::Type(message) => {
            assert!(!JS_IsExceptionPending(cx));
            throw_type_error(cx, &message);
            return;
        },
        Error::Range(message) => {
            assert!(!JS_IsExceptionPending(cx));
            throw_range_error(cx, &message);
            return;
        },
        Error::JSFailed => {
            assert!(JS_IsExceptionPending(cx));
            return;
        }
    };

    assert!(!JS_IsExceptionPending(cx));
    let exception = DOMException::new(global, code);
    rooted!(in(cx) let mut thrown = UndefinedValue());
    exception.to_jsval(cx, thrown.handle_mut());
    JS_SetPendingException(cx, thrown.handle());
}

/// A struct encapsulating information about a runtime script error.
pub struct ErrorInfo {
    /// The error message.
    pub message: String,
    /// The file name.
    pub filename: String,
    /// The line number.
    pub lineno: c_uint,
    /// The column number.
    pub column: c_uint,
}

impl ErrorInfo {
    unsafe fn from_native_error(cx: *mut JSContext, object: HandleObject)
                                -> Option<ErrorInfo> {
        let report = JS_ErrorFromException(cx, object);
        if report.is_null() {
            return None;
        }

        let filename = {
            let filename = (*report).filename as *const u8;
            if !filename.is_null() {
                let length = (0..).find(|idx| *filename.offset(*idx) == 0).unwrap();
                let filename = from_raw_parts(filename, length as usize);
                String::from_utf8_lossy(filename).into_owned()
            } else {
                "none".to_string()
            }
        };

        let lineno = (*report).lineno;
        let column = (*report).column;

        let message = {
            let message = (*report).ucmessage;
            let length = (0..).find(|idx| *message.offset(*idx) == 0).unwrap();
            let message = from_raw_parts(message, length as usize);
            String::from_utf16_lossy(message)
        };

        Some(ErrorInfo {
            filename: filename,
            message: message,
            lineno: lineno,
            column: column,
        })
    }

    fn from_dom_exception(object: HandleObject) -> Option<ErrorInfo> {
        let exception = match root_from_object::<DOMException>(object.get()) {
            Ok(exception) => exception,
            Err(_) => return None,
        };

        Some(ErrorInfo {
            filename: "".to_string(),
            message: exception.Stringifier().into(),
            lineno: 0,
            column: 0,
        })
    }
}

/// Report a pending exception, thereby clearing it.
///
/// The `dispatch_event` argument is temporary and non-standard; passing false
/// prevents dispatching the `error` event.
pub unsafe fn report_pending_exception(cx: *mut JSContext, dispatch_event: bool) {
    if JS_IsExceptionPending(cx) {
        rooted!(in(cx) let mut value = UndefinedValue());
        if !JS_GetPendingException(cx, value.handle_mut()) {
            JS_ClearPendingException(cx);
            error!("Uncaught exception: JS_GetPendingException failed");
            return;
        }

        JS_ClearPendingException(cx);
        let error_info = if value.is_object() {
            rooted!(in(cx) let object = value.to_object());
            let error_info = ErrorInfo::from_native_error(cx, object.handle())
                .or_else(|| ErrorInfo::from_dom_exception(object.handle()));
            match error_info {
                Some(error_info) => error_info,
                None => {
                    error!("Uncaught exception: failed to extract information");
                    return;
                }
            }
        } else {
            match USVString::from_jsval(cx, value.handle(), ()) {
                Ok(ConversionResult::Success(USVString(string))) => {
                    ErrorInfo {
                        message: format!("uncaught exception: {}", string),
                        filename: String::new(),
                        lineno: 0,
                        column: 0,
                    }
                },
                _ => {
                    error!("Uncaught exception: failed to stringify primitive");
                    return;
                },
            }
        };

        error!("Error at {}:{}:{} {}",
               error_info.filename,
               error_info.lineno,
               error_info.column,
               error_info.message);

        if dispatch_event {
            let global = global_root_from_context(cx);
            if let GlobalRef::Window(window) = global.r() {
                window.report_an_error(error_info, value.handle());
            }
        }
    }
}

/// Throw an exception to signal that a `JSVal` can not be converted to any of
/// the types in an IDL union type.
pub unsafe fn throw_not_in_union(cx: *mut JSContext, names: &'static str) {
    assert!(!JS_IsExceptionPending(cx));
    let error = format!("argument could not be converted to any of: {}", names);
    throw_type_error(cx, &error);
}

/// Throw an exception to signal that a `JSObject` can not be converted to a
/// given DOM type.
pub unsafe fn throw_invalid_this(cx: *mut JSContext, proto_id: u16) {
    debug_assert!(!JS_IsExceptionPending(cx));
    let error = format!("\"this\" object does not implement interface {}.",
                        proto_id_to_name(proto_id));
    throw_type_error(cx, &error);
}
<commit_msg>Panic when stringifying an primitive fails.<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/. */

//! Utilities to throw exceptions from Rust bindings.

use dom::bindings::codegen::Bindings::DOMExceptionBinding::DOMExceptionMethods;
use dom::bindings::codegen::PrototypeList::proto_id_to_name;
use dom::bindings::conversions::root_from_object;
use dom::bindings::conversions::{ConversionResult, FromJSValConvertible, ToJSValConvertible};
use dom::bindings::global::{GlobalRef, global_root_from_context};
use dom::bindings::str::USVString;
use dom::domexception::{DOMErrorName, DOMException};
use js::error::{throw_range_error, throw_type_error};
use js::jsapi::HandleObject;
use js::jsapi::JSContext;
use js::jsapi::JS_ClearPendingException;
use js::jsapi::JS_ErrorFromException;
use js::jsapi::JS_GetPendingException;
use js::jsapi::JS_IsExceptionPending;
use js::jsapi::JS_SetPendingException;
use js::jsval::UndefinedValue;
use libc::c_uint;
use std::slice::from_raw_parts;

/// DOM exceptions that can be thrown by a native DOM method.
#[derive(Debug, Clone, HeapSizeOf)]
pub enum Error {
    /// IndexSizeError DOMException
    IndexSize,
    /// NotFoundError DOMException
    NotFound,
    /// HierarchyRequestError DOMException
    HierarchyRequest,
    /// WrongDocumentError DOMException
    WrongDocument,
    /// InvalidCharacterError DOMException
    InvalidCharacter,
    /// NotSupportedError DOMException
    NotSupported,
    /// InUseAttributeError DOMException
    InUseAttribute,
    /// InvalidStateError DOMException
    InvalidState,
    /// SyntaxError DOMException
    Syntax,
    /// NamespaceError DOMException
    Namespace,
    /// InvalidAccessError DOMException
    InvalidAccess,
    /// SecurityError DOMException
    Security,
    /// NetworkError DOMException
    Network,
    /// AbortError DOMException
    Abort,
    /// TimeoutError DOMException
    Timeout,
    /// InvalidNodeTypeError DOMException
    InvalidNodeType,
    /// DataCloneError DOMException
    DataClone,
    /// NoModificationAllowedError DOMException
    NoModificationAllowed,
    /// QuotaExceededError DOMException
    QuotaExceeded,
    /// TypeMismatchError DOMException
    TypeMismatch,
    /// InvalidModificationError DOMException
    InvalidModification,

    /// TypeError JavaScript Error
    Type(String),
    /// RangeError JavaScript Error
    Range(String),

    /// A JavaScript exception is already pending.
    JSFailed,
}

/// The return type for IDL operations that can throw DOM exceptions.
pub type Fallible<T> = Result<T, Error>;

/// The return type for IDL operations that can throw DOM exceptions and
/// return `()`.
pub type ErrorResult = Fallible<()>;

/// Set a pending exception for the given `result` on `cx`.
pub unsafe fn throw_dom_exception(cx: *mut JSContext, global: GlobalRef, result: Error) {
    let code = match result {
        Error::IndexSize => DOMErrorName::IndexSizeError,
        Error::NotFound => DOMErrorName::NotFoundError,
        Error::HierarchyRequest => DOMErrorName::HierarchyRequestError,
        Error::WrongDocument => DOMErrorName::WrongDocumentError,
        Error::InvalidCharacter => DOMErrorName::InvalidCharacterError,
        Error::NotSupported => DOMErrorName::NotSupportedError,
        Error::InUseAttribute => DOMErrorName::InUseAttributeError,
        Error::InvalidState => DOMErrorName::InvalidStateError,
        Error::Syntax => DOMErrorName::SyntaxError,
        Error::Namespace => DOMErrorName::NamespaceError,
        Error::InvalidAccess => DOMErrorName::InvalidAccessError,
        Error::Security => DOMErrorName::SecurityError,
        Error::Network => DOMErrorName::NetworkError,
        Error::Abort => DOMErrorName::AbortError,
        Error::Timeout => DOMErrorName::TimeoutError,
        Error::InvalidNodeType => DOMErrorName::InvalidNodeTypeError,
        Error::DataClone => DOMErrorName::DataCloneError,
        Error::NoModificationAllowed => DOMErrorName::NoModificationAllowedError,
        Error::QuotaExceeded => DOMErrorName::QuotaExceededError,
        Error::TypeMismatch => DOMErrorName::TypeMismatchError,
        Error::InvalidModification => DOMErrorName::InvalidModificationError,
        Error::Type(message) => {
            assert!(!JS_IsExceptionPending(cx));
            throw_type_error(cx, &message);
            return;
        },
        Error::Range(message) => {
            assert!(!JS_IsExceptionPending(cx));
            throw_range_error(cx, &message);
            return;
        },
        Error::JSFailed => {
            assert!(JS_IsExceptionPending(cx));
            return;
        }
    };

    assert!(!JS_IsExceptionPending(cx));
    let exception = DOMException::new(global, code);
    rooted!(in(cx) let mut thrown = UndefinedValue());
    exception.to_jsval(cx, thrown.handle_mut());
    JS_SetPendingException(cx, thrown.handle());
}

/// A struct encapsulating information about a runtime script error.
pub struct ErrorInfo {
    /// The error message.
    pub message: String,
    /// The file name.
    pub filename: String,
    /// The line number.
    pub lineno: c_uint,
    /// The column number.
    pub column: c_uint,
}

impl ErrorInfo {
    unsafe fn from_native_error(cx: *mut JSContext, object: HandleObject)
                                -> Option<ErrorInfo> {
        let report = JS_ErrorFromException(cx, object);
        if report.is_null() {
            return None;
        }

        let filename = {
            let filename = (*report).filename as *const u8;
            if !filename.is_null() {
                let length = (0..).find(|idx| *filename.offset(*idx) == 0).unwrap();
                let filename = from_raw_parts(filename, length as usize);
                String::from_utf8_lossy(filename).into_owned()
            } else {
                "none".to_string()
            }
        };

        let lineno = (*report).lineno;
        let column = (*report).column;

        let message = {
            let message = (*report).ucmessage;
            let length = (0..).find(|idx| *message.offset(*idx) == 0).unwrap();
            let message = from_raw_parts(message, length as usize);
            String::from_utf16_lossy(message)
        };

        Some(ErrorInfo {
            filename: filename,
            message: message,
            lineno: lineno,
            column: column,
        })
    }

    fn from_dom_exception(object: HandleObject) -> Option<ErrorInfo> {
        let exception = match root_from_object::<DOMException>(object.get()) {
            Ok(exception) => exception,
            Err(_) => return None,
        };

        Some(ErrorInfo {
            filename: "".to_string(),
            message: exception.Stringifier().into(),
            lineno: 0,
            column: 0,
        })
    }
}

/// Report a pending exception, thereby clearing it.
///
/// The `dispatch_event` argument is temporary and non-standard; passing false
/// prevents dispatching the `error` event.
pub unsafe fn report_pending_exception(cx: *mut JSContext, dispatch_event: bool) {
    if JS_IsExceptionPending(cx) {
        rooted!(in(cx) let mut value = UndefinedValue());
        if !JS_GetPendingException(cx, value.handle_mut()) {
            JS_ClearPendingException(cx);
            error!("Uncaught exception: JS_GetPendingException failed");
            return;
        }

        JS_ClearPendingException(cx);
        let error_info = if value.is_object() {
            rooted!(in(cx) let object = value.to_object());
            let error_info = ErrorInfo::from_native_error(cx, object.handle())
                .or_else(|| ErrorInfo::from_dom_exception(object.handle()));
            match error_info {
                Some(error_info) => error_info,
                None => {
                    error!("Uncaught exception: failed to extract information");
                    return;
                }
            }
        } else {
            match USVString::from_jsval(cx, value.handle(), ()) {
                Ok(ConversionResult::Success(USVString(string))) => {
                    ErrorInfo {
                        message: format!("uncaught exception: {}", string),
                        filename: String::new(),
                        lineno: 0,
                        column: 0,
                    }
                },
                _ => {
                    panic!("Uncaught exception: failed to stringify primitive");
                },
            }
        };

        error!("Error at {}:{}:{} {}",
               error_info.filename,
               error_info.lineno,
               error_info.column,
               error_info.message);

        if dispatch_event {
            let global = global_root_from_context(cx);
            if let GlobalRef::Window(window) = global.r() {
                window.report_an_error(error_info, value.handle());
            }
        }
    }
}

/// Throw an exception to signal that a `JSVal` can not be converted to any of
/// the types in an IDL union type.
pub unsafe fn throw_not_in_union(cx: *mut JSContext, names: &'static str) {
    assert!(!JS_IsExceptionPending(cx));
    let error = format!("argument could not be converted to any of: {}", names);
    throw_type_error(cx, &error);
}

/// Throw an exception to signal that a `JSObject` can not be converted to a
/// given DOM type.
pub unsafe fn throw_invalid_this(cx: *mut JSContext, proto_id: u16) {
    debug_assert!(!JS_IsExceptionPending(cx));
    let error = format!("\"this\" object does not implement interface {}.",
                        proto_id_to_name(proto_id));
    throw_type_error(cx, &error);
}
<|endoftext|>


<commit_before>use std::collections::VecDeque;

use math::{self, FScalar};
use super::primitive::{Polygon, Polygonal, Triangle, Quad};

pub trait Interpolate: math::Interpolate<FScalar> {}

impl<T> Interpolate for T where T: math::Interpolate<FScalar> {}

pub trait PolygonalExt<T>: Polygonal<T>
    where T: Clone + Interpolate
{
    fn into_subdivisions<F>(self, n: usize, f: F) where F: FnMut(Polygon<T>);
}

pub trait QuadExt<T>
    where T: Clone + Interpolate
{
    fn into_tetrahedrons<F>(self, f: F) where F: FnMut(Triangle<T>);
}

impl<T> PolygonalExt<T> for Triangle<T>
    where T: Clone + Interpolate
{
    fn into_subdivisions<F>(self, n: usize, mut f: F)
        where F: FnMut(Polygon<T>)
    {
        for triangle in remap_polygon(n, self, |triangle| {
            let Triangle { a, b, c } = triangle;
            let ac = a.lerp(&c, 0.5);
            vec![Triangle::new(b.clone(), ac.clone(), a),
                 Triangle::new(c, ac, b)]
        }) {
            f(Polygon::Triangle(triangle));
        }
    }
}

impl<T> PolygonalExt<T> for Quad<T>
    where T: Clone + Interpolate
{
    fn into_subdivisions<F>(self, n: usize, mut f: F)
        where F: FnMut(Polygon<T>)
    {
        for quad in remap_polygon(n, self, |quad| {
            let Quad { a, b, c, d } = quad;
            let ab = a.lerp(&b, 0.5);
            let bc = b.lerp(&c, 0.5);
            let cd = c.lerp(&d, 0.5);
            let da = d.lerp(&a, 0.5);
            let ac = a.lerp(&c, 0.5); // Diagonal.
            vec![Quad::new(a, ab.clone(), ac.clone(), da.clone()),
                 Quad::new(ab, b, bc.clone(), ac.clone()),
                 Quad::new(ac.clone(), bc, c, cd.clone()),
                 Quad::new(da, ac, cd, d)]
        }) {
            f(Polygon::Quad(quad));
        }
    }
}

impl<T> QuadExt<T> for Quad<T>
    where T: Clone + Interpolate
{
    fn into_tetrahedrons<F>(self, mut f: F)
        where F: FnMut(Triangle<T>)
    {
        let Quad { a, b, c, d } = self;
        let ac = a.lerp(&c, 0.5); // Diagonal.
        f(Triangle::new(a.clone(), b.clone(), ac.clone()));
        f(Triangle::new(b, c.clone(), ac.clone()));
        f(Triangle::new(c, d.clone(), ac.clone()));
        f(Triangle::new(d, a, ac));
    }
}

impl<T> PolygonalExt<T> for Polygon<T>
    where T: Clone + Interpolate
{
    fn into_subdivisions<F>(self, n: usize, f: F)
        where F: FnMut(Polygon<T>)
    {
        match self {
            Polygon::Triangle(triangle) => triangle.into_subdivisions(n, f),
            Polygon::Quad(quad) => quad.into_subdivisions(n, f),
        }
    }
}

pub trait TessellatePolygon<T>: Sized {
    fn subdivide(self, n: usize) -> SubdivisionIter<Self, T>;
}

impl TessellatePolygon<T> for I
    where I: Iterator<Item = P>,
          T: Clone + Interpolate,
          P: PolygonalExt<T>
{
    fn subdivide(self, n: usize) -> SubdivisionIter<Self, T> {
        SubdivisionIter::new(self, n)
    }
}

pub struct SubdivisionIter {
    polygons: I,
    subdivisions: VecDeque<Polygon<T>>,
    n: usize,
}

impl SubdivisionIter {
    fn new(polygons: I, n: usize) -> Self {
        SubdivisionIter {
            polygons: polygons,
            subdivisions: VecDeque::new(),
            n: n,
        }
    }
}

impl Iterator for SubdivisionIter
    where I: Iterator<Item = P>,
          T: Clone + Interpolate,
          P: PolygonalExt<T>
{
    type Item = Polygon<T>;

    fn next(&mut self) -> Option<Self::Item> {
        loop {
            if let Some(subdivision) = self.subdivisions.pop_front() {
                return Some(subdivision);
            }
            if let Some(polygon) = self.polygons.next() {
                polygon.into_subdivisions(self.n, |polygon| self.subdivisions.push_back(polygon))
            }
            else {
                return None;
            }
        }
    }
}

pub trait TessellateQuad<T>: Sized {
    fn tetrahedrons(self) -> TetrahedronIter<Self, T>;
}

impl TessellateQuad<T> for I
    where I: Iterator<Item = Quad<T>>
{
    fn tetrahedrons(self) -> TetrahedronIter<Self, T> {
        TetrahedronIter::new(self)
    }
}

pub struct TetrahedronIter {
    quads: I,
    triangles: VecDeque<Triangle<T>>,
}

impl TetrahedronIter {
    fn new(quads: I) -> Self {
        TetrahedronIter {
            quads: quads,
            triangles: VecDeque::new(),
        }
    }
}

impl Iterator for TetrahedronIter
    where I: Iterator<Item = Quad<T>>,
          T: Clone + Interpolate
{
    type Item = Triangle<T>;

    fn next(&mut self) -> Option<Self::Item> {
        loop {
            if let Some(triangle) = self.triangles.pop_front() {
                return Some(triangle);
            }
            if let Some(quad) = self.quads.next() {
                quad.into_tetrahedrons(|triangle| self.triangles.push_back(triangle))
            }
            else {
                return None;
            }
        }
    }
}

fn remap_polygon<T, P, F>(n: usize, polygon: P, f: F) -> Vec

    where P: Polygonal<T>,
          F: Fn(P) -> Vec

{
    let mut queued = vec![];
    let mut transformed = vec![polygon];
    for _ in 0..n {
        queued.extend(transformed.drain(..));
        while let Some(polygon) = queued.pop() {
            transformed.extend(f(polygon));
        }
    }
    transformed
}
<commit_msg>Rename `remap_polygon` to `n_map_polygon`.<commit_after>use std::collections::VecDeque;

use math::{self, FScalar};
use super::primitive::{Polygon, Polygonal, Triangle, Quad};

pub trait Interpolate: math::Interpolate<FScalar> {}

impl<T> Interpolate for T where T: math::Interpolate<FScalar> {}

pub trait PolygonalExt<T>: Polygonal<T>
    where T: Clone + Interpolate
{
    fn into_subdivisions<F>(self, n: usize, f: F) where F: FnMut(Polygon<T>);
}

pub trait QuadExt<T>
    where T: Clone + Interpolate
{
    fn into_tetrahedrons<F>(self, f: F) where F: FnMut(Triangle<T>);
}

impl<T> PolygonalExt<T> for Triangle<T>
    where T: Clone + Interpolate
{
    fn into_subdivisions<F>(self, n: usize, mut f: F)
        where F: FnMut(Polygon<T>)
    {
        for triangle in n_map_polygon(n, self, |triangle| {
            let Triangle { a, b, c } = triangle;
            let ac = a.lerp(&c, 0.5);
            vec![Triangle::new(b.clone(), ac.clone(), a),
                 Triangle::new(c, ac, b)]
        }) {
            f(Polygon::Triangle(triangle));
        }
    }
}

impl<T> PolygonalExt<T> for Quad<T>
    where T: Clone + Interpolate
{
    fn into_subdivisions<F>(self, n: usize, mut f: F)
        where F: FnMut(Polygon<T>)
    {
        for quad in n_map_polygon(n, self, |quad| {
            let Quad { a, b, c, d } = quad;
            let ab = a.lerp(&b, 0.5);
            let bc = b.lerp(&c, 0.5);
            let cd = c.lerp(&d, 0.5);
            let da = d.lerp(&a, 0.5);
            let ac = a.lerp(&c, 0.5); // Diagonal.
            vec![Quad::new(a, ab.clone(), ac.clone(), da.clone()),
                 Quad::new(ab, b, bc.clone(), ac.clone()),
                 Quad::new(ac.clone(), bc, c, cd.clone()),
                 Quad::new(da, ac, cd, d)]
        }) {
            f(Polygon::Quad(quad));
        }
    }
}

impl<T> QuadExt<T> for Quad<T>
    where T: Clone + Interpolate
{
    fn into_tetrahedrons<F>(self, mut f: F)
        where F: FnMut(Triangle<T>)
    {
        let Quad { a, b, c, d } = self;
        let ac = a.lerp(&c, 0.5); // Diagonal.
        f(Triangle::new(a.clone(), b.clone(), ac.clone()));
        f(Triangle::new(b, c.clone(), ac.clone()));
        f(Triangle::new(c, d.clone(), ac.clone()));
        f(Triangle::new(d, a, ac));
    }
}

impl<T> PolygonalExt<T> for Polygon<T>
    where T: Clone + Interpolate
{
    fn into_subdivisions<F>(self, n: usize, f: F)
        where F: FnMut(Polygon<T>)
    {
        match self {
            Polygon::Triangle(triangle) => triangle.into_subdivisions(n, f),
            Polygon::Quad(quad) => quad.into_subdivisions(n, f),
        }
    }
}

pub trait TessellatePolygon<T>: Sized {
    fn subdivide(self, n: usize) -> SubdivisionIter<Self, T>;
}

impl TessellatePolygon<T> for I
    where I: Iterator<Item = P>,
          T: Clone + Interpolate,
          P: PolygonalExt<T>
{
    fn subdivide(self, n: usize) -> SubdivisionIter<Self, T> {
        SubdivisionIter::new(self, n)
    }
}

pub struct SubdivisionIter {
    polygons: I,
    subdivisions: VecDeque<Polygon<T>>,
    n: usize,
}

impl SubdivisionIter {
    fn new(polygons: I, n: usize) -> Self {
        SubdivisionIter {
            polygons: polygons,
            subdivisions: VecDeque::new(),
            n: n,
        }
    }
}

impl Iterator for SubdivisionIter
    where I: Iterator<Item = P>,
          T: Clone + Interpolate,
          P: PolygonalExt<T>
{
    type Item = Polygon<T>;

    fn next(&mut self) -> Option<Self::Item> {
        loop {
            if let Some(subdivision) = self.subdivisions.pop_front() {
                return Some(subdivision);
            }
            if let Some(polygon) = self.polygons.next() {
                polygon.into_subdivisions(self.n, |polygon| self.subdivisions.push_back(polygon))
            }
            else {
                return None;
            }
        }
    }
}

pub trait TessellateQuad<T>: Sized {
    fn tetrahedrons(self) -> TetrahedronIter<Self, T>;
}

impl TessellateQuad<T> for I
    where I: Iterator<Item = Quad<T>>
{
    fn tetrahedrons(self) -> TetrahedronIter<Self, T> {
        TetrahedronIter::new(self)
    }
}

pub struct TetrahedronIter {
    quads: I,
    triangles: VecDeque<Triangle<T>>,
}

impl TetrahedronIter {
    fn new(quads: I) -> Self {
        TetrahedronIter {
            quads: quads,
            triangles: VecDeque::new(),
        }
    }
}

impl Iterator for TetrahedronIter
    where I: Iterator<Item = Quad<T>>,
          T: Clone + Interpolate
{
    type Item = Triangle<T>;

    fn next(&mut self) -> Option<Self::Item> {
        loop {
            if let Some(triangle) = self.triangles.pop_front() {
                return Some(triangle);
            }
            if let Some(quad) = self.quads.next() {
                quad.into_tetrahedrons(|triangle| self.triangles.push_back(triangle))
            }
            else {
                return None;
            }
        }
    }
}

fn n_map_polygon<T, P, F>(n: usize, polygon: P, f: F) -> Vec

    where P: Polygonal<T>,
          F: Fn(P) -> Vec

{
    let mut queued = vec![];
    let mut transformed = vec![polygon];
    for _ in 0..n {
        queued.extend(transformed.drain(..));
        while let Some(polygon) = queued.pop() {
            transformed.extend(f(polygon));
        }
    }
    transformed
}
<|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/. */

//! Specified types for UI properties.

use crate::parser::{Parse, ParserContext};
use crate::values::generics::ui as generics;
use crate::values::specified::color::Color;
use crate::values::specified::url::SpecifiedImageUrl;
use crate::values::specified::Number;
use crate::values::{Auto, Either};
use cssparser::Parser;
use std::fmt::{self, Write};
use style_traits::cursor::CursorKind;
use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};

/// auto | <color>
pub type ColorOrAuto = Either<Color, Auto>;

/// A specified value for the `cursor` property.
pub type Cursor = generics::Cursor<CursorImage>;

/// A specified value for item of `image cursors`.
pub type CursorImage = generics::CursorImage<SpecifiedImageUrl, Number>;

impl Parse for Cursor {
    /// cursor: [<url> [<number> <number>]?]# [auto | default | ...]
    fn parse<'i, 't>(
        context: &ParserContext,
        input: &mut Parser<'i, 't>,
    ) -> Result<Self, ParseError<'i>> {
        let mut images = vec![];
        loop {
            match input.try(|input| CursorImage::parse(context, input)) {
                Ok(image) => images.push(image),
                Err(_) => break,
            }
            input.expect_comma()?;
        }
        Ok(Self {
            images: images.into_boxed_slice(),
            keyword: CursorKind::parse(context, input)?,
        })
    }
}

impl Parse for CursorKind {
    fn parse<'i, 't>(
        _context: &ParserContext,
        input: &mut Parser<'i, 't>,
    ) -> Result<Self, ParseError<'i>> {
        let location = input.current_source_location();
        let ident = input.expect_ident()?;
        CursorKind::from_css_keyword(&ident)
            .map_err(|_| location.new_custom_error(StyleParseErrorKind::UnspecifiedError))
    }
}

impl Parse for CursorImage {
    fn parse<'i, 't>(
        context: &ParserContext,
        input: &mut Parser<'i, 't>,
    ) -> Result<Self, ParseError<'i>> {
        Ok(Self {
            url: SpecifiedImageUrl::parse(context, input)?,
            hotspot: match input.try(|input| Number::parse(context, input)) {
                Ok(number) => Some((number, Number::parse(context, input)?)),
                Err(_) => None,
            },
        })
    }
}

/// Specified value of `-moz-force-broken-image-icon`
#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToComputedValue)]
pub struct MozForceBrokenImageIcon(pub bool);

impl MozForceBrokenImageIcon {
    /// Return initial value of -moz-force-broken-image-icon which is false.
    #[inline]
    pub fn false_value() -> MozForceBrokenImageIcon {
        MozForceBrokenImageIcon(false)
    }
}

impl Parse for MozForceBrokenImageIcon {
    fn parse<'i, 't>(
        _context: &ParserContext,
        input: &mut Parser<'i, 't>,
    ) -> Result<MozForceBrokenImageIcon, ParseError<'i>> {
        // We intentionally don't support calc values here.
        match input.expect_integer()? {
            0 => Ok(MozForceBrokenImageIcon(false)),
            1 => Ok(MozForceBrokenImageIcon(true)),
            _ => Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
        }
    }
}

impl ToCss for MozForceBrokenImageIcon {
    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
    where
        W: Write,
    {
        dest.write_str(if self.0 { "1" } else { "0" })
    }
}

impl From<u8> for MozForceBrokenImageIcon {
    fn from(bits: u8) -> MozForceBrokenImageIcon {
        MozForceBrokenImageIcon(bits == 1)
    }
}

impl From<MozForceBrokenImageIcon> for u8 {
    fn from(v: MozForceBrokenImageIcon) -> u8 {
        if v.0 {
            1
        } else {
            0
        }
    }
}

/// A specified value for `scrollbar-color` property
pub type ScrollbarColor = generics::ScrollbarColor<Color>;

impl Parse for ScrollbarColor {
    fn parse<'i, 't>(
        context: &ParserContext,
        input: &mut Parser<'i, 't>,
    ) -> Result<Self, ParseError<'i>> {
        if input.try(|i| i.expect_ident_matching("auto")).is_ok() {
            return Ok(generics::ScrollbarColor::Auto);
        }
        Ok(generics::ScrollbarColor::Colors {
            thumb: Color::parse(context, input)?,
            track: Color::parse(context, input)?,
        })
    }
}

/// The specified value for the `user-select` property.
///
/// https://drafts.csswg.org/css-ui-4/#propdef-user-select
#[allow(missing_docs)]
#[derive(
    Clone,
    Copy,
    Debug,
    Eq,
    MallocSizeOf,
    Parse,
    PartialEq,
    SpecifiedValueInfo,
    ToComputedValue,
    ToCss,
)]
#[repr(u8)]
pub enum UserSelect {
    Auto,
    Text,
    #[parse(aliases = "-moz-none")]
    None,
    Element,
    Elements,
    All,
    Toggle,
    TriState,
    /// Force selection of all children, unless an ancestor has `none` set.
    ///
    /// See bug 48096.
    MozAll,
    /// Like `text`, except that it won't get overridden by ancestors having
    /// `all`.
    MozText,
}
<commit_msg>style: Remove unimplemented values of the user-select property.<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/. */

//! Specified types for UI properties.

use crate::parser::{Parse, ParserContext};
use crate::values::generics::ui as generics;
use crate::values::specified::color::Color;
use crate::values::specified::url::SpecifiedImageUrl;
use crate::values::specified::Number;
use crate::values::{Auto, Either};
use cssparser::Parser;
use std::fmt::{self, Write};
use style_traits::cursor::CursorKind;
use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};

/// auto | <color>
pub type ColorOrAuto = Either<Color, Auto>;

/// A specified value for the `cursor` property.
pub type Cursor = generics::Cursor<CursorImage>;

/// A specified value for item of `image cursors`.
pub type CursorImage = generics::CursorImage<SpecifiedImageUrl, Number>;

impl Parse for Cursor {
    /// cursor: [<url> [<number> <number>]?]# [auto | default | ...]
    fn parse<'i, 't>(
        context: &ParserContext,
        input: &mut Parser<'i, 't>,
    ) -> Result<Self, ParseError<'i>> {
        let mut images = vec![];
        loop {
            match input.try(|input| CursorImage::parse(context, input)) {
                Ok(image) => images.push(image),
                Err(_) => break,
            }
            input.expect_comma()?;
        }
        Ok(Self {
            images: images.into_boxed_slice(),
            keyword: CursorKind::parse(context, input)?,
        })
    }
}

impl Parse for CursorKind {
    fn parse<'i, 't>(
        _context: &ParserContext,
        input: &mut Parser<'i, 't>,
    ) -> Result<Self, ParseError<'i>> {
        let location = input.current_source_location();
        let ident = input.expect_ident()?;
        CursorKind::from_css_keyword(&ident)
            .map_err(|_| location.new_custom_error(StyleParseErrorKind::UnspecifiedError))
    }
}

impl Parse for CursorImage {
    fn parse<'i, 't>(
        context: &ParserContext,
        input: &mut Parser<'i, 't>,
    ) -> Result<Self, ParseError<'i>> {
        Ok(Self {
            url: SpecifiedImageUrl::parse(context, input)?,
            hotspot: match input.try(|input| Number::parse(context, input)) {
                Ok(number) => Some((number, Number::parse(context, input)?)),
                Err(_) => None,
            },
        })
    }
}

/// Specified value of `-moz-force-broken-image-icon`
#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToComputedValue)]
pub struct MozForceBrokenImageIcon(pub bool);

impl MozForceBrokenImageIcon {
    /// Return initial value of -moz-force-broken-image-icon which is false.
    #[inline]
    pub fn false_value() -> MozForceBrokenImageIcon {
        MozForceBrokenImageIcon(false)
    }
}

impl Parse for MozForceBrokenImageIcon {
    fn parse<'i, 't>(
        _context: &ParserContext,
        input: &mut Parser<'i, 't>,
    ) -> Result<MozForceBrokenImageIcon, ParseError<'i>> {
        // We intentionally don't support calc values here.
        match input.expect_integer()? {
            0 => Ok(MozForceBrokenImageIcon(false)),
            1 => Ok(MozForceBrokenImageIcon(true)),
            _ => Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
        }
    }
}

impl ToCss for MozForceBrokenImageIcon {
    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
    where
        W: Write,
    {
        dest.write_str(if self.0 { "1" } else { "0" })
    }
}

impl From<u8> for MozForceBrokenImageIcon {
    fn from(bits: u8) -> MozForceBrokenImageIcon {
        MozForceBrokenImageIcon(bits == 1)
    }
}

impl From<MozForceBrokenImageIcon> for u8 {
    fn from(v: MozForceBrokenImageIcon) -> u8 {
        if v.0 {
            1
        } else {
            0
        }
    }
}

/// A specified value for `scrollbar-color` property
pub type ScrollbarColor = generics::ScrollbarColor<Color>;

impl Parse for ScrollbarColor {
    fn parse<'i, 't>(
        context: &ParserContext,
        input: &mut Parser<'i, 't>,
    ) -> Result<Self, ParseError<'i>> {
        if input.try(|i| i.expect_ident_matching("auto")).is_ok() {
            return Ok(generics::ScrollbarColor::Auto);
        }
        Ok(generics::ScrollbarColor::Colors {
            thumb: Color::parse(context, input)?,
            track: Color::parse(context, input)?,
        })
    }
}

/// The specified value for the `user-select` property.
///
/// https://drafts.csswg.org/css-ui-4/#propdef-user-select
#[allow(missing_docs)]
#[derive(
    Clone,
    Copy,
    Debug,
    Eq,
    MallocSizeOf,
    Parse,
    PartialEq,
    SpecifiedValueInfo,
    ToComputedValue,
    ToCss,
)]
#[repr(u8)]
pub enum UserSelect {
    Auto,
    Text,
    #[parse(aliases = "-moz-none")]
    None,
    All,
    /// Force selection of all children, unless an ancestor has `none` set.
    ///
    /// See bug 48096.
    MozAll,
    /// Like `text`, except that it won't get overridden by ancestors having
    /// `all`.
    MozText,
}
<|endoftext|>


<commit_before>use nom::{
  be_u8, be_u16, be_u32, be_u64,
  le_u32,
  IResult,
  ErrorCode, Err,
  Needed,
};

use std::str::from_utf8;

use metadata::{
  Block, BlockData,
  StreamInfo, Application, VorbisComment, CueSheet, Picture,
  SeekPoint, CueSheetTrack, CueSheetTrackIndex, PictureType,
};

use utility::to_u32;

macro_rules! skip_bytes(
  ($input: expr, $length: expr) => (
    {
      match take!($input, $length) {
        IResult::Done(i, bytes)   => {
          let sum = bytes.iter().fold(0, |result, byte| result + byte);

          if sum == 0 {
            IResult::Done(i, bytes)
          } else {
            IResult::Error(Err::Position(ErrorCode::Digit as u32, $input))
          }
        }
        IResult::Error(error)     => IResult::Error(error),
        IResult::Incomplete(need) => IResult::Incomplete(need),
      }
    }
  );
);

named!(pub stream_info <&[u8], BlockData>,
  chain!(
    min_block_size: be_u16 ~
    max_block_size: be_u16 ~
    min_frame_size: map!(take!(3), to_u32) ~
    max_frame_size: map!(take!(3), to_u32) ~
    bytes: take!(8) ~
    md5_sum: take!(16),
    || {
      let sample_rate     = ((bytes[0] as u32) << 12) +
                            ((bytes[1] as u32) << 4)  +
                            (bytes[2] as u32) >> 4;
      let channels        = (bytes[2] >> 1) & 0b0111;
      let bits_per_sample = ((bytes[2] & 0b01) << 4) +
                            bytes[3] >> 4;
      let total_samples   = (((bytes[3] as u64) & 0x0f) << 32) +
                            ((bytes[4] as u64) << 24) +
                            ((bytes[5] as u64) << 16) +
                            ((bytes[6] as u64) << 8) +
                            (bytes[7] as u64);

      BlockData::StreamInfo(StreamInfo {
        min_block_size: min_block_size,
        max_block_size: max_block_size,
        min_frame_size: min_frame_size,
        max_frame_size: max_frame_size,
        sample_rate: sample_rate,
        channels: channels + 1,
        bits_per_sample: bits_per_sample + 1,
        total_samples: total_samples,
        md5_sum: md5_sum,
      })
    }
  )
);

fn padding(input: &[u8], length: u32) -> IResult<&[u8], BlockData> {
  map!(input, skip_bytes!(length), |_| BlockData::Padding(0))
}

fn application(input: &[u8], length: u32) -> IResult<&[u8], BlockData> {
  chain!(input,
    id: take_str!(4) ~
    data: take!(length - 4),
    || {
      BlockData::Application(Application {
        id: id,
        data: data,
      })
    }
  )
}

named!(seek_point <&[u8], SeekPoint>,
  chain!(
    sample_number: be_u64 ~
    stream_offset: be_u64 ~
    frame_samples: be_u16,
    || {
      SeekPoint {
        sample_number: sample_number,
        stream_offset: stream_offset,
        frame_samples: frame_samples,
      }
    }
  )
);

fn seek_table(input: &[u8], length: u32) -> IResult<&[u8], BlockData> {
  let seek_count = (length / 18) as usize;

  map!(input, count!(seek_point, seek_count), BlockData::SeekTable)
}

named!(vorbis_comment <&[u8], BlockData>,
  chain!(
    vendor_string_length: le_u32 ~
    vendor_string: take_str!(vendor_string_length)  ~
    number_of_comments: le_u32 ~
    comments: count!(comment_field, number_of_comments as usize),
    || {
      BlockData::VorbisComment(VorbisComment {
        vendor_string: vendor_string,
        comments: comments,
      })
    }
  )
);

named!(comment_field <&[u8], &str>,
  chain!(
    comment_length: le_u32 ~
    comment: take_str!(comment_length),
    || { comment }
  )
);

named!(cue_sheet <&[u8], BlockData>,
  chain!(
    media_catalog_number: take_str!(128) ~
    lead_in: be_u64 ~
    bytes: take!(259) ~ // TODO: last (7 + 258 * 8) bits must be 0
    num_tracks: be_u8 ~
    tracks: count!(cue_sheet_track, num_tracks as usize),
    || {
      let is_cd = ((bytes[0] >> 7) & 0b01) == 1;

      BlockData::CueSheet(CueSheet {
        media_catalog_number: media_catalog_number,
        lead_in: lead_in,
        is_cd: is_cd,
        tracks: tracks,
      })
    }
  )
);

named!(cue_sheet_track <&[u8], CueSheetTrack>,
  chain!(
    offset: be_u64 ~
    number: be_u8 ~
    isrc: take_str!(12) ~
    bytes: take!(14) ~ // TODO: last (6 + 13 * 8) bits must be 0
    num_indices: be_u8 ~
    indices: cond!(
      num_indices != 0,
      count!(cue_sheet_track_index, num_indices as usize)
    ),
    || {
      let isnt_audio      = ((bytes[0] >> 7) & 0b01) == 1;
      let is_pre_emphasis = ((bytes[0] >> 6) & 0b01) == 1;

      CueSheetTrack {
        offset: offset,
        number: number,
        isrc: isrc,
        isnt_audio: isnt_audio,
        is_pre_emphasis: is_pre_emphasis,
        indices: indices.unwrap_or(Vec::new()),
      }
    }
  )
);

named!(cue_sheet_track_index <&[u8], CueSheetTrackIndex>,
  chain!(
    offset: be_u64 ~
    number: be_u8 ~
    skip_bytes!(3),
    || {
      CueSheetTrackIndex {
        offset: offset,
        number: number,
      }
    }
  )
);

named!(picture <&[u8], BlockData>,
  chain!(
    picture_type_num: be_u32 ~
    mime_type_length:  be_u32 ~
    mime_type: take_str!(mime_type_length) ~
    description_length: be_u32 ~
    description: take_str!(description_length) ~
    width: be_u32 ~
    height: be_u32 ~
    depth: be_u32 ~
    colors: be_u32 ~
    data_length: be_u32 ~
    data: take!(data_length),
    || {
      let picture_type = match picture_type_num {
        0  => PictureType::Other,
        1  => PictureType::FileIconStandard,
        2  => PictureType::FileIcon,
        3  => PictureType::FrontCover,
        4  => PictureType::BackCover,
        5  => PictureType::LeafletPage,
        6  => PictureType::Media,
        7  => PictureType::LeadArtist,
        8  => PictureType::Artist,
        9  => PictureType::Conductor,
        10 => PictureType::Band,
        11 => PictureType::Composer,
        12 => PictureType::Lyricist,
        13 => PictureType::RecordingLocation,
        14 => PictureType::DuringRecording,
        15 => PictureType::DuringPerformace,
        16 => PictureType::VideoScreenCapture,
        17 => PictureType::Fish,
        18 => PictureType::Illustration,
        19 => PictureType::BandLogoType,
        20 => PictureType::PublisherLogoType,
        _  => PictureType::Other,
      };

      BlockData::Picture(Picture {
        picture_type: picture_type,
        mime_type: mime_type,
        description: description,
        width: width,
        height: height,
        depth: depth,
        colors: colors,
        data: data,
      })
    }
  )
);

// As of FLAC v1.3.1, there is support for up to 127 different metadata
// `Block`s but actually 7 that are implemented. When the `Block` type isn't
// recognised, this block gets skipped over with this parser.
fn unknown(input: &[u8], length: u32) -> IResult<&[u8], BlockData> {
  map!(input, take!(length), BlockData::Unknown)
}

named!(pub header <&[u8], (bool, u8, u32)>,
  chain!(
    block_byte: be_u8 ~
    length: map!(take!(3), to_u32),
    || {
      let is_last    = (block_byte >> 7) == 1;
      let block_type = block_byte & 0b01111111;

      (is_last, block_type, length)
    }
  )
);

fn block_data(input: &[u8], block_type: u8, length: u32)
              -> IResult<&[u8], BlockData> {
  match block_type {
    0       => stream_info(input),
    1       => padding(input, length),
    2       => application(input, length),
    3       => seek_table(input, length),
    4       => vorbis_comment(input),
    5       => cue_sheet(input),
    6       => picture(input),
    7...126 => unknown(input, length),
    _       => IResult::Error(Err::Position(ErrorCode::Alt as u32, input)),
  }
}

named!(block <&[u8], Block>,
  chain!(
    block_header: header ~
    data: apply!(block_data, block_header.1, block_header.2),
    || {
      Block {
        is_last: block_header.0,
        length: block_header.2,
        data: data
      }
    }
  )
);

/// Parses one or more metadata `Block`s
///
/// The first block should always be `StreamInfo` since that is the only
/// required `Block`. At the moment `many_blocks` doesn't check that
/// requirement.
pub fn many_blocks(input: &[u8]) -> IResult<&[u8], Vec<Block>> {
  let mut is_last   = false;
  let mut blocks    = Vec::new();
  let mut start     = 0;
  let mut remaining = input.len();

  while !is_last {
    match block(&input[start..]) {
      IResult::Done(i, block) => {
        let result_len = i.len();

        if result_len == input[start..].len() {
          break;
        }

        start    += remaining - result_len;
        remaining = result_len;
        is_last   = block.is_last;

        blocks.push(block);
      }
      _                       => break,
    }
  }

  if blocks.len() == 0 {
    IResult::Error(Err::Position(ErrorCode::Many1 as u32, input))
  } else if is_last {
    IResult::Done(&input[start..], blocks)
  } else {
    IResult::Incomplete(Needed::Unknown)
  }
}

#[cfg(test)]
mod tests {
  use super::*;
  use metadata::{
    BlockData,
    StreamInfo,
  };
  use nom::IResult;

  #[test]
  fn test_header() {
    let inputs = [ [0x80, 0x00, 0x00, 0x22]
                 , [0x01, 0x00, 0x04, 0x00]
                 , [0x84, 0x00, 0x00, 0xf8]
                 ];

    assert!(header(&inputs[0]) == IResult::Done(&[], (true, 0, 34)),
            "Header Test #1");
    assert!(header(&inputs[1]) == IResult::Done(&[], (false, 1, 1024)),
            "Header Test #2");
    assert!(header(&inputs[2]) == IResult::Done(&[], (true, 4, 248)),
            "Header Test #3");
  }

  #[test]
  fn test_stream_info() {
    let input   = [ 0x12, 0x00, 0x12, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x10
                  , 0x01, 0xf4, 0x02, 0x70, 0x00, 0x01, 0x38, 0x80, 0xa0, 0x42
                  , 0x23, 0x7c, 0x54, 0x93, 0xfd, 0xb9, 0x65, 0x6b, 0x94, 0xa8
                  , 0x36, 0x08, 0xd1, 0x1a
                  ];

    let md5_sum = [ 0xa0, 0x42, 0x23, 0x7c, 0x54, 0x93, 0xfd, 0xb9, 0x65, 0x6b
                  , 0x94, 0xa8, 0x36, 0x08, 0xd1, 0x1a
                  ];

    let result = BlockData::StreamInfo(StreamInfo {
      min_block_size: 4608,
      max_block_size: 4608,
      min_frame_size: 14,
      max_frame_size: 16,
      sample_rate: 8000,
      channels: 2,
      bits_per_sample: 8,
      total_samples: 80000,
      md5_sum: &md5_sum,
    });

    assert_eq!(stream_info(&input), IResult::Done(&[][..], result));
  }
}
<commit_msg>Fix failing test test_stream_info<commit_after>use nom::{
  be_u8, be_u16, be_u32, be_u64,
  le_u32,
  IResult,
  ErrorCode, Err,
  Needed,
};

use std::str::from_utf8;

use metadata::{
  Block, BlockData,
  StreamInfo, Application, VorbisComment, CueSheet, Picture,
  SeekPoint, CueSheetTrack, CueSheetTrackIndex, PictureType,
};

use utility::to_u32;

macro_rules! skip_bytes(
  ($input: expr, $length: expr) => (
    {
      match take!($input, $length) {
        IResult::Done(i, bytes)   => {
          let sum = bytes.iter().fold(0, |result, byte| result + byte);

          if sum == 0 {
            IResult::Done(i, bytes)
          } else {
            IResult::Error(Err::Position(ErrorCode::Digit as u32, $input))
          }
        }
        IResult::Error(error)     => IResult::Error(error),
        IResult::Incomplete(need) => IResult::Incomplete(need),
      }
    }
  );
);

named!(pub stream_info <&[u8], BlockData>,
  chain!(
    min_block_size: be_u16 ~
    max_block_size: be_u16 ~
    min_frame_size: map!(take!(3), to_u32) ~
    max_frame_size: map!(take!(3), to_u32) ~
    bytes: take!(8) ~
    md5_sum: take!(16),
    || {
      let sample_rate     = ((bytes[0] as u32) << 12) +
                            ((bytes[1] as u32) << 4)  +
                            ((bytes[2] as u32) >> 4);
      let channels        = (bytes[2] >> 1) & 0b0111;
      let bits_per_sample = ((bytes[2] & 0b01) << 4) +
                            bytes[3] >> 4;
      let total_samples   = (((bytes[3] as u64) & 0x0f) << 32) +
                            ((bytes[4] as u64) << 24) +
                            ((bytes[5] as u64) << 16) +
                            ((bytes[6] as u64) << 8) +
                            (bytes[7] as u64);

      BlockData::StreamInfo(StreamInfo {
        min_block_size: min_block_size,
        max_block_size: max_block_size,
        min_frame_size: min_frame_size,
        max_frame_size: max_frame_size,
        sample_rate: sample_rate,
        channels: channels + 1,
        bits_per_sample: bits_per_sample + 1,
        total_samples: total_samples,
        md5_sum: md5_sum,
      })
    }
  )
);

fn padding(input: &[u8], length: u32) -> IResult<&[u8], BlockData> {
  map!(input, skip_bytes!(length), |_| BlockData::Padding(0))
}

fn application(input: &[u8], length: u32) -> IResult<&[u8], BlockData> {
  chain!(input,
    id: take_str!(4) ~
    data: take!(length - 4),
    || {
      BlockData::Application(Application {
        id: id,
        data: data,
      })
    }
  )
}

named!(seek_point <&[u8], SeekPoint>,
  chain!(
    sample_number: be_u64 ~
    stream_offset: be_u64 ~
    frame_samples: be_u16,
    || {
      SeekPoint {
        sample_number: sample_number,
        stream_offset: stream_offset,
        frame_samples: frame_samples,
      }
    }
  )
);

fn seek_table(input: &[u8], length: u32) -> IResult<&[u8], BlockData> {
  let seek_count = (length / 18) as usize;

  map!(input, count!(seek_point, seek_count), BlockData::SeekTable)
}

named!(vorbis_comment <&[u8], BlockData>,
  chain!(
    vendor_string_length: le_u32 ~
    vendor_string: take_str!(vendor_string_length)  ~
    number_of_comments: le_u32 ~
    comments: count!(comment_field, number_of_comments as usize),
    || {
      BlockData::VorbisComment(VorbisComment {
        vendor_string: vendor_string,
        comments: comments,
      })
    }
  )
);

named!(comment_field <&[u8], &str>,
  chain!(
    comment_length: le_u32 ~
    comment: take_str!(comment_length),
    || { comment }
  )
);

named!(cue_sheet <&[u8], BlockData>,
  chain!(
    media_catalog_number: take_str!(128) ~
    lead_in: be_u64 ~
    bytes: take!(259) ~ // TODO: last (7 + 258 * 8) bits must be 0
    num_tracks: be_u8 ~
    tracks: count!(cue_sheet_track, num_tracks as usize),
    || {
      let is_cd = ((bytes[0] >> 7) & 0b01) == 1;

      BlockData::CueSheet(CueSheet {
        media_catalog_number: media_catalog_number,
        lead_in: lead_in,
        is_cd: is_cd,
        tracks: tracks,
      })
    }
  )
);

named!(cue_sheet_track <&[u8], CueSheetTrack>,
  chain!(
    offset: be_u64 ~
    number: be_u8 ~
    isrc: take_str!(12) ~
    bytes: take!(14) ~ // TODO: last (6 + 13 * 8) bits must be 0
    num_indices: be_u8 ~
    indices: cond!(
      num_indices != 0,
      count!(cue_sheet_track_index, num_indices as usize)
    ),
    || {
      let isnt_audio      = ((bytes[0] >> 7) & 0b01) == 1;
      let is_pre_emphasis = ((bytes[0] >> 6) & 0b01) == 1;

      CueSheetTrack {
        offset: offset,
        number: number,
        isrc: isrc,
        isnt_audio: isnt_audio,
        is_pre_emphasis: is_pre_emphasis,
        indices: indices.unwrap_or(Vec::new()),
      }
    }
  )
);

named!(cue_sheet_track_index <&[u8], CueSheetTrackIndex>,
  chain!(
    offset: be_u64 ~
    number: be_u8 ~
    skip_bytes!(3),
    || {
      CueSheetTrackIndex {
        offset: offset,
        number: number,
      }
    }
  )
);

named!(picture <&[u8], BlockData>,
  chain!(
    picture_type_num: be_u32 ~
    mime_type_length:  be_u32 ~
    mime_type: take_str!(mime_type_length) ~
    description_length: be_u32 ~
    description: take_str!(description_length) ~
    width: be_u32 ~
    height: be_u32 ~
    depth: be_u32 ~
    colors: be_u32 ~
    data_length: be_u32 ~
    data: take!(data_length),
    || {
      let picture_type = match picture_type_num {
        0  => PictureType::Other,
        1  => PictureType::FileIconStandard,
        2  => PictureType::FileIcon,
        3  => PictureType::FrontCover,
        4  => PictureType::BackCover,
        5  => PictureType::LeafletPage,
        6  => PictureType::Media,
        7  => PictureType::LeadArtist,
        8  => PictureType::Artist,
        9  => PictureType::Conductor,
        10 => PictureType::Band,
        11 => PictureType::Composer,
        12 => PictureType::Lyricist,
        13 => PictureType::RecordingLocation,
        14 => PictureType::DuringRecording,
        15 => PictureType::DuringPerformace,
        16 => PictureType::VideoScreenCapture,
        17 => PictureType::Fish,
        18 => PictureType::Illustration,
        19 => PictureType::BandLogoType,
        20 => PictureType::PublisherLogoType,
        _  => PictureType::Other,
      };

      BlockData::Picture(Picture {
        picture_type: picture_type,
        mime_type: mime_type,
        description: description,
        width: width,
        height: height,
        depth: depth,
        colors: colors,
        data: data,
      })
    }
  )
);

// As of FLAC v1.3.1, there is support for up to 127 different metadata
// `Block`s but actually 7 that are implemented. When the `Block` type isn't
// recognised, this block gets skipped over with this parser.
fn unknown(input: &[u8], length: u32) -> IResult<&[u8], BlockData> {
  map!(input, take!(length), BlockData::Unknown)
}

named!(pub header <&[u8], (bool, u8, u32)>,
  chain!(
    block_byte: be_u8 ~
    length: map!(take!(3), to_u32),
    || {
      let is_last    = (block_byte >> 7) == 1;
      let block_type = block_byte & 0b01111111;

      (is_last, block_type, length)
    }
  )
);

fn block_data(input: &[u8], block_type: u8, length: u32)
              -> IResult<&[u8], BlockData> {
  match block_type {
    0       => stream_info(input),
    1       => padding(input, length),
    2       => application(input, length),
    3       => seek_table(input, length),
    4       => vorbis_comment(input),
    5       => cue_sheet(input),
    6       => picture(input),
    7...126 => unknown(input, length),
    _       => IResult::Error(Err::Position(ErrorCode::Alt as u32, input)),
  }
}

named!(block <&[u8], Block>,
  chain!(
    block_header: header ~
    data: apply!(block_data, block_header.1, block_header.2),
    || {
      Block {
        is_last: block_header.0,
        length: block_header.2,
        data: data
      }
    }
  )
);

/// Parses one or more metadata `Block`s
///
/// The first block should always be `StreamInfo` since that is the only
/// required `Block`. At the moment `many_blocks` doesn't check that
/// requirement.
pub fn many_blocks(input: &[u8]) -> IResult<&[u8], Vec<Block>> {
  let mut is_last   = false;
  let mut blocks    = Vec::new();
  let mut start     = 0;
  let mut remaining = input.len();

  while !is_last {
    match block(&input[start..]) {
      IResult::Done(i, block) => {
        let result_len = i.len();

        if result_len == input[start..].len() {
          break;
        }

        start    += remaining - result_len;
        remaining = result_len;
        is_last   = block.is_last;

        blocks.push(block);
      }
      _                       => break,
    }
  }

  if blocks.len() == 0 {
    IResult::Error(Err::Position(ErrorCode::Many1 as u32, input))
  } else if is_last {
    IResult::Done(&input[start..], blocks)
  } else {
    IResult::Incomplete(Needed::Unknown)
  }
}

#[cfg(test)]
mod tests {
  use super::*;
  use metadata::{
    BlockData,
    StreamInfo,
  };
  use nom::IResult;

  #[test]
  fn test_header() {
    let inputs = [ [0x80, 0x00, 0x00, 0x22]
                 , [0x01, 0x00, 0x04, 0x00]
                 , [0x84, 0x00, 0x00, 0xf8]
                 ];

    assert!(header(&inputs[0]) == IResult::Done(&[], (true, 0, 34)),
            "Header Test #1");
    assert!(header(&inputs[1]) == IResult::Done(&[], (false, 1, 1024)),
            "Header Test #2");
    assert!(header(&inputs[2]) == IResult::Done(&[], (true, 4, 248)),
            "Header Test #3");
  }

  #[test]
  fn test_stream_info() {
    let input   = [ 0x12, 0x00, 0x12, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x10
                  , 0x01, 0xf4, 0x02, 0x70, 0x00, 0x01, 0x38, 0x80, 0xa0, 0x42
                  , 0x23, 0x7c, 0x54, 0x93, 0xfd, 0xb9, 0x65, 0x6b, 0x94, 0xa8
                  , 0x36, 0x08, 0xd1, 0x1a
                  ];

    let md5_sum = [ 0xa0, 0x42, 0x23, 0x7c, 0x54, 0x93, 0xfd, 0xb9, 0x65, 0x6b
                  , 0x94, 0xa8, 0x36, 0x08, 0xd1, 0x1a
                  ];

    let result = BlockData::StreamInfo(StreamInfo {
      min_block_size: 4608,
      max_block_size: 4608,
      min_frame_size: 14,
      max_frame_size: 16,
      sample_rate: 8000,
      channels: 2,
      bits_per_sample: 8,
      total_samples: 80000,
      md5_sum: &md5_sum,
    });

    assert_eq!(stream_info(&input), IResult::Done(&[][..], result));
  }
}
<|endoftext|>