<commit_before>use super::error::{ErrorKind, Result, ResultExt};
use super::header;
use super::util::streamfork::{fork, DataStream, TargetList};
use super::util::tcpconnection;
use super::{Message, Topic};
use crate::util::FAILED_TO_LOCK;
use log::error;
use std;
use std::collections::HashMap;
use std::net::{TcpListener, ToSocketAddrs};
use std::sync::{Arc, Mutex};
use std::thread;

pub struct Publisher {
    subscriptions: DataStream,
    pub port: u16,
    pub topic: Topic,
    last_message: Arc<Mutex<Arc<Vec<u8>>>>,
    queue_size: usize,
    _raii: tcpconnection::Raii,
}

fn read_request<T: Message, U: std::io::Read>(mut stream: &mut U, topic: &str) -> Result<()> {
    let fields = header::decode(&mut stream)?;
    header::match_field(&fields, "md5sum", &T::md5sum())?;
    header::match_field(&fields, "type", &T::msg_type())?;
    header::match_field(&fields, "topic", topic)?;
    if fields.get("callerid").is_none() {
        bail!(ErrorKind::HeaderMissingField("callerid".into()));
    }
    Ok(())
}

fn write_response<T: Message, U: std::io::Write>(mut stream: &mut U) -> Result<()> {
    let mut fields = HashMap::<String, String>::new();
    fields.insert(String::from("md5sum"), T::md5sum());
    fields.insert(String::from("type"), T::msg_type());
    header::encode(&mut stream, &fields)?;
    Ok(())
}

fn exchange_headers<T, U>(mut stream: &mut U, topic: &str) -> Result<()>
where
    T: Message,
    U: std::io::Write + std::io::Read,
{
    read_request::<T, U>(&mut stream, topic)?;
    write_response::<T, U>(&mut stream)
}

fn listen_for_subscribers<T, U, V>(
    topic: &str,
    listener: V,
    targets: &TargetList<U>,
    last_message: &Mutex<Arc<Vec<u8>>>,
) where
    T: Message,
    U: std::io::Read + std::io::Write + Send,
    V: Iterator<Item = U>,
{
    // This listener stream never breaks by itself since it's a TcpListener
    for mut stream in listener {
        let result = exchange_headers::<T, _>(&mut stream, topic)
            .chain_err(|| ErrorKind::TopicConnectionFail(topic.into()));
        if let Err(err) = result {
            let info = err
                .iter()
                .map(|v| format!("{}", v))
                .collect::<Vec<_>>()
                .join("\nCaused by:");
            error!("{}", info);
            continue;
        }

        if let Err(err) = stream.write_all(&last_message.lock().expect(FAILED_TO_LOCK)) {
            error!("{}", err);
            continue;
        }

        if targets.add(stream).is_err() {
            // The TCP listener gets shut down when streamfork's thread deallocates.
            // This happens only when all the corresponding publisher streams get deallocated,
            // causing streamfork's data channel to shut down
            break;
        }
    }
}

impl Publisher {
    pub fn new<T, U>(address: U, topic: &str, queue_size: usize) -> Result<Publisher>
    where
        T: Message,
        U: ToSocketAddrs,
    {
        let listener = TcpListener::bind(address)?;
        let socket_address = listener.local_addr()?;
        let (raii, listener) = tcpconnection::iterate(listener, format!("topic '{}'", topic));
        Ok(Publisher::wrap_stream::<T, _, _>(
            topic,
            raii,
            listener,
            socket_address.port(),
            queue_size,
        ))
    }

    fn wrap_stream<T, U, V>(
        topic: &str,
        raii: tcpconnection::Raii,
        listener: V,
        port: u16,
        queue_size: usize,
    ) -> Publisher
    where
        T: Message,
        U: std::io::Read + std::io::Write + Send + 'static,
        V: Iterator<Item = U> + Send + 'static,
    {
        let (targets, data) = fork(queue_size);
        let topic_name = String::from(topic);
        let last_message = Arc::new(Mutex::new(Arc::new(Vec::new())));
        let last_msg_for_thread = Arc::clone(&last_message);
        thread::spawn(move || {
            listen_for_subscribers::<T, _, _>(&topic_name, listener, &targets, &last_msg_for_thread)
        });
        let topic = Topic {
            name: String::from(topic),
            msg_type: T::msg_type(),
        };
        Publisher {
            subscriptions: data,
            port,
            topic,
            last_message,
            queue_size,
            _raii: raii,
        }
    }

    pub fn stream<T: Message>(&self, queue_size: usize) -> Result<PublisherStream<T>> {
        let mut stream = PublisherStream::new(self)?;
        stream.set_queue_size_max(queue_size);
        Ok(stream)
    }

    pub fn get_topic(&self) -> &Topic {
        &self.topic
    }
}

// TODO: publisher should only be removed from master API once the publisher and all
// publisher streams are gone. This should be done with a RAII Arc, residing next todo
// the datastream. So maybe replace DataStream with a wrapper that holds that Arc too

#[derive(Clone)]
pub struct PublisherStream<T: Message> {
    stream: DataStream,
    last_message: Arc<Mutex<Arc<Vec<u8>>>>,
    datatype: std::marker::PhantomData<T>,
    latching: bool,
}

impl<T: Message> PublisherStream<T> {
    fn new(publisher: &Publisher) -> Result<PublisherStream<T>> {
        let msg_type = T::msg_type();
        if publisher.topic.msg_type != msg_type {
            bail!(ErrorKind::MessageTypeMismatch(
                publisher.topic.msg_type.clone(),
                msg_type,
            ));
        }
        let mut stream = PublisherStream {
            stream: publisher.subscriptions.clone(),
            datatype: std::marker::PhantomData,
            last_message: Arc::clone(&publisher.last_message),
            latching: false,
        };
        stream.set_queue_size_max(publisher.queue_size);
        Ok(stream)
    }

    #[inline]
    pub fn set_latching(&mut self, latching: bool) {
        self.latching = latching;
    }

    #[inline]
    pub fn set_queue_size(&mut self, queue_size: usize) {
        self.stream.set_queue_size(queue_size);
    }

    #[inline]
    pub fn set_queue_size_max(&mut self, queue_size: usize) {
        self.stream.set_queue_size_max(queue_size);
    }

    pub fn send(&self, message: &T) -> Result<()> {
        let bytes = Arc::new(message.encode_vec()?);

        if self.latching {
            *self.last_message.lock().expect(FAILED_TO_LOCK) = Arc::clone(&bytes);
        }

        // Subscriptions can only be closed from the Publisher side
        // There is no way for the streamfork thread to fail by itself
        self.stream.send(bytes).expect("Connected thread died");
        Ok(())
    }
}
<commit_msg>Allow wildcard topic headers<commit_after>use super::error::{ErrorKind, Result, ResultExt};
use super::header;
use super::util::streamfork::{fork, DataStream, TargetList};
use super::util::tcpconnection;
use super::{Message, Topic};
use crate::util::FAILED_TO_LOCK;
use log::error;
use std;
use std::collections::HashMap;
use std::net::{TcpListener, ToSocketAddrs};
use std::sync::{Arc, Mutex};
use std::thread;

pub struct Publisher {
    subscriptions: DataStream,
    pub port: u16,
    pub topic: Topic,
    last_message: Arc<Mutex<Arc<Vec<u8>>>>,
    queue_size: usize,
    _raii: tcpconnection::Raii,
}

fn match_concrete_headers<T: Message>(fields: &HashMap<String, String>, topic: &str) -> Result<()> {
    header::match_field(fields, "md5sum", &T::md5sum())?;
    header::match_field(fields, "type", &T::msg_type())?;
    header::match_field(fields, "topic", topic)?;
    Ok(())
}

fn match_wildcard_headers(fields: &HashMap<String, String>, topic: &str) -> Result<()> {
    header::match_field(fields, "md5sum", "*")?;
    header::match_field(fields, "type", "*")?;
    header::match_field(fields, "topic", topic)?;
    Ok(())
}

fn read_request<T: Message, U: std::io::Read>(mut stream: &mut U, topic: &str) -> Result<()> {
    let fields = header::decode(&mut stream)?;
    if let Err(err) = match_concrete_headers::<T>(&fields, topic) {
        match_wildcard_headers(&fields, topic).map_err(|_| err)?;
    }
    if fields.get("callerid").is_none() {
        bail!(ErrorKind::HeaderMissingField("callerid".into()));
    }
    Ok(())
}

fn write_response<T: Message, U: std::io::Write>(mut stream: &mut U) -> Result<()> {
    let mut fields = HashMap::<String, String>::new();
    fields.insert(String::from("md5sum"), T::md5sum());
    fields.insert(String::from("type"), T::msg_type());
    header::encode(&mut stream, &fields)?;
    Ok(())
}

fn exchange_headers<T, U>(mut stream: &mut U, topic: &str) -> Result<()>
where
    T: Message,
    U: std::io::Write + std::io::Read,
{
    read_request::<T, U>(&mut stream, topic)?;
    write_response::<T, U>(&mut stream)
}

fn listen_for_subscribers<T, U, V>(
    topic: &str,
    listener: V,
    targets: &TargetList<U>,
    last_message: &Mutex<Arc<Vec<u8>>>,
) where
    T: Message,
    U: std::io::Read + std::io::Write + Send,
    V: Iterator<Item = U>,
{
    // This listener stream never breaks by itself since it's a TcpListener
    for mut stream in listener {
        let result = exchange_headers::<T, _>(&mut stream, topic)
            .chain_err(|| ErrorKind::TopicConnectionFail(topic.into()));
        if let Err(err) = result {
            let info = err
                .iter()
                .map(|v| format!("{}", v))
                .collect::<Vec<_>>()
                .join("\nCaused by:");
            error!("{}", info);
            continue;
        }

        if let Err(err) = stream.write_all(&last_message.lock().expect(FAILED_TO_LOCK)) {
            error!("{}", err);
            continue;
        }

        if targets.add(stream).is_err() {
            // The TCP listener gets shut down when streamfork's thread deallocates.
            // This happens only when all the corresponding publisher streams get deallocated,
            // causing streamfork's data channel to shut down
            break;
        }
    }
}

impl Publisher {
    pub fn new<T, U>(address: U, topic: &str, queue_size: usize) -> Result<Publisher>
    where
        T: Message,
        U: ToSocketAddrs,
    {
        let listener = TcpListener::bind(address)?;
        let socket_address = listener.local_addr()?;
        let (raii, listener) = tcpconnection::iterate(listener, format!("topic '{}'", topic));
        Ok(Publisher::wrap_stream::<T, _, _>(
            topic,
            raii,
            listener,
            socket_address.port(),
            queue_size,
        ))
    }

    fn wrap_stream<T, U, V>(
        topic: &str,
        raii: tcpconnection::Raii,
        listener: V,
        port: u16,
        queue_size: usize,
    ) -> Publisher
    where
        T: Message,
        U: std::io::Read + std::io::Write + Send + 'static,
        V: Iterator<Item = U> + Send + 'static,
    {
        let (targets, data) = fork(queue_size);
        let topic_name = String::from(topic);
        let last_message = Arc::new(Mutex::new(Arc::new(Vec::new())));
        let last_msg_for_thread = Arc::clone(&last_message);
        thread::spawn(move || {
            listen_for_subscribers::<T, _, _>(&topic_name, listener, &targets, &last_msg_for_thread)
        });
        let topic = Topic {
            name: String::from(topic),
            msg_type: T::msg_type(),
        };
        Publisher {
            subscriptions: data,
            port,
            topic,
            last_message,
            queue_size,
            _raii: raii,
        }
    }

    pub fn stream<T: Message>(&self, queue_size: usize) -> Result<PublisherStream<T>> {
        let mut stream = PublisherStream::new(self)?;
        stream.set_queue_size_max(queue_size);
        Ok(stream)
    }

    pub fn get_topic(&self) -> &Topic {
        &self.topic
    }
}

// TODO: publisher should only be removed from master API once the publisher and all
// publisher streams are gone. This should be done with a RAII Arc, residing next todo
// the datastream. So maybe replace DataStream with a wrapper that holds that Arc too

#[derive(Clone)]
pub struct PublisherStream<T: Message> {
    stream: DataStream,
    last_message: Arc<Mutex<Arc<Vec<u8>>>>,
    datatype: std::marker::PhantomData<T>,
    latching: bool,
}

impl<T: Message> PublisherStream<T> {
    fn new(publisher: &Publisher) -> Result<PublisherStream<T>> {
        let msg_type = T::msg_type();
        if publisher.topic.msg_type != msg_type {
            bail!(ErrorKind::MessageTypeMismatch(
                publisher.topic.msg_type.clone(),
                msg_type,
            ));
        }
        let mut stream = PublisherStream {
            stream: publisher.subscriptions.clone(),
            datatype: std::marker::PhantomData,
            last_message: Arc::clone(&publisher.last_message),
            latching: false,
        };
        stream.set_queue_size_max(publisher.queue_size);
        Ok(stream)
    }

    #[inline]
    pub fn set_latching(&mut self, latching: bool) {
        self.latching = latching;
    }

    #[inline]
    pub fn set_queue_size(&mut self, queue_size: usize) {
        self.stream.set_queue_size(queue_size);
    }

    #[inline]
    pub fn set_queue_size_max(&mut self, queue_size: usize) {
        self.stream.set_queue_size_max(queue_size);
    }

    pub fn send(&self, message: &T) -> Result<()> {
        let bytes = Arc::new(message.encode_vec()?);

        if self.latching {
            *self.last_message.lock().expect(FAILED_TO_LOCK) = Arc::clone(&bytes);
        }

        // Subscriptions can only be closed from the Publisher side
        // There is no way for the streamfork thread to fail by itself
        self.stream.send(bytes).expect("Connected thread died");
        Ok(())
    }
}
<|endoftext|>


<commit_before>// Type Names for Debug Info.

use crate::common::CodegenCx;
use rustc::hir::def_id::DefId;
use rustc::ty::subst::SubstsRef;
use rustc::ty::{self, Ty};
use rustc_codegen_ssa::traits::*;
use rustc_data_structures::fx::FxHashSet;

use rustc::hir;

// Compute the name of the type as it should be stored in debuginfo. Does not do
// any caching, i.e., calling the function twice with the same type will also do
// the work twice. The `qualified` parameter only affects the first level of the
// type name, further levels (i.e., type parameters) are always fully qualified.
pub fn compute_debuginfo_type_name<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>,
                                             t: Ty<'tcx>,
                                             qualified: bool)
                                             -> String {
    let mut result = String::with_capacity(64);
    let mut visited = FxHashSet::default();
    push_debuginfo_type_name(cx, t, qualified, &mut result, &mut visited);
    result
}

// Pushes the name of the type as it should be stored in debuginfo on the
// `output` String. See also compute_debuginfo_type_name().
pub fn push_debuginfo_type_name<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>,
                                          t: Ty<'tcx>,
                                          qualified: bool,
                                          output: &mut String,
                                          visited: &mut FxHashSet<Ty<'tcx>>) {

    // We've encountered a weird 'recursive type'
    // Currently, the only way to generate such a type
    // is by using 'impl trait':
    //
    // fn foo() -> impl Copy { foo }
    //
    // There's not really a sensible name we can generate,
    // since we don't include 'impl trait' types (e.g. ty::Opaque)
    // in the output
    //
    // Since we need to generate *something*, we just
    // use a dummy string that should make it clear
    // that something unusual is going on
    if visited.insert(t) {
        output.push_str("<recursive_type>");
        return;
    }

    // When targeting MSVC, emit C++ style type names for compatibility with
    // .natvis visualizers (and perhaps other existing native debuggers?)
    let cpp_like_names = cx.sess().target.target.options.is_like_msvc;

    match t.sty {
        ty::Bool => output.push_str("bool"),
        ty::Char => output.push_str("char"),
        ty::Str => output.push_str("str"),
        ty::Never => output.push_str("!"),
        ty::Int(int_ty) => output.push_str(int_ty.ty_to_string()),
        ty::Uint(uint_ty) => output.push_str(uint_ty.ty_to_string()),
        ty::Float(float_ty) => output.push_str(float_ty.ty_to_string()),
        ty::Foreign(def_id) => push_item_name(cx, def_id, qualified, output),
        ty::Adt(def, substs) => {
            push_item_name(cx, def.did, qualified, output);
            push_type_params(cx, substs, output, visited);
        },
        ty::Tuple(component_types) => {
            output.push('(');
            for &component_type in component_types {
                push_debuginfo_type_name(cx, component_type, true, output, visited);
                output.push_str(", ");
            }
            if !component_types.is_empty() {
                output.pop();
                output.pop();
            }
            output.push(')');
        },
        ty::RawPtr(ty::TypeAndMut { ty: inner_type, mutbl } ) => {
            if !cpp_like_names {
                output.push('*');
            }
            match mutbl {
                hir::MutImmutable => output.push_str("const "),
                hir::MutMutable => output.push_str("mut "),
            }

            push_debuginfo_type_name(cx, inner_type, true, output, visited);

            if cpp_like_names {
                output.push('*');
            }
        },
        ty::Ref(_, inner_type, mutbl) => {
            if !cpp_like_names {
                output.push('&');
            }
            if mutbl == hir::MutMutable {
                output.push_str("mut ");
            }

            push_debuginfo_type_name(cx, inner_type, true, output, visited);

            if cpp_like_names {
                output.push('*');
            }
        },
        ty::Array(inner_type, len) => {
            output.push('[');
            push_debuginfo_type_name(cx, inner_type, true, output, visited);
            output.push_str(&format!("; {}", len.unwrap_usize(cx.tcx)));
            output.push(']');
        },
        ty::Slice(inner_type) => {
            if cpp_like_names {
                output.push_str("slice<");
            } else {
                output.push('[');
            }

            push_debuginfo_type_name(cx, inner_type, true, output, visited);

            if cpp_like_names {
                output.push('>');
            } else {
                output.push(']');
            }
        },
        ty::Dynamic(ref trait_data, ..) => {
            if let Some(principal) = trait_data.principal() {
                let principal = cx.tcx.normalize_erasing_late_bound_regions(
                    ty::ParamEnv::reveal_all(),
                    &principal,
                );
                push_item_name(cx, principal.def_id, false, output);
                push_type_params(cx, principal.substs, output, visited);
            } else {
                output.push_str("dyn '_");
            }
        },
        ty::FnDef(..) | ty::FnPtr(_) => {
            let sig = t.fn_sig(cx.tcx);
            if sig.unsafety() == hir::Unsafety::Unsafe {
                output.push_str("unsafe ");
            }

            let abi = sig.abi();
            if abi != crate::abi::Abi::Rust {
                output.push_str("extern \"");
                output.push_str(abi.name());
                output.push_str("\" ");
            }

            output.push_str("fn(");

            let sig = cx.tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), &sig);
            if !sig.inputs().is_empty() {
                for &parameter_type in sig.inputs() {
                    push_debuginfo_type_name(cx, parameter_type, true, output, visited);
                    output.push_str(", ");
                }
                output.pop();
                output.pop();
            }

            if sig.c_variadic {
                if !sig.inputs().is_empty() {
                    output.push_str(", ...");
                } else {
                    output.push_str("...");
                }
            }

            output.push(')');

            if !sig.output().is_unit() {
                output.push_str(" -> ");
                push_debuginfo_type_name(cx, sig.output(), true, output, visited);
            }
        },
        ty::Closure(..) => {
            output.push_str("closure");
        }
        ty::Generator(..) => {
            output.push_str("generator");
        }
        ty::Error |
        ty::Infer(_) |
        ty::Placeholder(..) |
        ty::UnnormalizedProjection(..) |
        ty::Projection(..) |
        ty::Bound(..) |
        ty::Opaque(..) |
        ty::GeneratorWitness(..) |
        ty::Param(_) => {
            bug!("debuginfo: Trying to create type name for \
                  unexpected type: {:?}", t);
        }
    }

    fn push_item_name(cx: &CodegenCx<'_, '_>,
                      def_id: DefId,
                      qualified: bool,
                      output: &mut String) {
        if qualified {
            output.push_str(&cx.tcx.crate_name(def_id.krate).as_str());
            for path_element in cx.tcx.def_path(def_id).data {
                output.push_str("::");
                output.push_str(&path_element.data.as_interned_str().as_str());
            }
        } else {
            output.push_str(&cx.tcx.item_name(def_id).as_str());
        }
    }

    // Pushes the type parameters in the given `InternalSubsts` to the output string.
    // This ignores region parameters, since they can't reliably be
    // reconstructed for items from non-local crates. For local crates, this
    // would be possible but with inlining and LTO we have to use the least
    // common denominator - otherwise we would run into conflicts.
    fn push_type_params<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>,
                                  substs: SubstsRef<'tcx>,
                                  output: &mut String,
                                  visited: &mut FxHashSet<Ty<'tcx>>) {
        if substs.types().next().is_none() {
            return;
        }

        output.push('<');

        for type_parameter in substs.types() {
            push_debuginfo_type_name(cx, type_parameter, true, output, visited);
            output.push_str(", ");
        }

        output.pop();
        output.pop();

        output.push('>');
    }
}
<commit_msg>Fix inverted panic check<commit_after>// Type Names for Debug Info.

use crate::common::CodegenCx;
use rustc::hir::def_id::DefId;
use rustc::ty::subst::SubstsRef;
use rustc::ty::{self, Ty};
use rustc_codegen_ssa::traits::*;
use rustc_data_structures::fx::FxHashSet;

use rustc::hir;

// Compute the name of the type as it should be stored in debuginfo. Does not do
// any caching, i.e., calling the function twice with the same type will also do
// the work twice. The `qualified` parameter only affects the first level of the
// type name, further levels (i.e., type parameters) are always fully qualified.
pub fn compute_debuginfo_type_name<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>,
                                             t: Ty<'tcx>,
                                             qualified: bool)
                                             -> String {
    let mut result = String::with_capacity(64);
    let mut visited = FxHashSet::default();
    push_debuginfo_type_name(cx, t, qualified, &mut result, &mut visited);
    result
}

// Pushes the name of the type as it should be stored in debuginfo on the
// `output` String. See also compute_debuginfo_type_name().
pub fn push_debuginfo_type_name<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>,
                                          t: Ty<'tcx>,
                                          qualified: bool,
                                          output: &mut String,
                                          visited: &mut FxHashSet<Ty<'tcx>>) {

    // We've encountered a weird 'recursive type'
    // Currently, the only way to generate such a type
    // is by using 'impl trait':
    //
    // fn foo() -> impl Copy { foo }
    //
    // There's not really a sensible name we can generate,
    // since we don't include 'impl trait' types (e.g. ty::Opaque)
    // in the output
    //
    // Since we need to generate *something*, we just
    // use a dummy string that should make it clear
    // that something unusual is going on
    if !visited.insert(t) {
        output.push_str("<recursive_type>");
        return;
    }

    // When targeting MSVC, emit C++ style type names for compatibility with
    // .natvis visualizers (and perhaps other existing native debuggers?)
    let cpp_like_names = cx.sess().target.target.options.is_like_msvc;

    match t.sty {
        ty::Bool => output.push_str("bool"),
        ty::Char => output.push_str("char"),
        ty::Str => output.push_str("str"),
        ty::Never => output.push_str("!"),
        ty::Int(int_ty) => output.push_str(int_ty.ty_to_string()),
        ty::Uint(uint_ty) => output.push_str(uint_ty.ty_to_string()),
        ty::Float(float_ty) => output.push_str(float_ty.ty_to_string()),
        ty::Foreign(def_id) => push_item_name(cx, def_id, qualified, output),
        ty::Adt(def, substs) => {
            push_item_name(cx, def.did, qualified, output);
            push_type_params(cx, substs, output, visited);
        },
        ty::Tuple(component_types) => {
            output.push('(');
            for &component_type in component_types {
                push_debuginfo_type_name(cx, component_type, true, output, visited);
                output.push_str(", ");
            }
            if !component_types.is_empty() {
                output.pop();
                output.pop();
            }
            output.push(')');
        },
        ty::RawPtr(ty::TypeAndMut { ty: inner_type, mutbl } ) => {
            if !cpp_like_names {
                output.push('*');
            }
            match mutbl {
                hir::MutImmutable => output.push_str("const "),
                hir::MutMutable => output.push_str("mut "),
            }

            push_debuginfo_type_name(cx, inner_type, true, output, visited);

            if cpp_like_names {
                output.push('*');
            }
        },
        ty::Ref(_, inner_type, mutbl) => {
            if !cpp_like_names {
                output.push('&');
            }
            if mutbl == hir::MutMutable {
                output.push_str("mut ");
            }

            push_debuginfo_type_name(cx, inner_type, true, output, visited);

            if cpp_like_names {
                output.push('*');
            }
        },
        ty::Array(inner_type, len) => {
            output.push('[');
            push_debuginfo_type_name(cx, inner_type, true, output, visited);
            output.push_str(&format!("; {}", len.unwrap_usize(cx.tcx)));
            output.push(']');
        },
        ty::Slice(inner_type) => {
            if cpp_like_names {
                output.push_str("slice<");
            } else {
                output.push('[');
            }

            push_debuginfo_type_name(cx, inner_type, true, output, visited);

            if cpp_like_names {
                output.push('>');
            } else {
                output.push(']');
            }
        },
        ty::Dynamic(ref trait_data, ..) => {
            if let Some(principal) = trait_data.principal() {
                let principal = cx.tcx.normalize_erasing_late_bound_regions(
                    ty::ParamEnv::reveal_all(),
                    &principal,
                );
                push_item_name(cx, principal.def_id, false, output);
                push_type_params(cx, principal.substs, output, visited);
            } else {
                output.push_str("dyn '_");
            }
        },
        ty::FnDef(..) | ty::FnPtr(_) => {
            let sig = t.fn_sig(cx.tcx);
            if sig.unsafety() == hir::Unsafety::Unsafe {
                output.push_str("unsafe ");
            }

            let abi = sig.abi();
            if abi != crate::abi::Abi::Rust {
                output.push_str("extern \"");
                output.push_str(abi.name());
                output.push_str("\" ");
            }

            output.push_str("fn(");

            let sig = cx.tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), &sig);
            if !sig.inputs().is_empty() {
                for &parameter_type in sig.inputs() {
                    push_debuginfo_type_name(cx, parameter_type, true, output, visited);
                    output.push_str(", ");
                }
                output.pop();
                output.pop();
            }

            if sig.c_variadic {
                if !sig.inputs().is_empty() {
                    output.push_str(", ...");
                } else {
                    output.push_str("...");
                }
            }

            output.push(')');

            if !sig.output().is_unit() {
                output.push_str(" -> ");
                push_debuginfo_type_name(cx, sig.output(), true, output, visited);
            }
        },
        ty::Closure(..) => {
            output.push_str("closure");
        }
        ty::Generator(..) => {
            output.push_str("generator");
        }
        ty::Error |
        ty::Infer(_) |
        ty::Placeholder(..) |
        ty::UnnormalizedProjection(..) |
        ty::Projection(..) |
        ty::Bound(..) |
        ty::Opaque(..) |
        ty::GeneratorWitness(..) |
        ty::Param(_) => {
            bug!("debuginfo: Trying to create type name for \
                  unexpected type: {:?}", t);
        }
    }

    fn push_item_name(cx: &CodegenCx<'_, '_>,
                      def_id: DefId,
                      qualified: bool,
                      output: &mut String) {
        if qualified {
            output.push_str(&cx.tcx.crate_name(def_id.krate).as_str());
            for path_element in cx.tcx.def_path(def_id).data {
                output.push_str("::");
                output.push_str(&path_element.data.as_interned_str().as_str());
            }
        } else {
            output.push_str(&cx.tcx.item_name(def_id).as_str());
        }
    }

    // Pushes the type parameters in the given `InternalSubsts` to the output string.
    // This ignores region parameters, since they can't reliably be
    // reconstructed for items from non-local crates. For local crates, this
    // would be possible but with inlining and LTO we have to use the least
    // common denominator - otherwise we would run into conflicts.
    fn push_type_params<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>,
                                  substs: SubstsRef<'tcx>,
                                  output: &mut String,
                                  visited: &mut FxHashSet<Ty<'tcx>>) {
        if substs.types().next().is_none() {
            return;
        }

        output.push('<');

        for type_parameter in substs.types() {
            push_debuginfo_type_name(cx, type_parameter, true, output, visited);
            output.push_str(", ");
        }

        output.pop();
        output.pop();

        output.push('>');
    }
}
<|endoftext|>


<commit_before>#[derive(Debug, PartialEq)]
pub enum SimilarityModel {
    TF_IDF,
    BM25{k1: f64, b: f64},
}

/// tf(term_frequency) = log(term_frequency) + 1.0
#[inline]
fn tf(term_frequency: u32) -> f64 {
    (term_frequency as f64).log(10.0) + 1.0
}

/// idf(term_docs, total_docs) = log((total_docs + 1.0) / (term_docs + 1.0)) + 1.0
#[inline]
fn idf(term_docs: u64, total_docs: u64) -> f64 {
    ((total_docs as f64 + 1.0) / (term_docs as f64 + 1.0)).log(10.0) + 1.0
}

impl SimilarityModel {
    pub fn score(&self, term_frequency: u32, length: u32, total_tokens: u64, total_docs: u64, total_docs_with_term: u64) -> f64 {
        match *self {
            SimilarityModel::TF_IDF => {
                let tf = tf(term_frequency);
                let idf = idf(total_docs_with_term, total_docs);

                tf * idf
            }
            SimilarityModel::BM25{k1, b} => {
                let tf = tf(term_frequency);
                let idf = idf(total_docs_with_term, total_docs);
                let average_length = (total_tokens as f64) / (total_docs as f64);

                idf * (k1 + 1.0) * (tf / (tf + (k1 * ((1.0 - b) + b * (length as f64) / average_length))))
            }
        }
    }
}
<commit_msg>Tests for similarity models<commit_after>#[derive(Debug, PartialEq)]
pub enum SimilarityModel {
    TF_IDF,
    BM25{k1: f64, b: f64},
}

/// tf(term_frequency) = log(term_frequency) + 1.0
#[inline]
fn tf(term_frequency: u32) -> f64 {
    (term_frequency as f64).log(10.0) + 1.0
}

/// idf(term_docs, total_docs) = log((total_docs + 1.0) / (term_docs + 1.0)) + 1.0
#[inline]
fn idf(term_docs: u64, total_docs: u64) -> f64 {
    ((total_docs as f64 + 1.0) / (term_docs as f64 + 1.0)).log(10.0) + 1.0
}

impl SimilarityModel {
    pub fn score(&self, term_frequency: u32, length: u32, total_tokens: u64, total_docs: u64, total_docs_with_term: u64) -> f64 {
        match *self {
            SimilarityModel::TF_IDF => {
                let tf = tf(term_frequency);
                let idf = idf(total_docs_with_term, total_docs);

                tf * idf
            }
            SimilarityModel::BM25{k1, b} => {
                let tf = tf(term_frequency);
                let idf = idf(total_docs_with_term, total_docs);
                let average_length = (total_tokens as f64) / (total_docs as f64);

                idf * (k1 + 1.0) * (tf / (tf + (k1 * ((1.0 - b) + b * (length as f64) / average_length))))
            }
        }
    }
}

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

    #[test]
    fn test_tf_idf_higher_term_freq_increases_score() {
        let similarity = SimilarityModel::TF_IDF;

        assert!(similarity.score(2, 40, 100, 10, 5) > similarity.score(1, 40, 100, 10, 5));
    }

    #[test]
    fn test_tf_idf_lower_term_docs_increases_score() {
        let similarity = SimilarityModel::TF_IDF;

        assert!(similarity.score(1, 40, 100, 10, 5) > similarity.score(1, 40, 100, 10, 10));
    }

    #[test]
    fn test_tf_idf_field_length_doesnt_affect_score() {
        let similarity = SimilarityModel::TF_IDF;

        assert!(similarity.score(1, 100, 100, 20, 5) == similarity.score(1, 40, 100, 20, 5));
    }

    #[test]
    fn test_tf_idf_total_tokens_doesnt_affect_score() {
        let similarity = SimilarityModel::TF_IDF;

        assert!(similarity.score(1, 40, 1000, 20, 5) == similarity.score(1, 40, 100, 20, 5));
    }

    #[test]
    fn test_bm25_higher_term_freq_increases_score() {
        let similarity = SimilarityModel::BM25 {
            k1: 1.2,
            b: 0.75,
        };

        assert!(similarity.score(2, 40, 100, 10, 5) > similarity.score(1, 40, 100, 10, 5));
    }

    #[test]
    fn test_bm25_lower_term_docs_increases_score() {
        let similarity = SimilarityModel::BM25 {
            k1: 1.2,
            b: 0.75,
        };

        assert!(similarity.score(1, 40, 100, 10, 5) > similarity.score(1, 40, 100, 10, 10));
    }

    #[test]
    fn test_bm25_lower_field_length_increases_score() {
        let similarity = SimilarityModel::BM25 {
            k1: 1.2,
            b: 0.75,
        };

        assert!(similarity.score(1, 40, 100, 20, 5) > similarity.score(1, 100, 100, 20, 5));
    }

    #[test]
    fn test_bm25_higher_total_tokens_increases_score() {
        let similarity = SimilarityModel::BM25 {
            k1: 1.2,
            b: 0.75,
        };

        assert!(similarity.score(1, 40, 1000, 20, 5) > similarity.score(1, 40, 100, 20, 5));
    }
}
<|endoftext|>


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

use std::cmp;

use matches::matches;
use crate::rustc::hir;
use crate::rustc::hir::*;
use crate::rustc::hir::intravisit::FnKind;
use crate::rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
use crate::rustc::{declare_tool_lint, lint_array};
use if_chain::if_chain;
use crate::rustc::ty::TyKind;
use crate::rustc::ty::FnSig;
use crate::rustc::session::config::Config as SessionConfig;
use crate::rustc_target::spec::abi::Abi;
use crate::rustc_target::abi::LayoutOf;
use crate::syntax::ast::NodeId;
use crate::syntax_pos::Span;
use crate::utils::{in_macro, is_copy, is_self_ty, span_lint_and_sugg, snippet};

/// **What it does:** Checks for functions taking arguments by reference, where
/// the argument type is `Copy` and small enough to be more efficient to always
/// pass by value.
///
/// **Why is this bad?** In many calling conventions instances of structs will
/// be passed through registers if they fit into two or less general purpose
/// registers.
///
/// **Known problems:** This lint is target register size dependent, it is
/// limited to 32-bit to try and reduce portability problems between 32 and
/// 64-bit, but if you are compiling for 8 or 16-bit targets then the limit
/// will be different.
///
/// The configuration option `trivial_copy_size_limit` can be set to override
/// this limit for a project.
///
/// This lint attempts to allow passing arguments by reference if a reference
/// to that argument is returned. This is implemented by comparing the lifetime
/// of the argument and return value for equality. However, this can cause
/// false positives in cases involving multiple lifetimes that are bounded by
/// each other.
///
/// **Example:**
/// ```rust
/// fn foo(v: &u32) {
///     assert_eq!(v, 42);
/// }
/// // should be
/// fn foo(v: u32) {
///     assert_eq!(v, 42);
/// }
/// ```
declare_clippy_lint! {
    pub TRIVIALLY_COPY_PASS_BY_REF,
    perf,
    "functions taking small copyable arguments by reference"
}

pub struct TriviallyCopyPassByRef {
    limit: u64,
}

impl<'a, 'tcx> TriviallyCopyPassByRef {
    pub fn new(limit: Option<u64>, target: &SessionConfig) -> Self {
        let limit = limit.unwrap_or_else(|| {
            let bit_width = target.usize_ty.bit_width().expect("usize should have a width") as u64;
            // Cap the calculated bit width at 32-bits to reduce
            // portability problems between 32 and 64-bit targets
            let bit_width = cmp::min(bit_width, 32);
            let byte_width = bit_width / 8;
            // Use a limit of 2 times the register bit width
            byte_width * 2
        });
        Self { limit }
    }

    fn check_trait_method(
        &mut self,
        cx: &LateContext<'_, 'tcx>,
        item: &TraitItemRef
    ) {
        let method_def_id = cx.tcx.hir.local_def_id(item.id.node_id);
        let method_sig = cx.tcx.fn_sig(method_def_id);
        let method_sig = cx.tcx.erase_late_bound_regions(&method_sig);

        let decl = match cx.tcx.hir.fn_decl(item.id.node_id) {
            Some(b) => b,
            None => return,
        };

        self.check_poly_fn(cx, &decl, &method_sig, None);
    }

    fn check_poly_fn(
        &mut self,
        cx: &LateContext<'_, 'tcx>,
        decl: &FnDecl,
        sig: &FnSig<'tcx>,
        span: Option<Span>,
    ) {
        // Use lifetimes to determine if we're returning a reference to the
        // argument. In that case we can't switch to pass-by-value as the
        // argument will not live long enough.
        let output_lts = match sig.output().sty {
            TyKind::Ref(output_lt, _, _) => vec![output_lt],
            TyKind::Adt(_, substs) => substs.regions().collect(),
            _ => vec![],
        };

        for (input, &ty) in decl.inputs.iter().zip(sig.inputs()) {
            // All spans generated from a proc-macro invocation are the same...
            match span {
                Some(s) if  s == input.span => return,
                _ => (),
            }

            if_chain! {
                if let TyKind::Ref(input_lt, ty, Mutability::MutImmutable) = ty.sty;
                if !output_lts.contains(&input_lt);
                if is_copy(cx, ty);
                if let Some(size) = cx.layout_of(ty).ok().map(|l| l.size.bytes());
                if size <= self.limit;
                if let hir::TyKind::Rptr(_, MutTy { ty: ref decl_ty, .. }) = input.node;
                then {
                    let value_type = if is_self_ty(decl_ty) {
                        "self".into()
                    } else {
                        snippet(cx, decl_ty.span, "_").into()
                    };
                    span_lint_and_sugg(
                        cx,
                        TRIVIALLY_COPY_PASS_BY_REF,
                        input.span,
                        "this argument is passed by reference, but would be more efficient if passed by value",
                        "consider passing by value instead",
                        value_type);
                }
            }
        }
    }

    fn check_trait_items(
        &mut self,
        cx: &LateContext<'_, '_>,
        trait_items: &[TraitItemRef]
    ) {
        for item in trait_items {
            match item.kind {
                AssociatedItemKind::Method{..} => self.check_trait_method(cx, item),
                _ => (),
            }
        }
    }
}

impl LintPass for TriviallyCopyPassByRef {
    fn get_lints(&self) -> LintArray {
        lint_array![TRIVIALLY_COPY_PASS_BY_REF]
    }
}

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TriviallyCopyPassByRef {
    fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx Item) {
        if in_macro(item.span) {
            return;
        }
        match item.node {
            ItemKind::Trait(_, _, _, _, ref trait_items) => self.check_trait_items(cx, trait_items),
            _ => (),
        }
    }

    fn check_fn(
        &mut self,
        cx: &LateContext<'a, 'tcx>,
        kind: FnKind<'tcx>,
        decl: &'tcx FnDecl,
        _body: &'tcx Body,
        span: Span,
        node_id: NodeId,
    ) {
        if in_macro(span) {
            return;
        }

        match kind {
            FnKind::ItemFn(.., header, _, attrs) => {
                if header.abi != Abi::Rust {
                    return;
                }
                for a in attrs {
                    if a.meta_item_list().is_some() && a.name() == "proc_macro_derive" {
                        return;
                    }
                }
            },
            FnKind::Method(..) => (),
            _ => return,
        }

        // Exclude non-inherent impls
        if let Some(Node::Item(item)) = cx.tcx.hir.find(cx.tcx.hir.get_parent_node(node_id)) {
            if matches!(item.node, ItemKind::Impl(_, _, _, _, Some(_), _, _) |
                ItemKind::Trait(..))
            {
                return;
            }
        }

        let fn_def_id = cx.tcx.hir.local_def_id(node_id);

        let fn_sig = cx.tcx.fn_sig(fn_def_id);
        let fn_sig = cx.tcx.erase_late_bound_regions(&fn_sig);

        self.check_poly_fn(cx, decl, &fn_sig, Some(span));
    }
}
<commit_msg>Fix nit<commit_after>// Copyright 2014-2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::cmp;

use matches::matches;
use crate::rustc::hir;
use crate::rustc::hir::*;
use crate::rustc::hir::intravisit::FnKind;
use crate::rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
use crate::rustc::{declare_tool_lint, lint_array};
use if_chain::if_chain;
use crate::rustc::ty::TyKind;
use crate::rustc::ty::FnSig;
use crate::rustc::session::config::Config as SessionConfig;
use crate::rustc_target::spec::abi::Abi;
use crate::rustc_target::abi::LayoutOf;
use crate::syntax::ast::NodeId;
use crate::syntax_pos::Span;
use crate::utils::{in_macro, is_copy, is_self_ty, span_lint_and_sugg, snippet};

/// **What it does:** Checks for functions taking arguments by reference, where
/// the argument type is `Copy` and small enough to be more efficient to always
/// pass by value.
///
/// **Why is this bad?** In many calling conventions instances of structs will
/// be passed through registers if they fit into two or less general purpose
/// registers.
///
/// **Known problems:** This lint is target register size dependent, it is
/// limited to 32-bit to try and reduce portability problems between 32 and
/// 64-bit, but if you are compiling for 8 or 16-bit targets then the limit
/// will be different.
///
/// The configuration option `trivial_copy_size_limit` can be set to override
/// this limit for a project.
///
/// This lint attempts to allow passing arguments by reference if a reference
/// to that argument is returned. This is implemented by comparing the lifetime
/// of the argument and return value for equality. However, this can cause
/// false positives in cases involving multiple lifetimes that are bounded by
/// each other.
///
/// **Example:**
/// ```rust
/// fn foo(v: &u32) {
///     assert_eq!(v, 42);
/// }
/// // should be
/// fn foo(v: u32) {
///     assert_eq!(v, 42);
/// }
/// ```
declare_clippy_lint! {
    pub TRIVIALLY_COPY_PASS_BY_REF,
    perf,
    "functions taking small copyable arguments by reference"
}

pub struct TriviallyCopyPassByRef {
    limit: u64,
}

impl<'a, 'tcx> TriviallyCopyPassByRef {
    pub fn new(limit: Option<u64>, target: &SessionConfig) -> Self {
        let limit = limit.unwrap_or_else(|| {
            let bit_width = target.usize_ty.bit_width().expect("usize should have a width") as u64;
            // Cap the calculated bit width at 32-bits to reduce
            // portability problems between 32 and 64-bit targets
            let bit_width = cmp::min(bit_width, 32);
            let byte_width = bit_width / 8;
            // Use a limit of 2 times the register bit width
            byte_width * 2
        });
        Self { limit }
    }

    fn check_trait_method(
        &mut self,
        cx: &LateContext<'_, 'tcx>,
        item: &TraitItemRef
    ) {
        let method_def_id = cx.tcx.hir.local_def_id(item.id.node_id);
        let method_sig = cx.tcx.fn_sig(method_def_id);
        let method_sig = cx.tcx.erase_late_bound_regions(&method_sig);

        let decl = match cx.tcx.hir.fn_decl(item.id.node_id) {
            Some(b) => b,
            None => return,
        };

        self.check_poly_fn(cx, &decl, &method_sig, None);
    }

    fn check_poly_fn(
        &mut self,
        cx: &LateContext<'_, 'tcx>,
        decl: &FnDecl,
        sig: &FnSig<'tcx>,
        span: Option<Span>,
    ) {
        // Use lifetimes to determine if we're returning a reference to the
        // argument. In that case we can't switch to pass-by-value as the
        // argument will not live long enough.
        let output_lts = match sig.output().sty {
            TyKind::Ref(output_lt, _, _) => vec![output_lt],
            TyKind::Adt(_, substs) => substs.regions().collect(),
            _ => vec![],
        };

        for (input, &ty) in decl.inputs.iter().zip(sig.inputs()) {
            // All spans generated from a proc-macro invocation are the same...
            match span {
                Some(s) if s == input.span => return,
                _ => (),
            }

            if_chain! {
                if let TyKind::Ref(input_lt, ty, Mutability::MutImmutable) = ty.sty;
                if !output_lts.contains(&input_lt);
                if is_copy(cx, ty);
                if let Some(size) = cx.layout_of(ty).ok().map(|l| l.size.bytes());
                if size <= self.limit;
                if let hir::TyKind::Rptr(_, MutTy { ty: ref decl_ty, .. }) = input.node;
                then {
                    let value_type = if is_self_ty(decl_ty) {
                        "self".into()
                    } else {
                        snippet(cx, decl_ty.span, "_").into()
                    };
                    span_lint_and_sugg(
                        cx,
                        TRIVIALLY_COPY_PASS_BY_REF,
                        input.span,
                        "this argument is passed by reference, but would be more efficient if passed by value",
                        "consider passing by value instead",
                        value_type);
                }
            }
        }
    }

    fn check_trait_items(
        &mut self,
        cx: &LateContext<'_, '_>,
        trait_items: &[TraitItemRef]
    ) {
        for item in trait_items {
            match item.kind {
                AssociatedItemKind::Method{..} => self.check_trait_method(cx, item),
                _ => (),
            }
        }
    }
}

impl LintPass for TriviallyCopyPassByRef {
    fn get_lints(&self) -> LintArray {
        lint_array![TRIVIALLY_COPY_PASS_BY_REF]
    }
}

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TriviallyCopyPassByRef {
    fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx Item) {
        if in_macro(item.span) {
            return;
        }
        match item.node {
            ItemKind::Trait(_, _, _, _, ref trait_items) => self.check_trait_items(cx, trait_items),
            _ => (),
        }
    }

    fn check_fn(
        &mut self,
        cx: &LateContext<'a, 'tcx>,
        kind: FnKind<'tcx>,
        decl: &'tcx FnDecl,
        _body: &'tcx Body,
        span: Span,
        node_id: NodeId,
    ) {
        if in_macro(span) {
            return;
        }

        match kind {
            FnKind::ItemFn(.., header, _, attrs) => {
                if header.abi != Abi::Rust {
                    return;
                }
                for a in attrs {
                    if a.meta_item_list().is_some() && a.name() == "proc_macro_derive" {
                        return;
                    }
                }
            },
            FnKind::Method(..) => (),
            _ => return,
        }

        // Exclude non-inherent impls
        if let Some(Node::Item(item)) = cx.tcx.hir.find(cx.tcx.hir.get_parent_node(node_id)) {
            if matches!(item.node, ItemKind::Impl(_, _, _, _, Some(_), _, _) |
                ItemKind::Trait(..))
            {
                return;
            }
        }

        let fn_def_id = cx.tcx.hir.local_def_id(node_id);

        let fn_sig = cx.tcx.fn_sig(fn_def_id);
        let fn_sig = cx.tcx.erase_late_bound_regions(&fn_sig);

        self.check_poly_fn(cx, decl, &fn_sig, Some(span));
    }
}
<|endoftext|>


<commit_before>//! Various utilities for working with Cairo image surfaces.

use std::mem;
use std::ops::DerefMut;
use std::slice;

pub mod iterators;
pub mod shared_surface;
pub mod srgb;

// These two are for Cairo's platform-endian 0xaarrggbb pixels

#[cfg(target_endian = "little")]
use rgb::alt::BGRA8;
#[cfg(target_endian = "little")]
pub type CairoARGB = BGRA8;

#[cfg(target_endian = "big")]
use rgb::alt::ARGB8;
#[cfg(target_endian = "big")]
pub type CairoARGB = ARGB8;

/// Analogous to `rgb::FromSlice`, to convert from `[T]` to `[CairoARGB]`
pub trait AsCairoARGB<T: Copy> {
    /// Reinterpret slice as `CairoARGB` pixels.
    fn as_cairo_argb(&self) -> &[CairoARGB];

    /// Reinterpret mutable slice as `CairoARGB` pixels.
    fn as_cairo_argb_mut(&mut self) -> &mut [CairoARGB];
}

impl<T: Copy> AsCairoARGB<T> for [T] {
    fn as_cairo_argb(&self) -> &[CairoARGB] {
        debug_assert_eq!(4, mem::size_of::<CairoARGB>() / mem::size_of::<T>());
        unsafe { slice::from_raw_parts(self.as_ptr() as *const _, self.len() / 4) }
    }

    fn as_cairo_argb_mut(&mut self) -> &mut [CairoARGB] {
        debug_assert_eq!(4, mem::size_of::<CairoARGB>() / mem::size_of::<T>());
        unsafe { slice::from_raw_parts_mut(self.as_ptr() as *mut _, self.len() / 4) }
    }
}

/// Modes which specify how the values of out of bounds pixels are computed.
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub enum EdgeMode {
    /// The nearest inbounds pixel value is returned.
    Duplicate,
    /// The image is extended by taking the color values from the opposite of the image.
    ///
    /// Imagine the image being tiled infinitely, with the original image at the origin.
    Wrap,
    /// Zero RGBA values are returned.
    None,
}

/// Extension methods for `cairo::ImageSurfaceData`.
pub trait ImageSurfaceDataExt: DerefMut<Target = [u8]> {
    /// Sets the pixel at the given coordinates. Assumes the `ARgb32` format.
    #[inline]
    fn set_pixel(&mut self, stride: usize, pixel: Pixel, x: u32, y: u32) {
        let value = pixel.to_u32();

        #[allow(clippy::cast_ptr_alignment)]
        unsafe {
            let p: *mut u8 = &mut self[y as usize * stride + x as usize * 4];
            *(p as *mut u32) = value;
        }
    }
}

/// A pixel consisting of R, G, B and A values.
pub type Pixel = rgb::RGBA8;

pub trait PixelOps {
    fn premultiply(self) -> Self;
    fn unpremultiply(self) -> Self;
    fn diff(&self, other: &Self) -> Self;
    fn to_mask(&self, opacity: u8) -> Self;
    fn to_u32(&self) -> u32;
    fn from_u32(x: u32) -> Self;
}

impl PixelOps for Pixel {
    /// Returns an unpremultiplied value of this pixel.
    ///
    /// For a fully transparent pixel, a transparent black pixel will be returned.
    #[inline]
    fn unpremultiply(self) -> Self {
        if self.a == 0 {
            Self {
                r: 0,
                g: 0,
                b: 0,
                a: 0,
            }
        } else {
            let alpha = f32::from(self.a) / 255.0;
            self.map_rgb(|x| ((f32::from(x) / alpha) + 0.5) as u8)
        }
    }

    /// Returns a premultiplied value of this pixel.
    #[inline]
    fn premultiply(self) -> Self {
        let a = self.a as u32;
        self.map_rgb(|x| (((x as u32) * a + 127) / 255) as u8)
    }

    #[inline]
    fn diff(&self, other: &Pixel) -> Pixel {
        self.iter()
            .zip(other.iter())
            .map(|(l, r)| (l as i32 - r as i32).abs() as u8)
            .collect()
    }

    /// Returns a 'mask' pixel with only the alpha channel
    ///
    /// Assuming, the pixel is linear RGB (not sRGB)
    /// y = luminance
    /// Y = 0.2126 R + 0.7152 G + 0.0722 B
    /// 1.0 opacity = 255
    ///
    /// When Y = 1.0, pixel for mask should be 0xFFFFFFFF
    /// (you get 1.0 luminance from 255 from R, G and B)
    ///
    /// r_mult = 0xFFFFFFFF / (255.0 * 255.0) * .2126 = 14042.45  ~= 14042
    /// g_mult = 0xFFFFFFFF / (255.0 * 255.0) * .7152 = 47239.69  ~= 47240
    /// b_mult = 0xFFFFFFFF / (255.0 * 255.0) * .0722 =  4768.88  ~= 4769
    ///
    /// This allows for the following expected behaviour:
    ///    (we only care about the most significant byte)
    /// if pixel = 0x00FFFFFF, pixel' = 0xFF......
    /// if pixel = 0x00020202, pixel' = 0x02......

    /// if pixel = 0x00000000, pixel' = 0x00......
    #[inline]
    fn to_mask(&self, opacity: u8) -> Self {
        let r = u32::from(self.r);
        let g = u32::from(self.g);
        let b = u32::from(self.b);
        let o = u32::from(opacity);

        Self {
            r: 0,
            g: 0,
            b: 0,
            a: (((r * 14042 + g * 47240 + b * 4769) * o) >> 24) as u8,
        }
    }

    /// Returns the pixel value as a `u32`, in the same format as `cairo::Format::ARgb32`.
    #[inline]
    fn to_u32(&self) -> u32 {
        (u32::from(self.a) << 24)
            | (u32::from(self.r) << 16)
            | (u32::from(self.g) << 8)
            | u32::from(self.b)
    }

    /// Converts a `u32` in the same format as `cairo::Format::ARgb32` into a `Pixel`.
    #[inline]
    fn from_u32(x: u32) -> Self {
        Self {
            r: ((x >> 16) & 0xFF) as u8,
            g: ((x >> 8) & 0xFF) as u8,
            b: (x & 0xFF) as u8,
            a: ((x >> 24) & 0xFF) as u8,
        }
    }
}

impl<'a> ImageSurfaceDataExt for cairo::ImageSurfaceData<'a> {}
impl<'a> ImageSurfaceDataExt for &'a mut [u8] {}

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

    #[test]
    fn pixel_diff() {
        let a = Pixel::new(0x10, 0x20, 0xf0, 0x40);
        assert_eq!(a, a.diff(&Pixel::default()));
        let b = Pixel::new(0x50, 0xff, 0x20, 0x10);
        assert_eq!(a.diff(&b), Pixel::new(0x40, 0xdf, 0xd0, 0x30));
    }

    // Floating-point reference implementation
    fn premultiply_float(pixel: Pixel) -> Pixel {
        let alpha = f64::from(pixel.a) / 255.0;
        pixel.map_rgb(|x| ((f64::from(x) * alpha) + 0.5) as u8)
    }

    prop_compose! {
        fn arbitrary_pixel()(a: u8, r: u8, g: u8, b: u8) -> Pixel {
            Pixel { r, g, b, a }
        }
    }

    proptest! {
        #[test]
        fn pixel_premultiply(pixel in arbitrary_pixel()) {
            prop_assert_eq!(pixel.premultiply(), premultiply_float(pixel));
        }

        #[test]
        fn pixel_unpremultiply(pixel in arbitrary_pixel()) {
            let roundtrip = pixel.premultiply().unpremultiply();
            if pixel.a == 0 {
                prop_assert_eq!(roundtrip, Pixel::default());
            } else {
                // roundtrip can't be perfect, the accepted error depends on alpha
                let tolerance = 0xff / pixel.a;
                let diff = roundtrip.diff(&pixel);
                prop_assert!(diff.r <= tolerance, "red component value differs by more than {}: {:?}", tolerance, roundtrip);
                prop_assert!(diff.g <= tolerance, "green component value differs by more than {}: {:?}", tolerance, roundtrip);
                prop_assert!(diff.b <= tolerance, "blue component value differs by more than {}: {:?}", tolerance, roundtrip);

                prop_assert_eq!(pixel.a, roundtrip.a);
            }
       }
    }
}
<commit_msg>Allow names like CairoARGB<commit_after>//! Various utilities for working with Cairo image surfaces.

use std::mem;
use std::ops::DerefMut;
use std::slice;

pub mod iterators;
pub mod shared_surface;
pub mod srgb;

// These two are for Cairo's platform-endian 0xaarrggbb pixels

#[cfg(target_endian = "little")]
use rgb::alt::BGRA8;
#[cfg(target_endian = "little")]
#[allow(clippy::upper_case_acronyms)]
pub type CairoARGB = BGRA8;

#[cfg(target_endian = "big")]
use rgb::alt::ARGB8;
#[cfg(target_endian = "big")]
#[allow(clippy::upper_case_acronyms)]
pub type CairoARGB = ARGB8;

/// Analogous to `rgb::FromSlice`, to convert from `[T]` to `[CairoARGB]`
#[allow(clippy::upper_case_acronyms)]
pub trait AsCairoARGB<T: Copy> {
    /// Reinterpret slice as `CairoARGB` pixels.
    fn as_cairo_argb(&self) -> &[CairoARGB];

    /// Reinterpret mutable slice as `CairoARGB` pixels.
    fn as_cairo_argb_mut(&mut self) -> &mut [CairoARGB];
}

impl<T: Copy> AsCairoARGB<T> for [T] {
    fn as_cairo_argb(&self) -> &[CairoARGB] {
        debug_assert_eq!(4, mem::size_of::<CairoARGB>() / mem::size_of::<T>());
        unsafe { slice::from_raw_parts(self.as_ptr() as *const _, self.len() / 4) }
    }

    fn as_cairo_argb_mut(&mut self) -> &mut [CairoARGB] {
        debug_assert_eq!(4, mem::size_of::<CairoARGB>() / mem::size_of::<T>());
        unsafe { slice::from_raw_parts_mut(self.as_ptr() as *mut _, self.len() / 4) }
    }
}

/// Modes which specify how the values of out of bounds pixels are computed.
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub enum EdgeMode {
    /// The nearest inbounds pixel value is returned.
    Duplicate,
    /// The image is extended by taking the color values from the opposite of the image.
    ///
    /// Imagine the image being tiled infinitely, with the original image at the origin.
    Wrap,
    /// Zero RGBA values are returned.
    None,
}

/// Extension methods for `cairo::ImageSurfaceData`.
pub trait ImageSurfaceDataExt: DerefMut<Target = [u8]> {
    /// Sets the pixel at the given coordinates. Assumes the `ARgb32` format.
    #[inline]
    fn set_pixel(&mut self, stride: usize, pixel: Pixel, x: u32, y: u32) {
        let value = pixel.to_u32();

        #[allow(clippy::cast_ptr_alignment)]
        unsafe {
            let p: *mut u8 = &mut self[y as usize * stride + x as usize * 4];
            *(p as *mut u32) = value;
        }
    }
}

/// A pixel consisting of R, G, B and A values.
pub type Pixel = rgb::RGBA8;

pub trait PixelOps {
    fn premultiply(self) -> Self;
    fn unpremultiply(self) -> Self;
    fn diff(&self, other: &Self) -> Self;
    fn to_mask(&self, opacity: u8) -> Self;
    fn to_u32(&self) -> u32;
    fn from_u32(x: u32) -> Self;
}

impl PixelOps for Pixel {
    /// Returns an unpremultiplied value of this pixel.
    ///
    /// For a fully transparent pixel, a transparent black pixel will be returned.
    #[inline]
    fn unpremultiply(self) -> Self {
        if self.a == 0 {
            Self {
                r: 0,
                g: 0,
                b: 0,
                a: 0,
            }
        } else {
            let alpha = f32::from(self.a) / 255.0;
            self.map_rgb(|x| ((f32::from(x) / alpha) + 0.5) as u8)
        }
    }

    /// Returns a premultiplied value of this pixel.
    #[inline]
    fn premultiply(self) -> Self {
        let a = self.a as u32;
        self.map_rgb(|x| (((x as u32) * a + 127) / 255) as u8)
    }

    #[inline]
    fn diff(&self, other: &Pixel) -> Pixel {
        self.iter()
            .zip(other.iter())
            .map(|(l, r)| (l as i32 - r as i32).abs() as u8)
            .collect()
    }

    /// Returns a 'mask' pixel with only the alpha channel
    ///
    /// Assuming, the pixel is linear RGB (not sRGB)
    /// y = luminance
    /// Y = 0.2126 R + 0.7152 G + 0.0722 B
    /// 1.0 opacity = 255
    ///
    /// When Y = 1.0, pixel for mask should be 0xFFFFFFFF
    /// (you get 1.0 luminance from 255 from R, G and B)
    ///
    /// r_mult = 0xFFFFFFFF / (255.0 * 255.0) * .2126 = 14042.45  ~= 14042
    /// g_mult = 0xFFFFFFFF / (255.0 * 255.0) * .7152 = 47239.69  ~= 47240
    /// b_mult = 0xFFFFFFFF / (255.0 * 255.0) * .0722 =  4768.88  ~= 4769
    ///
    /// This allows for the following expected behaviour:
    ///    (we only care about the most significant byte)
    /// if pixel = 0x00FFFFFF, pixel' = 0xFF......
    /// if pixel = 0x00020202, pixel' = 0x02......

    /// if pixel = 0x00000000, pixel' = 0x00......
    #[inline]
    fn to_mask(&self, opacity: u8) -> Self {
        let r = u32::from(self.r);
        let g = u32::from(self.g);
        let b = u32::from(self.b);
        let o = u32::from(opacity);

        Self {
            r: 0,
            g: 0,
            b: 0,
            a: (((r * 14042 + g * 47240 + b * 4769) * o) >> 24) as u8,
        }
    }

    /// Returns the pixel value as a `u32`, in the same format as `cairo::Format::ARgb32`.
    #[inline]
    fn to_u32(&self) -> u32 {
        (u32::from(self.a) << 24)
            | (u32::from(self.r) << 16)
            | (u32::from(self.g) << 8)
            | u32::from(self.b)
    }

    /// Converts a `u32` in the same format as `cairo::Format::ARgb32` into a `Pixel`.
    #[inline]
    fn from_u32(x: u32) -> Self {
        Self {
            r: ((x >> 16) & 0xFF) as u8,
            g: ((x >> 8) & 0xFF) as u8,
            b: (x & 0xFF) as u8,
            a: ((x >> 24) & 0xFF) as u8,
        }
    }
}

impl<'a> ImageSurfaceDataExt for cairo::ImageSurfaceData<'a> {}
impl<'a> ImageSurfaceDataExt for &'a mut [u8] {}

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

    #[test]
    fn pixel_diff() {
        let a = Pixel::new(0x10, 0x20, 0xf0, 0x40);
        assert_eq!(a, a.diff(&Pixel::default()));
        let b = Pixel::new(0x50, 0xff, 0x20, 0x10);
        assert_eq!(a.diff(&b), Pixel::new(0x40, 0xdf, 0xd0, 0x30));
    }

    // Floating-point reference implementation
    fn premultiply_float(pixel: Pixel) -> Pixel {
        let alpha = f64::from(pixel.a) / 255.0;
        pixel.map_rgb(|x| ((f64::from(x) * alpha) + 0.5) as u8)
    }

    prop_compose! {
        fn arbitrary_pixel()(a: u8, r: u8, g: u8, b: u8) -> Pixel {
            Pixel { r, g, b, a }
        }
    }

    proptest! {
        #[test]
        fn pixel_premultiply(pixel in arbitrary_pixel()) {
            prop_assert_eq!(pixel.premultiply(), premultiply_float(pixel));
        }

        #[test]
        fn pixel_unpremultiply(pixel in arbitrary_pixel()) {
            let roundtrip = pixel.premultiply().unpremultiply();
            if pixel.a == 0 {
                prop_assert_eq!(roundtrip, Pixel::default());
            } else {
                // roundtrip can't be perfect, the accepted error depends on alpha
                let tolerance = 0xff / pixel.a;
                let diff = roundtrip.diff(&pixel);
                prop_assert!(diff.r <= tolerance, "red component value differs by more than {}: {:?}", tolerance, roundtrip);
                prop_assert!(diff.g <= tolerance, "green component value differs by more than {}: {:?}", tolerance, roundtrip);
                prop_assert!(diff.b <= tolerance, "blue component value differs by more than {}: {:?}", tolerance, roundtrip);

                prop_assert_eq!(pixel.a, roundtrip.a);
            }
       }
    }
}
<|endoftext|>


<commit_before>#![doc = include_str!("../README.md")]
#![feature(box_patterns)]
#![warn(missing_docs)]

mod ast;
mod convert;
mod render;
mod ts;

pub use convert::Config;

/// Generate a JavaScript module from a ditto module.
pub fn codegen(config: &Config, module: ditto_ast::Module) -> String {
    render::render_module(convert::convert_module(config, module))
}

/// Generate a JavaScript module from a ditto module, with TypeScript declarations.
#[doc(hidden)]
pub fn codegen_with_dts(config: &Config, module: ditto_ast::Module) -> (String, String) {
    let dts = ts::generate_declarations(config, &module.module_name, &module.exports);
    let js = render::render_module(convert::convert_module(config, module));
    (js, dts)
}

#[cfg(test)]
mod tests {
    use crate as js;
    use ditto_ast as ast;
    use ditto_checker as checker;
    use ditto_cst as cst;

    #[snapshot_test::snapshot_lf(
        input = "golden-tests/javascript/(.*).ditto",
        output = "golden-tests/javascript/${1}.js"
    )]
    fn javascript(input: &str) -> String {
        let cst_module = cst::Module::parse(input).unwrap();
        let everything = mk_everything();
        let (ast_module, _warnings) = checker::check_module(&everything, cst_module).unwrap();
        prettier(&js::codegen(
            &js::Config {
                module_name_to_path: Box::new(module_name_to_path),
                foreign_module_path: "./foreign.js".into(),
            },
            ast_module,
        ))
    }

    #[snapshot_test::snapshot_lf(
        input = "golden-tests/typescript/(.*).ditto",
        output = "golden-tests/typescript/${1}.d.ts"
    )]
    fn typescript(input: &str) -> String {
        let cst_module = cst::Module::parse(input).unwrap();
        let everything = mk_everything();
        let (ast_module, _warnings) = checker::check_module(&everything, cst_module).unwrap();
        prettier(
            &js::codegen_with_dts(
                &js::Config {
                    module_name_to_path: Box::new(module_name_to_path),
                    foreign_module_path: "./foreign.js".into(),
                },
                ast_module,
            )
            .1,
        )
    }

    /// Use prettier to make sure the generated code is valid syntactically.
    fn prettier(text: &str) -> String {
        use std::{
            io::Write,
            process::{Command, Stdio},
        };

        println!("{text}");

        let mut child = Command::new("node")
            // NOTE: node_modules/.bin/prettier is a shell script on windows
            .arg("../../node_modules/prettier/bin-prettier.js")
            .arg("--parser")
            .arg("typescript")
            // NOTE: prettier defaults to `--end-of-line=lf`
            .stdin(Stdio::piped())
            .stdout(Stdio::piped())
            .spawn()
            .unwrap();

        let child_stdin = child.stdin.as_mut().unwrap();
        child_stdin.write_all(text.as_bytes()).unwrap();
        // Close stdin to finish and avoid indefinite blocking
        drop(child_stdin);

        let output = child.wait_with_output().unwrap();
        assert!(output.status.success());
        String::from_utf8(output.stdout).unwrap()
    }

    fn mk_everything() -> checker::Everything {
        let source = r#"
            module Data.Stuff exports (..);
            type Maybe(a) = Just(a) | Nothing;
            type Five = Five;
            five : Int = 5;
            five_string = "five" ;

            id = (a) -> a;
        "#;
        let cst_module = cst::Module::parse(source).unwrap();
        let (ast_module, _warnings) =
            checker::check_module(&checker::Everything::default(), cst_module).unwrap();
        let exports = ast_module.exports;

        checker::Everything {
            packages: std::collections::HashMap::from_iter([(
                ast::package_name!("test-stuff"),
                std::collections::HashMap::from_iter([(
                    ast::module_name!("Data", "Stuff"),
                    exports.clone(),
                )]),
            )]),
            modules: std::collections::HashMap::from_iter([(
                ast::module_name!("Data", "Stuff"),
                exports,
            )]),
        }
    }

    fn module_name_to_path((package_name, module_name): ast::FullyQualifiedModuleName) -> String {
        let module_path = module_name
            .0
            .into_iter()
            .map(|proper_name| proper_name.0)
            .collect::<Vec<_>>()
            .join(".");

        match package_name {
            None => module_path,
            Some(ast::PackageName(pkg)) => format!("{}/{}", pkg, module_path),
        }
    }
}
<commit_msg>Remove debug `println!`<commit_after>#![doc = include_str!("../README.md")]
#![feature(box_patterns)]
#![warn(missing_docs)]

mod ast;
mod convert;
mod render;
mod ts;

pub use convert::Config;

/// Generate a JavaScript module from a ditto module.
pub fn codegen(config: &Config, module: ditto_ast::Module) -> String {
    render::render_module(convert::convert_module(config, module))
}

/// Generate a JavaScript module from a ditto module, with TypeScript declarations.
#[doc(hidden)]
pub fn codegen_with_dts(config: &Config, module: ditto_ast::Module) -> (String, String) {
    let dts = ts::generate_declarations(config, &module.module_name, &module.exports);
    let js = render::render_module(convert::convert_module(config, module));
    (js, dts)
}

#[cfg(test)]
mod tests {
    use crate as js;
    use ditto_ast as ast;
    use ditto_checker as checker;
    use ditto_cst as cst;

    #[snapshot_test::snapshot_lf(
        input = "golden-tests/javascript/(.*).ditto",
        output = "golden-tests/javascript/${1}.js"
    )]
    fn javascript(input: &str) -> String {
        let cst_module = cst::Module::parse(input).unwrap();
        let everything = mk_everything();
        let (ast_module, _warnings) = checker::check_module(&everything, cst_module).unwrap();
        prettier(&js::codegen(
            &js::Config {
                module_name_to_path: Box::new(module_name_to_path),
                foreign_module_path: "./foreign.js".into(),
            },
            ast_module,
        ))
    }

    #[snapshot_test::snapshot_lf(
        input = "golden-tests/typescript/(.*).ditto",
        output = "golden-tests/typescript/${1}.d.ts"
    )]
    fn typescript(input: &str) -> String {
        let cst_module = cst::Module::parse(input).unwrap();
        let everything = mk_everything();
        let (ast_module, _warnings) = checker::check_module(&everything, cst_module).unwrap();
        prettier(
            &js::codegen_with_dts(
                &js::Config {
                    module_name_to_path: Box::new(module_name_to_path),
                    foreign_module_path: "./foreign.js".into(),
                },
                ast_module,
            )
            .1,
        )
    }

    /// Use prettier to make sure the generated code is valid syntactically.
    fn prettier(text: &str) -> String {
        use std::{
            io::Write,
            process::{Command, Stdio},
        };

        let mut child = Command::new("node")
            // NOTE: node_modules/.bin/prettier is a shell script on windows
            .arg("../../node_modules/prettier/bin-prettier.js")
            .arg("--parser")
            .arg("typescript")
            // NOTE: prettier defaults to `--end-of-line=lf`
            .stdin(Stdio::piped())
            .stdout(Stdio::piped())
            .spawn()
            .unwrap();

        let child_stdin = child.stdin.as_mut().unwrap();
        child_stdin.write_all(text.as_bytes()).unwrap();
        // Close stdin to finish and avoid indefinite blocking
        drop(child_stdin);

        let output = child.wait_with_output().unwrap();
        assert!(output.status.success());
        String::from_utf8(output.stdout).unwrap()
    }

    fn mk_everything() -> checker::Everything {
        let source = r#"
            module Data.Stuff exports (..);
            type Maybe(a) = Just(a) | Nothing;
            type Five = Five;
            five : Int = 5;
            five_string = "five" ;

            id = (a) -> a;
        "#;
        let cst_module = cst::Module::parse(source).unwrap();
        let (ast_module, _warnings) =
            checker::check_module(&checker::Everything::default(), cst_module).unwrap();
        let exports = ast_module.exports;

        checker::Everything {
            packages: std::collections::HashMap::from_iter([(
                ast::package_name!("test-stuff"),
                std::collections::HashMap::from_iter([(
                    ast::module_name!("Data", "Stuff"),
                    exports.clone(),
                )]),
            )]),
            modules: std::collections::HashMap::from_iter([(
                ast::module_name!("Data", "Stuff"),
                exports,
            )]),
        }
    }

    fn module_name_to_path((package_name, module_name): ast::FullyQualifiedModuleName) -> String {
        let module_path = module_name
            .0
            .into_iter()
            .map(|proper_name| proper_name.0)
            .collect::<Vec<_>>()
            .join(".");

        match package_name {
            None => module_path,
            Some(ast::PackageName(pkg)) => format!("{}/{}", pkg, module_path),
        }
    }
}
<|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 for the implementation of JSAPI proxy handlers.

#![deny(missing_docs)]

use dom::bindings::conversions::is_dom_proxy;
use dom::bindings::utils::delete_property_by_id;
use js::jsapi::{JSContext, JSPropertyDescriptor, JSObject, JSString};
use js::jsapi::{JS_GetPropertyDescriptorById, JS_NewStringCopyN};
use js::jsapi::{JS_DefinePropertyById6, JS_NewObjectWithGivenProto};
use js::jsapi::{JS_StrictPropertyStub, JSErrNum};
use js::jsapi::{Handle, HandleObject, HandleId, MutableHandle, RootedObject, ObjectOpResult};
use js::jsapi::AutoIdVector;
use js::jsapi::GetObjectProto;
use js::jsval::ObjectValue;
use js::glue::GetProxyExtra;
use js::glue::{SetProxyExtra, GetProxyHandler};
use js::glue::InvokeGetOwnPropertyDescriptor;
use js::{JSPROP_GETTER, JSPROP_ENUMERATE, JSPROP_READONLY};
use js::{JSTrue, JSFalse};

use libc;
use std::mem;
use std::ptr;

static JSPROXYSLOT_EXPANDO: u32 = 0;

/// Invoke the [[GetOwnProperty]] trap (`getOwnPropertyDescriptor`) on `proxy`,
/// with argument `id` and return the result, if it is not `undefined`.
/// Otherwise, walk along the prototype chain to find a property with that
/// name.
pub unsafe extern fn get_property_descriptor(cx: *mut JSContext,
                                             proxy: HandleObject,
                                             id: HandleId,
                                             desc: MutableHandle<JSPropertyDescriptor>)
                                             -> u8 {
    let handler = GetProxyHandler(proxy.get());
    if InvokeGetOwnPropertyDescriptor(handler, cx, proxy, id, desc) == 0 {
        return JSFalse;
    }
    if !desc.get().obj.is_null() {
        return JSTrue;
    }

    let mut proto = RootedObject::new(cx, ptr::null_mut());
    if GetObjectProto(cx, proxy, proto.handle_mut()) == 0 {
        desc.get().obj = ptr::null_mut();
        return JSTrue;
    }

    JS_GetPropertyDescriptorById(cx, proto.handle(), id, desc)
}

/// Defines an expando on the given `proxy`.
pub unsafe extern fn define_property(cx: *mut JSContext, proxy: HandleObject,
                                     id: HandleId, desc: Handle<JSPropertyDescriptor>,
                                     result: *mut ObjectOpResult)
                                     -> u8 {
    //FIXME: Workaround for https://github.com/mozilla/rust/issues/13385
    let setter: *const libc::c_void = mem::transmute(desc.get().setter);
    let setter_stub: *const libc::c_void = mem::transmute(JS_StrictPropertyStub);
    if (desc.get().attrs & JSPROP_GETTER) != 0 && setter == setter_stub {
        (*result).code_ = JSErrNum::JSMSG_GETTER_ONLY as u32;
        return JSTrue;
    }

    let expando = RootedObject::new(cx, ensure_expando_object(cx, proxy));
    JS_DefinePropertyById6(cx, expando.handle(), id, desc, result)
}

/// Deletes an expando off the given `proxy`.
pub unsafe extern fn delete(cx: *mut JSContext, proxy: HandleObject, id: HandleId,
                            bp: *mut ObjectOpResult) -> u8 {
    let expando = RootedObject::new(cx, get_expando_object(proxy));
    if expando.ptr.is_null() {
        (*bp).code_ = 0 /* OkCode */;
        return JSTrue;
    }

    delete_property_by_id(cx, expando.handle(), id, bp)
}

/// Stub for ownPropertyKeys
pub unsafe extern fn own_property_keys(_cx: *mut JSContext,
                                       _proxy: HandleObject,
                                       _props: *mut AutoIdVector) -> u8 {
    // FIXME: implement this
    // https://github.com/servo/servo/issues/6390
    JSTrue
}

/// Controls whether the Extensible bit can be changed
pub unsafe extern fn prevent_extensions(_cx: *mut JSContext,
                                        _proxy: HandleObject,
                                        result: *mut ObjectOpResult) -> u8 {
    (*result).code_ = JSErrNum::JSMSG_CANT_PREVENT_EXTENSIONS as u32;
    return JSTrue;
}

/// Reports whether the object is Extensible
pub unsafe extern fn is_extensible(_cx: *mut JSContext, _proxy: HandleObject,
                                   succeeded: *mut u8) -> u8 {
    *succeeded = JSTrue;
    return JSTrue;
}

/// Returns the stringification of an object with class `name`.
pub fn object_to_string(cx: *mut JSContext, name: &str) -> *mut JSString {
    unsafe {
        let result = format!("[object {}]", name);

        let chars = result.as_ptr() as *const libc::c_char;
        let length = result.len() as libc::size_t;

        let string = JS_NewStringCopyN(cx, chars, length);
        assert!(!string.is_null());
        return string;
    }
}

/// Get the expando object, or null if there is none.
pub fn get_expando_object(obj: HandleObject) -> *mut JSObject {
    unsafe {
        assert!(is_dom_proxy(obj.get()));
        let val = GetProxyExtra(obj.get(), JSPROXYSLOT_EXPANDO);
        if val.is_undefined() {
            ptr::null_mut()
        } else {
            val.to_object()
        }
    }
}

/// Get the expando object, or create it if it doesn't exist yet.
/// Fails on JSAPI failure.
pub fn ensure_expando_object(cx: *mut JSContext, obj: HandleObject)
                             -> *mut JSObject {
    unsafe {
        assert!(is_dom_proxy(obj.get()));
        let mut expando = get_expando_object(obj);
        if expando.is_null() {
            expando = JS_NewObjectWithGivenProto(cx, ptr::null_mut(), HandleObject::null());
            assert!(!expando.is_null());

            SetProxyExtra(obj.get(), JSPROXYSLOT_EXPANDO, ObjectValue(&*expando));
        }
        return expando;
    }
}

/// Set the property descriptor's object to `obj` and set it to enumerable,
/// and writable if `readonly` is true.
pub fn fill_property_descriptor(desc: &mut JSPropertyDescriptor,
                                obj: *mut JSObject, readonly: bool) {
    desc.obj = obj;
    desc.attrs = if readonly { JSPROP_READONLY } else { 0 } | JSPROP_ENUMERATE;
    desc.getter = None;
    desc.setter = None;
}
<commit_msg>Removed unused object_to_string method<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 for the implementation of JSAPI proxy handlers.

#![deny(missing_docs)]

use dom::bindings::conversions::is_dom_proxy;
use dom::bindings::utils::delete_property_by_id;
use js::jsapi::{JSContext, JSPropertyDescriptor, JSObject};
use js::jsapi::{JS_GetPropertyDescriptorById};
use js::jsapi::{JS_DefinePropertyById6, JS_NewObjectWithGivenProto};
use js::jsapi::{JS_StrictPropertyStub, JSErrNum};
use js::jsapi::{Handle, HandleObject, HandleId, MutableHandle, RootedObject, ObjectOpResult};
use js::jsapi::AutoIdVector;
use js::jsapi::GetObjectProto;
use js::jsval::ObjectValue;
use js::glue::GetProxyExtra;
use js::glue::{SetProxyExtra, GetProxyHandler};
use js::glue::InvokeGetOwnPropertyDescriptor;
use js::{JSPROP_GETTER, JSPROP_ENUMERATE, JSPROP_READONLY};
use js::{JSTrue, JSFalse};

use libc;
use std::mem;
use std::ptr;

static JSPROXYSLOT_EXPANDO: u32 = 0;

/// Invoke the [[GetOwnProperty]] trap (`getOwnPropertyDescriptor`) on `proxy`,
/// with argument `id` and return the result, if it is not `undefined`.
/// Otherwise, walk along the prototype chain to find a property with that
/// name.
pub unsafe extern fn get_property_descriptor(cx: *mut JSContext,
                                             proxy: HandleObject,
                                             id: HandleId,
                                             desc: MutableHandle<JSPropertyDescriptor>)
                                             -> u8 {
    let handler = GetProxyHandler(proxy.get());
    if InvokeGetOwnPropertyDescriptor(handler, cx, proxy, id, desc) == 0 {
        return JSFalse;
    }
    if !desc.get().obj.is_null() {
        return JSTrue;
    }

    let mut proto = RootedObject::new(cx, ptr::null_mut());
    if GetObjectProto(cx, proxy, proto.handle_mut()) == 0 {
        desc.get().obj = ptr::null_mut();
        return JSTrue;
    }

    JS_GetPropertyDescriptorById(cx, proto.handle(), id, desc)
}

/// Defines an expando on the given `proxy`.
pub unsafe extern fn define_property(cx: *mut JSContext, proxy: HandleObject,
                                     id: HandleId, desc: Handle<JSPropertyDescriptor>,
                                     result: *mut ObjectOpResult)
                                     -> u8 {
    //FIXME: Workaround for https://github.com/mozilla/rust/issues/13385
    let setter: *const libc::c_void = mem::transmute(desc.get().setter);
    let setter_stub: *const libc::c_void = mem::transmute(JS_StrictPropertyStub);
    if (desc.get().attrs & JSPROP_GETTER) != 0 && setter == setter_stub {
        (*result).code_ = JSErrNum::JSMSG_GETTER_ONLY as u32;
        return JSTrue;
    }

    let expando = RootedObject::new(cx, ensure_expando_object(cx, proxy));
    JS_DefinePropertyById6(cx, expando.handle(), id, desc, result)
}

/// Deletes an expando off the given `proxy`.
pub unsafe extern fn delete(cx: *mut JSContext, proxy: HandleObject, id: HandleId,
                            bp: *mut ObjectOpResult) -> u8 {
    let expando = RootedObject::new(cx, get_expando_object(proxy));
    if expando.ptr.is_null() {
        (*bp).code_ = 0 /* OkCode */;
        return JSTrue;
    }

    delete_property_by_id(cx, expando.handle(), id, bp)
}

/// Stub for ownPropertyKeys
pub unsafe extern fn own_property_keys(_cx: *mut JSContext,
                                       _proxy: HandleObject,
                                       _props: *mut AutoIdVector) -> u8 {
    // FIXME: implement this
    // https://github.com/servo/servo/issues/6390
    JSTrue
}

/// Controls whether the Extensible bit can be changed
pub unsafe extern fn prevent_extensions(_cx: *mut JSContext,
                                        _proxy: HandleObject,
                                        result: *mut ObjectOpResult) -> u8 {
    (*result).code_ = JSErrNum::JSMSG_CANT_PREVENT_EXTENSIONS as u32;
    return JSTrue;
}

/// Reports whether the object is Extensible
pub unsafe extern fn is_extensible(_cx: *mut JSContext, _proxy: HandleObject,
                                   succeeded: *mut u8) -> u8 {
    *succeeded = JSTrue;
    return JSTrue;
}

/// Get the expando object, or null if there is none.
pub fn get_expando_object(obj: HandleObject) -> *mut JSObject {
    unsafe {
        assert!(is_dom_proxy(obj.get()));
        let val = GetProxyExtra(obj.get(), JSPROXYSLOT_EXPANDO);
        if val.is_undefined() {
            ptr::null_mut()
        } else {
            val.to_object()
        }
    }
}

/// Get the expando object, or create it if it doesn't exist yet.
/// Fails on JSAPI failure.
pub fn ensure_expando_object(cx: *mut JSContext, obj: HandleObject)
                             -> *mut JSObject {
    unsafe {
        assert!(is_dom_proxy(obj.get()));
        let mut expando = get_expando_object(obj);
        if expando.is_null() {
            expando = JS_NewObjectWithGivenProto(cx, ptr::null_mut(), HandleObject::null());
            assert!(!expando.is_null());

            SetProxyExtra(obj.get(), JSPROXYSLOT_EXPANDO, ObjectValue(&*expando));
        }
        return expando;
    }
}

/// Set the property descriptor's object to `obj` and set it to enumerable,
/// and writable if `readonly` is true.
pub fn fill_property_descriptor(desc: &mut JSPropertyDescriptor,
                                obj: *mut JSObject, readonly: bool) {
    desc.obj = obj;
    desc.attrs = if readonly { JSPROP_READONLY } else { 0 } | JSPROP_ENUMERATE;
    desc.getter = None;
    desc.setter = None;
}
<|endoftext|>


<commit_before>use id3::Tag;
use chill;
use super::{Index, Indexer};
use walkdir::{DirEntry, WalkDir};
use {DecodedTrack, User, Error, FileBackend, Uri, FileUri, MACHINE_ID};

impl Indexer<FileBackend> for Index {
    fn index(db: &chill::Client, user: &User, backend: &FileBackend) -> Result<(), Error> {

        let mut tracks: Vec<DecodedTrack> = Vec::new();

        for path in &backend.paths {
            for entry in walk_path(&path) {
                if let Ok(tag) = Tag::read_from_path(entry.path()) {
                    if let Some(file_path) = entry.path().to_str() {
                        let uri = Uri::File(FileUri::new(&MACHINE_ID.to_string(), file_path));

                        if let Some(track) = DecodedTrack::from_tag(tag, uri) {
                            tracks.push(track);
                        }
                    }
                }
            }
        }

        Index::take_result(db, &user.db_name(), tracks)?;
        Ok(())
    }
}

fn is_file_type(e: &DirEntry, ext: &str) -> bool {
    let p = e.path();
    p.is_file() && p.extension().map_or(false, |s| s == ext)
}

fn is_music(e: &DirEntry) -> bool {
    is_file_type(e, "mp3") || is_file_type(e, "ogg")
}

fn walk_path(path: &str) -> Vec<DirEntry> {
    WalkDir::new(path)
        .into_iter()
        .filter_map(|e| e.ok())
        .filter(|e| is_music(e))
        .collect()
}
<commit_msg>refactor(lib): fix warning<commit_after>use id3::Tag;
use chill;
use super::{Index, Indexer};
use walkdir::{DirEntry, WalkDir};
use {DecodedTrack, User, Error, FileBackend, Uri, FileUri, MACHINE_ID};

impl Indexer<FileBackend> for Index {
    fn index(db: &chill::Client, user: &User, backend: &FileBackend) -> Result<(), Error> {

        let mut tracks: Vec<DecodedTrack> = Vec::new();

        for path in &backend.paths {
            for entry in walk_path(path) {
                if let Ok(tag) = Tag::read_from_path(entry.path()) {
                    if let Some(file_path) = entry.path().to_str() {
                        let uri = Uri::File(FileUri::new(&MACHINE_ID.to_string(), file_path));

                        if let Some(track) = DecodedTrack::from_tag(tag, uri) {
                            tracks.push(track);
                        }
                    }
                }
            }
        }

        Index::take_result(db, &user.db_name(), tracks)?;
        Ok(())
    }
}

fn is_file_type(e: &DirEntry, ext: &str) -> bool {
    let p = e.path();
    p.is_file() && p.extension().map_or(false, |s| s == ext)
}

fn is_music(e: &DirEntry) -> bool {
    is_file_type(e, "mp3") || is_file_type(e, "ogg")
}

fn walk_path(path: &str) -> Vec<DirEntry> {
    WalkDir::new(path)
        .into_iter()
        .filter_map(|e| e.ok())
        .filter(|e| is_music(e))
        .collect()
}
<|endoftext|>