<commit_before>use super::MessageAction;
use super::InjectMode;
use config::action::ExecCondition;

use handlebars::Template;
use handlebars::Handlebars;
use serde::de::{Deserialize, Deserializer, Error, MapVisitor, Visitor};
use std::collections::BTreeMap;
use super::MESSAGE;

impl Deserialize for MessageAction {
    fn deserialize<D>(deserializer: &mut D) -> Result<MessageAction, D::Error>
        where D: Deserializer
    {
        deserializer.visit_struct("MessageAction", &[], MessageActionVisitor)
    }
}

enum Field {
    Uuid,
    Name,
    Message,
    Values,
    When,
    InjectMode,
}

impl Deserialize for Field {
    fn deserialize<D>(deserializer: &mut D) -> Result<Field, D::Error>
        where D: Deserializer
    {
        struct FieldVisitor;

        impl Visitor for FieldVisitor {
            type Value = Field;

            fn visit_str<E>(&mut self, value: &str) -> Result<Field, E>
                where E: Error
            {
                match value {
                    "name" => Ok(Field::Name),
                    "uuid" => Ok(Field::Uuid),
                    "values" => Ok(Field::Values),
                    "message" => Ok(Field::Message),
                    "when" => Ok(Field::When),
                    "inject_mode" => Ok(Field::InjectMode),
                    _ => Err(Error::syntax(&format!("Unexpected field: {}", value))),
                }
            }
        }

        deserializer.visit(FieldVisitor)
    }
}

struct MessageActionVisitor;

impl MessageActionVisitor {
    fn compile_template<V>(template_string: String, uuid: &str) -> Result<Template, V::Error>
        where V: MapVisitor
    {
        match Template::compile(template_string) {
            Ok(message) => Ok(message),
            Err(error) => {
                Err(Error::syntax(&format!("Invalid handlebars template in 'message' field: \
                                            uuid={}, error={}",
                                           uuid,
                                           error)))
            }
        }
    }
}

impl Visitor for MessageActionVisitor {
    type Value = MessageAction;

    fn visit_map<V>(&mut self, mut visitor: V) -> Result<MessageAction, V::Error>
        where V: MapVisitor
    {
        let mut name: Option<String> = None;
        let mut uuid: Option<String> = None;
        let mut message: Option<String> = None;
        let mut values: Option<BTreeMap<String, String>> = None;
        let mut when: ExecCondition = ExecCondition::new();
        let mut inject_mode = Default::default();

        while let Some(field) = try!(visitor.visit_key()) {
            match field {
                Field::Name => name = Some(try!(visitor.visit_value())),
                Field::Uuid => uuid = Some(try!(visitor.visit_value())),
                Field::Message => message = Some(try!(visitor.visit_value())),
                Field::Values => values = Some(try!(visitor.visit_value())),
                Field::When => when = try!(visitor.visit_value()),
                Field::InjectMode => inject_mode = try!(visitor.visit_value()),
            }
        }

        let uuid = match uuid {
            Some(uuid) => uuid,
            None => return visitor.missing_field("uuid"),
        };

        let message = match message {
            Some(message) => try!(MessageActionVisitor::compile_template::<V>(message, &uuid)),
            None => {
                error!("Missing 'message' field: uuid={}", &uuid);
                return visitor.missing_field("message");
            }
        };

        let mut values = match values {
            Some(values) => {
                let mut registry = Handlebars::new();
                for (key, value) in values.into_iter() {
                    let template = try!(MessageActionVisitor::compile_template::<V>(value, &uuid));
                    registry.register_template(&key, template);
                }
                registry
            }
            None => Handlebars::new(),
        };

        try!(visitor.end());

        values.register_template(MESSAGE, message.clone());

        Ok(MessageAction {
            uuid: uuid,
            message: message,
            name: name,
            values: values,
            when: when,
            inject_mode: inject_mode,
        })
    }
}

impl Deserialize for InjectMode {
    fn deserialize<D>(deserializer: &mut D) -> Result<InjectMode, D::Error>
        where D: Deserializer
    {
        struct FieldVisitor;

        impl Visitor for FieldVisitor {
            type Value = InjectMode;

            fn visit_str<E>(&mut self, value: &str) -> Result<InjectMode, E>
                where E: Error
            {
                match value {
                    "log" => Ok(InjectMode::Log),
                    "loopback" => Ok(InjectMode::Loopback),
                    "forward" => Ok(InjectMode::Forward),
                    _ => Err(Error::syntax(&format!("Unexpected field: {}", value))),
                }
            }
        }

        deserializer.visit(FieldVisitor)
    }
}

#[cfg(test)]
mod test {
    use config::action::message::{MessageActionBuilder, MessageAction, InjectMode};

    use handlebars::Template;
    use serde_json::from_str;

    fn assert_message_action_eq(expected: &MessageAction, actual: &MessageAction) {
        assert_eq!(expected.uuid(), actual.uuid());
        assert_eq!(expected.name(), actual.name());
        assert_eq!(expected.message(), actual.message());
    }

    #[test]
    fn test_given_message_as_a_json_string_when_it_is_deserialized_then_we_get_the_expected_message
        () {
        let text = r#"
        {
          "uuid": "UUID",
          "name": "NAME",
          "message": "message",
          "values": {
            "key1": "value1",
            "key2": "value2"
          }
        }
        "#;

        let message = Template::compile("message".to_owned())
                          .expect("Failed to compile a handlebars template");
        let value1 = Template::compile("value1".to_owned())
                         .expect("Failed to compile a handlebars template");
        let value2 = Template::compile("value2".to_owned())
                         .expect("Failed to compile a handlebars template");
        let expected_message = MessageActionBuilder::new("UUID", message)
                                   .name(Some("NAME"))
                                   .pair("key1", value1)
                                   .pair("key2", value2)
                                   .build();
        let result = from_str::<MessageAction>(text);
        let message = result.expect("Failed to deserialize a valid MessageAction object");
        assert_message_action_eq(&expected_message, &message);
    }

    #[test]
    fn test_given_message_as_a_json_string_when_only_the_required_fields_are_present_then_we_can_deserialize_the_message
        () {
        let text = r#"
        {
          "uuid": "UUID",
          "message": "message"
        }
        "#;

        let message = Template::compile("message".to_owned())
                          .expect("Failed to compile a handlebars template");
        let expected_message = MessageActionBuilder::new("UUID", message).build();
        let result = from_str::<MessageAction>(text);
        let message = result.expect("Failed to deserialize a valid MessageAction object");
        assert_message_action_eq(&expected_message, &message);
    }

    #[test]
    fn test_given_message_as_a_json_string_when_one_of_the_required_fields_are_not_present_then_we_get_error
        () {
        let text = r#"
        {
        }
        "#;

        let result = from_str::<MessageAction>(text);
        let _ = result.err().expect("Successfully deserialized an invalid MessageAction object");
    }

    #[test]
    fn test_given_inject_modes_when_they_are_deserialized_then_we_get_the_right_result() {
        let text = r#"
        ["forward", "log", "loopback", "log"]
        "#;
        let expected = vec![InjectMode::Forward,
                            InjectMode::Log,
                            InjectMode::Loopback,
                            InjectMode::Log];

        let result = from_str::<Vec<InjectMode>>(text);
        println!("{:?}", &result);
        let array = result.expect("Failed to deserialize a valid array of inject modes");
        assert_eq!(&expected, &array);
    }

    #[test]
    fn test_given_invalid_inject_mode_when_it_is_deserialized_then_we_get_the_right_result() {
        let text = r#"
        ["invalid inject mode", "log"]
        "#;

        let result = from_str::<Vec<InjectMode>>(text);
        println!("{:?}", &result);
        let _ = result.err().expect("Successfully deserialized an invalid inject mode");
    }

    #[test]
    fn test_given_message_when_it_contains_inject_mode_then_it_can_be_deserialized() {
        let text = r#"
        {
          "uuid": "UUID",
          "message": "message",
          "inject_mode": "forward"
        }
        "#;

        let message = Template::compile("message".to_owned())
                          .expect("Failed to compile a handlebars template");
        let expected_message = MessageActionBuilder::new("UUID", message)
                                   .inject_mode(InjectMode::Forward)
                                   .build();
        let result = from_str::<MessageAction>(text);
        let message = result.expect("Failed to deserialize a valid MessageAction object");
        assert_message_action_eq(&expected_message, &message);
    }

    #[test]
    fn test_given_message_is_deserialized_when_it_contains_an_unexpected_field_then_an_error_is_returned() {
        let text = r#"{ "unexpected": "UUID" }"#;
        let result = from_str::<MessageAction>(text);
        let _ = result.err().unwrap();
    }
}
<commit_msg>MessageActionVisitor: simplify compile_template()<commit_after>use super::MessageAction;
use super::InjectMode;
use config::action::ExecCondition;

use handlebars::Template;
use handlebars::Handlebars;
use serde::de::{Deserialize, Deserializer, Error, MapVisitor, Visitor};
use std::collections::BTreeMap;
use super::MESSAGE;

impl Deserialize for MessageAction {
    fn deserialize<D>(deserializer: &mut D) -> Result<MessageAction, D::Error>
        where D: Deserializer
    {
        deserializer.visit_struct("MessageAction", &[], MessageActionVisitor)
    }
}

enum Field {
    Uuid,
    Name,
    Message,
    Values,
    When,
    InjectMode,
}

impl Deserialize for Field {
    fn deserialize<D>(deserializer: &mut D) -> Result<Field, D::Error>
        where D: Deserializer
    {
        struct FieldVisitor;

        impl Visitor for FieldVisitor {
            type Value = Field;

            fn visit_str<E>(&mut self, value: &str) -> Result<Field, E>
                where E: Error
            {
                match value {
                    "name" => Ok(Field::Name),
                    "uuid" => Ok(Field::Uuid),
                    "values" => Ok(Field::Values),
                    "message" => Ok(Field::Message),
                    "when" => Ok(Field::When),
                    "inject_mode" => Ok(Field::InjectMode),
                    _ => Err(Error::syntax(&format!("Unexpected field: {}", value))),
                }
            }
        }

        deserializer.visit(FieldVisitor)
    }
}

struct MessageActionVisitor;

impl MessageActionVisitor {
    fn compile_template<V>(template_string: String, uuid: &str) -> Result<Template, V::Error>
        where V: MapVisitor
    {
        Template::compile(template_string).map_err(|error| {
            Error::syntax(&format!("Invalid handlebars template in 'message' field: \
                                        uuid={}, error={}",
                                       uuid,
                                       error))
        })
    }
}

impl Visitor for MessageActionVisitor {
    type Value = MessageAction;

    fn visit_map<V>(&mut self, mut visitor: V) -> Result<MessageAction, V::Error>
        where V: MapVisitor
    {
        let mut name: Option<String> = None;
        let mut uuid: Option<String> = None;
        let mut message: Option<String> = None;
        let mut values: Option<BTreeMap<String, String>> = None;
        let mut when: ExecCondition = ExecCondition::new();
        let mut inject_mode = Default::default();

        while let Some(field) = try!(visitor.visit_key()) {
            match field {
                Field::Name => name = Some(try!(visitor.visit_value())),
                Field::Uuid => uuid = Some(try!(visitor.visit_value())),
                Field::Message => message = Some(try!(visitor.visit_value())),
                Field::Values => values = Some(try!(visitor.visit_value())),
                Field::When => when = try!(visitor.visit_value()),
                Field::InjectMode => inject_mode = try!(visitor.visit_value()),
            }
        }

        let uuid = match uuid {
            Some(uuid) => uuid,
            None => return visitor.missing_field("uuid"),
        };

        let message = match message {
            Some(message) => try!(MessageActionVisitor::compile_template::<V>(message, &uuid)),
            None => {
                error!("Missing 'message' field: uuid={}", &uuid);
                return visitor.missing_field("message");
            }
        };

        let mut values = match values {
            Some(values) => {
                let mut registry = Handlebars::new();
                for (key, value) in values.into_iter() {
                    let template = try!(MessageActionVisitor::compile_template::<V>(value, &uuid));
                    registry.register_template(&key, template);
                }
                registry
            }
            None => Handlebars::new(),
        };

        try!(visitor.end());

        values.register_template(MESSAGE, message.clone());

        Ok(MessageAction {
            uuid: uuid,
            message: message,
            name: name,
            values: values,
            when: when,
            inject_mode: inject_mode,
        })
    }
}

impl Deserialize for InjectMode {
    fn deserialize<D>(deserializer: &mut D) -> Result<InjectMode, D::Error>
        where D: Deserializer
    {
        struct FieldVisitor;

        impl Visitor for FieldVisitor {
            type Value = InjectMode;

            fn visit_str<E>(&mut self, value: &str) -> Result<InjectMode, E>
                where E: Error
            {
                match value {
                    "log" => Ok(InjectMode::Log),
                    "loopback" => Ok(InjectMode::Loopback),
                    "forward" => Ok(InjectMode::Forward),
                    _ => Err(Error::syntax(&format!("Unexpected field: {}", value))),
                }
            }
        }

        deserializer.visit(FieldVisitor)
    }
}

#[cfg(test)]
mod test {
    use config::action::message::{MessageActionBuilder, MessageAction, InjectMode};

    use handlebars::Template;
    use serde_json::from_str;

    fn assert_message_action_eq(expected: &MessageAction, actual: &MessageAction) {
        assert_eq!(expected.uuid(), actual.uuid());
        assert_eq!(expected.name(), actual.name());
        assert_eq!(expected.message(), actual.message());
    }

    #[test]
    fn test_given_message_as_a_json_string_when_it_is_deserialized_then_we_get_the_expected_message
        () {
        let text = r#"
        {
          "uuid": "UUID",
          "name": "NAME",
          "message": "message",
          "values": {
            "key1": "value1",
            "key2": "value2"
          }
        }
        "#;

        let message = Template::compile("message".to_owned())
                          .expect("Failed to compile a handlebars template");
        let value1 = Template::compile("value1".to_owned())
                         .expect("Failed to compile a handlebars template");
        let value2 = Template::compile("value2".to_owned())
                         .expect("Failed to compile a handlebars template");
        let expected_message = MessageActionBuilder::new("UUID", message)
                                   .name(Some("NAME"))
                                   .pair("key1", value1)
                                   .pair("key2", value2)
                                   .build();
        let result = from_str::<MessageAction>(text);
        let message = result.expect("Failed to deserialize a valid MessageAction object");
        assert_message_action_eq(&expected_message, &message);
    }

    #[test]
    fn test_given_message_as_a_json_string_when_only_the_required_fields_are_present_then_we_can_deserialize_the_message
        () {
        let text = r#"
        {
          "uuid": "UUID",
          "message": "message"
        }
        "#;

        let message = Template::compile("message".to_owned())
                          .expect("Failed to compile a handlebars template");
        let expected_message = MessageActionBuilder::new("UUID", message).build();
        let result = from_str::<MessageAction>(text);
        let message = result.expect("Failed to deserialize a valid MessageAction object");
        assert_message_action_eq(&expected_message, &message);
    }

    #[test]
    fn test_given_message_as_a_json_string_when_one_of_the_required_fields_are_not_present_then_we_get_error
        () {
        let text = r#"
        {
        }
        "#;

        let result = from_str::<MessageAction>(text);
        let _ = result.err().expect("Successfully deserialized an invalid MessageAction object");
    }

    #[test]
    fn test_given_inject_modes_when_they_are_deserialized_then_we_get_the_right_result() {
        let text = r#"
        ["forward", "log", "loopback", "log"]
        "#;
        let expected = vec![InjectMode::Forward,
                            InjectMode::Log,
                            InjectMode::Loopback,
                            InjectMode::Log];

        let result = from_str::<Vec<InjectMode>>(text);
        println!("{:?}", &result);
        let array = result.expect("Failed to deserialize a valid array of inject modes");
        assert_eq!(&expected, &array);
    }

    #[test]
    fn test_given_invalid_inject_mode_when_it_is_deserialized_then_we_get_the_right_result() {
        let text = r#"
        ["invalid inject mode", "log"]
        "#;

        let result = from_str::<Vec<InjectMode>>(text);
        println!("{:?}", &result);
        let _ = result.err().expect("Successfully deserialized an invalid inject mode");
    }

    #[test]
    fn test_given_message_when_it_contains_inject_mode_then_it_can_be_deserialized() {
        let text = r#"
        {
          "uuid": "UUID",
          "message": "message",
          "inject_mode": "forward"
        }
        "#;

        let message = Template::compile("message".to_owned())
                          .expect("Failed to compile a handlebars template");
        let expected_message = MessageActionBuilder::new("UUID", message)
                                   .inject_mode(InjectMode::Forward)
                                   .build();
        let result = from_str::<MessageAction>(text);
        let message = result.expect("Failed to deserialize a valid MessageAction object");
        assert_message_action_eq(&expected_message, &message);
    }

    #[test]
    fn test_given_message_is_deserialized_when_it_contains_an_unexpected_field_then_an_error_is_returned() {
        let text = r#"{ "unexpected": "UUID" }"#;
        let result = from_str::<MessageAction>(text);
        let _ = result.err().unwrap();
    }
}
<|endoftext|>


<commit_before>#[doc = "
Communication between tasks

Communication between tasks is facilitated by ports (in the receiving
task), and channels (in the sending task). Any number of channels may
feed into a single port.  Ports and channels may only transmit values
of unique types; that is, values that are statically guaranteed to be
accessed by a single 'owner' at a time.  Unique types include scalars,
vectors, strings, and records, tags, tuples and unique boxes (`~T`)
thereof. Most notably, shared boxes (`@T`) may not be transmitted
across channels.

# Example

~~~
let po = comm::port();
let ch = comm::chan(po);

task::spawn {||
    comm::send(ch, \"Hello, World\");
});

io::println(comm::recv(p));
~~~
"];

import either::either;

export send;
export recv;
export peek;
export select2;
export chan::{};
export port::{};

enum rust_port {}

#[abi = "cdecl"]
native mod rustrt {
    fn rust_port_id_send<T: send>(t: *sys::type_desc,
                                  target_port: port_id,
                                  data: T) -> libc::uintptr_t;

    fn new_port(unit_sz: libc::size_t) -> *rust_port;
    fn del_port(po: *rust_port);
    fn rust_port_begin_detach(po: *rust_port,
                              yield: *libc::uintptr_t);
    fn rust_port_end_detach(po: *rust_port);
    fn get_port_id(po: *rust_port) -> port_id;
    fn rust_port_size(po: *rust_port) -> libc::size_t;
    fn port_recv(dptr: *uint, po: *rust_port,
                 yield: *libc::uintptr_t);
    fn rust_port_select(dptr: **rust_port, ports: **rust_port,
                        n_ports: libc::size_t,
                        yield: *libc::uintptr_t);
}

#[abi = "rust-intrinsic"]
native mod rusti {
    fn init<T>() -> T;
}

type port_id = int;

// It's critical that this only have one variant, so it has a record
// layout, and will work in the rust_task structure in task.rs.
#[doc = "
A communication endpoint that can send messages

Each channel is bound to a port when the channel is constructed, so
the destination port for a channel must exist before the channel
itself.  Channels are weak: a channel does not keep the port it is
bound to alive. If a channel attempts to send data to a dead port that
data will be silently dropped.  Channels may be duplicated and
themselves transmitted over other channels.
"]
enum chan<T: send> {
    chan_t(port_id)
}

resource port_ptr<T: send>(po: *rust_port) {
    // Once the port is detached it's guaranteed not to receive further
    // messages
    let yield = 0u;
    let yieldp = ptr::addr_of(yield);
    rustrt::rust_port_begin_detach(po, yieldp);
    if yield != 0u {
        // Need to wait for the port to be detached
        // FIXME: If this fails then we're going to leave our port
        // in a bogus state. (Issue #1988)
        task::yield();
    }
    rustrt::rust_port_end_detach(po);

    // Drain the port so that all the still-enqueued items get dropped
    while rustrt::rust_port_size(po) > 0u {
        // FIXME: For some reason if we don't assign to something here
        // we end up with invalid reads in the drop glue.
        let _t = recv_::<T>(po);
    }
    rustrt::del_port(po);
}

#[doc = "
A communication endpoint that can receive messages

Each port has a unique per-task identity and may not be replicated or
transmitted. If a port value is copied, both copies refer to the same
port.  Ports may be associated with multiple `chan`s.
"]
enum port<T: send> { port_t(@port_ptr<T>) }

#[doc = "
Sends data over a channel. The sent data is moved into the channel,
whereupon the caller loses access to it.
"]
fn send<T: send>(ch: chan<T>, -data: T) {
    let chan_t(p) = ch;
    let res = rustrt::rust_port_id_send(sys::get_type_desc::<T>(), p, data);
    if res != 0u unsafe {
        // Data sent successfully
        unsafe::forget(data);
    }
    task::yield();
}

#[doc = "Constructs a port"]
fn port<T: send>() -> port<T> {
    port_t(@port_ptr(rustrt::new_port(sys::size_of::<T>())))
}

#[doc = "
Receive from a port.  If no data is available on the port then the
task will block until data becomes available.
"]
fn recv<T: send>(p: port<T>) -> T { recv_(***p) }

#[doc = "Receive on a raw port pointer"]
fn recv_<T: send>(p: *rust_port) -> T {
    let yield = 0u;
    let yieldp = ptr::addr_of(yield);
    let mut res;
    res = rusti::init::<T>();
    rustrt::port_recv(ptr::addr_of(res) as *uint, p, yieldp);

    if yield != 0u {
        // Data isn't available yet, so res has not been initialized.
        task::yield();
    } else {
        // In the absense of compiler-generated preemption points
        // this is a good place to yield
        task::yield();
    }
    ret res;
}

#[doc = "Receive on one of two ports"]
fn select2<A: send, B: send>(p_a: port<A>, p_b: port<B>)
    -> either<A, B> unsafe {
    let ports = [***p_a, ***p_b];
    let n_ports = 2 as libc::size_t;
    let yield = 0u, yieldp = ptr::addr_of(yield);

    let mut resport: *rust_port;
    resport = rusti::init::<*rust_port>();
    vec::as_buf(ports) {|ports|
        rustrt::rust_port_select(ptr::addr_of(resport), ports, n_ports,
                                 yieldp);
    }

    if yield != 0u {
        // Wait for data
        task::yield();
    } else {
        // As in recv, this is a good place to yield anyway until
        // the compiler generates yield calls
        task::yield();
    }

    // Now we know the port we're supposed to receive from
    assert resport != ptr::null();

    if resport == ***p_a {
        either::left(recv(p_a))
    } else if resport == ***p_b {
        either::right(recv(p_b))
    } else {
        fail "unexpected result from rust_port_select";
    }
}

#[doc = "Returns true if there are messages available"]
fn peek<T: send>(p: port<T>) -> bool {
    rustrt::rust_port_size(***p) != 0u as libc::size_t
}

#[doc = "
Constructs a channel. The channel is bound to the port used to
construct it.
"]
fn chan<T: send>(p: port<T>) -> chan<T> {
    chan_t(rustrt::get_port_id(***p))
}

#[test]
fn create_port_and_chan() { let p = port::<int>(); chan(p); }

#[test]
fn send_int() {
    let p = port::<int>();
    let c = chan(p);
    send(c, 22);
}

#[test]
fn send_recv_fn() {
    let p = port::<int>();
    let c = chan::<int>(p);
    send(c, 42);
    assert (recv(p) == 42);
}

#[test]
fn send_recv_fn_infer() {
    let p = port();
    let c = chan(p);
    send(c, 42);
    assert (recv(p) == 42);
}

#[test]
fn chan_chan_infer() {
    let p = port(), p2 = port::<int>();
    let c = chan(p);
    send(c, chan(p2));
    recv(p);
}

#[test]
fn chan_chan() {
    let p = port::<chan<int>>(), p2 = port::<int>();
    let c = chan(p);
    send(c, chan(p2));
    recv(p);
}

#[test]
fn test_peek() {
    let po = port();
    let ch = chan(po);
    assert !peek(po);
    send(ch, ());
    assert peek(po);
    recv(po);
    assert !peek(po);
}

#[test]
fn test_select2_available() {
    let po_a = port();
    let po_b = port();
    let ch_a = chan(po_a);
    let ch_b = chan(po_b);

    send(ch_a, "a");

    assert select2(po_a, po_b) == either::left("a");

    send(ch_b, "b");

    assert select2(po_a, po_b) == either::right("b");
}

#[test]
fn test_select2_rendezvous() {
    let po_a = port();
    let po_b = port();
    let ch_a = chan(po_a);
    let ch_b = chan(po_b);

    iter::repeat(10u) {||
        task::spawn {||
            iter::repeat(10u) {|| task::yield() }
            send(ch_a, "a");
        };

        assert select2(po_a, po_b) == either::left("a");

        task::spawn {||
            iter::repeat(10u) {|| task::yield() }
            send(ch_b, "b");
        };

        assert select2(po_a, po_b) == either::right("b");
    }
}

#[test]
fn test_select2_stress() {
    let po_a = port();
    let po_b = port();
    let ch_a = chan(po_a);
    let ch_b = chan(po_b);

    let msgs = 100u;
    let times = 4u;

    iter::repeat(times) {||
        task::spawn {||
            iter::repeat(msgs) {||
                send(ch_a, "a")
            }
        };
        task::spawn {||
            iter::repeat(msgs) {||
                send(ch_b, "b")
            }
        };
    }

    let mut as = 0;
    let mut bs = 0;
    iter::repeat(msgs * times * 2u) {||
        alt check select2(po_a, po_b) {
          either::left("a") { as += 1 }
          either::right("b") { bs += 1 }
        }
    }

    assert as == 400;
    assert bs == 400;
}
<commit_msg>Remove a FIXME and workaround that appear to be obsolete<commit_after>#[doc = "
Communication between tasks

Communication between tasks is facilitated by ports (in the receiving
task), and channels (in the sending task). Any number of channels may
feed into a single port.  Ports and channels may only transmit values
of unique types; that is, values that are statically guaranteed to be
accessed by a single 'owner' at a time.  Unique types include scalars,
vectors, strings, and records, tags, tuples and unique boxes (`~T`)
thereof. Most notably, shared boxes (`@T`) may not be transmitted
across channels.

# Example

~~~
let po = comm::port();
let ch = comm::chan(po);

task::spawn {||
    comm::send(ch, \"Hello, World\");
});

io::println(comm::recv(p));
~~~
"];

import either::either;

export send;
export recv;
export peek;
export select2;
export chan::{};
export port::{};

enum rust_port {}

#[abi = "cdecl"]
native mod rustrt {
    fn rust_port_id_send<T: send>(t: *sys::type_desc,
                                  target_port: port_id,
                                  data: T) -> libc::uintptr_t;

    fn new_port(unit_sz: libc::size_t) -> *rust_port;
    fn del_port(po: *rust_port);
    fn rust_port_begin_detach(po: *rust_port,
                              yield: *libc::uintptr_t);
    fn rust_port_end_detach(po: *rust_port);
    fn get_port_id(po: *rust_port) -> port_id;
    fn rust_port_size(po: *rust_port) -> libc::size_t;
    fn port_recv(dptr: *uint, po: *rust_port,
                 yield: *libc::uintptr_t);
    fn rust_port_select(dptr: **rust_port, ports: **rust_port,
                        n_ports: libc::size_t,
                        yield: *libc::uintptr_t);
}

#[abi = "rust-intrinsic"]
native mod rusti {
    fn init<T>() -> T;
}

type port_id = int;

// It's critical that this only have one variant, so it has a record
// layout, and will work in the rust_task structure in task.rs.
#[doc = "
A communication endpoint that can send messages

Each channel is bound to a port when the channel is constructed, so
the destination port for a channel must exist before the channel
itself.  Channels are weak: a channel does not keep the port it is
bound to alive. If a channel attempts to send data to a dead port that
data will be silently dropped.  Channels may be duplicated and
themselves transmitted over other channels.
"]
enum chan<T: send> {
    chan_t(port_id)
}

resource port_ptr<T: send>(po: *rust_port) {
    // Once the port is detached it's guaranteed not to receive further
    // messages
    let yield = 0u;
    let yieldp = ptr::addr_of(yield);
    rustrt::rust_port_begin_detach(po, yieldp);
    if yield != 0u {
        // Need to wait for the port to be detached
        // FIXME: If this fails then we're going to leave our port
        // in a bogus state. (Issue #1988)
        task::yield();
    }
    rustrt::rust_port_end_detach(po);

    // Drain the port so that all the still-enqueued items get dropped
    while rustrt::rust_port_size(po) > 0u {
       recv_::<T>(po);
    }
    rustrt::del_port(po);
}

#[doc = "
A communication endpoint that can receive messages

Each port has a unique per-task identity and may not be replicated or
transmitted. If a port value is copied, both copies refer to the same
port.  Ports may be associated with multiple `chan`s.
"]
enum port<T: send> { port_t(@port_ptr<T>) }

#[doc = "
Sends data over a channel. The sent data is moved into the channel,
whereupon the caller loses access to it.
"]
fn send<T: send>(ch: chan<T>, -data: T) {
    let chan_t(p) = ch;
    let res = rustrt::rust_port_id_send(sys::get_type_desc::<T>(), p, data);
    if res != 0u unsafe {
        // Data sent successfully
        unsafe::forget(data);
    }
    task::yield();
}

#[doc = "Constructs a port"]
fn port<T: send>() -> port<T> {
    port_t(@port_ptr(rustrt::new_port(sys::size_of::<T>())))
}

#[doc = "
Receive from a port.  If no data is available on the port then the
task will block until data becomes available.
"]
fn recv<T: send>(p: port<T>) -> T { recv_(***p) }

#[doc = "Receive on a raw port pointer"]
fn recv_<T: send>(p: *rust_port) -> T {
    let yield = 0u;
    let yieldp = ptr::addr_of(yield);
    let mut res;
    res = rusti::init::<T>();
    rustrt::port_recv(ptr::addr_of(res) as *uint, p, yieldp);

    if yield != 0u {
        // Data isn't available yet, so res has not been initialized.
        task::yield();
    } else {
        // In the absense of compiler-generated preemption points
        // this is a good place to yield
        task::yield();
    }
    ret res;
}

#[doc = "Receive on one of two ports"]
fn select2<A: send, B: send>(p_a: port<A>, p_b: port<B>)
    -> either<A, B> unsafe {
    let ports = [***p_a, ***p_b];
    let n_ports = 2 as libc::size_t;
    let yield = 0u, yieldp = ptr::addr_of(yield);

    let mut resport: *rust_port;
    resport = rusti::init::<*rust_port>();
    vec::as_buf(ports) {|ports|
        rustrt::rust_port_select(ptr::addr_of(resport), ports, n_ports,
                                 yieldp);
    }

    if yield != 0u {
        // Wait for data
        task::yield();
    } else {
        // As in recv, this is a good place to yield anyway until
        // the compiler generates yield calls
        task::yield();
    }

    // Now we know the port we're supposed to receive from
    assert resport != ptr::null();

    if resport == ***p_a {
        either::left(recv(p_a))
    } else if resport == ***p_b {
        either::right(recv(p_b))
    } else {
        fail "unexpected result from rust_port_select";
    }
}

#[doc = "Returns true if there are messages available"]
fn peek<T: send>(p: port<T>) -> bool {
    rustrt::rust_port_size(***p) != 0u as libc::size_t
}

#[doc = "
Constructs a channel. The channel is bound to the port used to
construct it.
"]
fn chan<T: send>(p: port<T>) -> chan<T> {
    chan_t(rustrt::get_port_id(***p))
}

#[test]
fn create_port_and_chan() { let p = port::<int>(); chan(p); }

#[test]
fn send_int() {
    let p = port::<int>();
    let c = chan(p);
    send(c, 22);
}

#[test]
fn send_recv_fn() {
    let p = port::<int>();
    let c = chan::<int>(p);
    send(c, 42);
    assert (recv(p) == 42);
}

#[test]
fn send_recv_fn_infer() {
    let p = port();
    let c = chan(p);
    send(c, 42);
    assert (recv(p) == 42);
}

#[test]
fn chan_chan_infer() {
    let p = port(), p2 = port::<int>();
    let c = chan(p);
    send(c, chan(p2));
    recv(p);
}

#[test]
fn chan_chan() {
    let p = port::<chan<int>>(), p2 = port::<int>();
    let c = chan(p);
    send(c, chan(p2));
    recv(p);
}

#[test]
fn test_peek() {
    let po = port();
    let ch = chan(po);
    assert !peek(po);
    send(ch, ());
    assert peek(po);
    recv(po);
    assert !peek(po);
}

#[test]
fn test_select2_available() {
    let po_a = port();
    let po_b = port();
    let ch_a = chan(po_a);
    let ch_b = chan(po_b);

    send(ch_a, "a");

    assert select2(po_a, po_b) == either::left("a");

    send(ch_b, "b");

    assert select2(po_a, po_b) == either::right("b");
}

#[test]
fn test_select2_rendezvous() {
    let po_a = port();
    let po_b = port();
    let ch_a = chan(po_a);
    let ch_b = chan(po_b);

    iter::repeat(10u) {||
        task::spawn {||
            iter::repeat(10u) {|| task::yield() }
            send(ch_a, "a");
        };

        assert select2(po_a, po_b) == either::left("a");

        task::spawn {||
            iter::repeat(10u) {|| task::yield() }
            send(ch_b, "b");
        };

        assert select2(po_a, po_b) == either::right("b");
    }
}

#[test]
fn test_select2_stress() {
    let po_a = port();
    let po_b = port();
    let ch_a = chan(po_a);
    let ch_b = chan(po_b);

    let msgs = 100u;
    let times = 4u;

    iter::repeat(times) {||
        task::spawn {||
            iter::repeat(msgs) {||
                send(ch_a, "a")
            }
        };
        task::spawn {||
            iter::repeat(msgs) {||
                send(ch_b, "b")
            }
        };
    }

    let mut as = 0;
    let mut bs = 0;
    iter::repeat(msgs * times * 2u) {||
        alt check select2(po_a, po_b) {
          either::left("a") { as += 1 }
          either::right("b") { bs += 1 }
        }
    }

    assert as == 400;
    assert bs == 400;
}
<|endoftext|>


<commit_before>use super::{Open, Sink};
use jack::prelude::{client_options, AsyncClient, AudioOutPort, AudioOutSpec, Client, JackControl, Port,
                    ProcessHandler, ProcessScope};
use std::io;
use std::sync::mpsc::{sync_channel, Receiver, SyncSender};

pub struct JackSink {
    send: SyncSender<i16>,
    active_client: AsyncClient<(), JackData>,
}

pub struct JackData {
    rec: Receiver<i16>,
    port_l: Port<AudioOutSpec>,
    port_r: Port<AudioOutSpec>,
}

fn pcm_to_f32(sample: i16) -> f32 {
    sample as f32 / 32768.0;
}

impl ProcessHandler for JackData {
    fn process(&mut self, _: &Client, ps: &ProcessScope) -> JackControl {
        // get output port buffers
        let mut out_r = AudioOutPort::new(&mut self.port_r, ps);
        let mut out_l = AudioOutPort::new(&mut self.port_l, ps);
        let buf_r: &mut [f32] = &mut out_r;
        let buf_l: &mut [f32] = &mut out_l;
        // get queue iterator
        let mut queue_iter = self.rec.try_iter();

        let buf_size = buf_r.len();
        for i in 0..buf_size {
            buf_r[i] = pcm_to_f32(queue_iter.next().unwrap_or(0));
            buf_l[i] = pcm_to_f32(queue_iter.next().unwrap_or(0));
        }
        JackControl::Continue
    }
}

impl Open for JackSink {
    fn open(client_name: Option<String>) -> JackSink {
        info!("Using jack sink!");

        let client_name = client_name.unwrap_or("librespot".to_string());
        let (client, _status) = Client::new(&client_name[..], client_options::NO_START_SERVER).unwrap();
        let ch_r = client.register_port("out_0", AudioOutSpec::default()).unwrap();
        let ch_l = client.register_port("out_1", AudioOutSpec::default()).unwrap();
        // buffer for samples from librespot (~10ms)
        let (tx, rx) = sync_channel(2 * 1024 * 4);
        let jack_data = JackData {
            rec: rx,
            port_l: ch_l,
            port_r: ch_r,
        };
        let active_client = AsyncClient::new(client, (), jack_data).unwrap();

        JackSink {
            send: tx,
            active_client: active_client,
        }
    }
}

impl Sink for JackSink {
    fn start(&mut self) -> io::Result<()> {
        Ok(())
    }

    fn stop(&mut self) -> io::Result<()> {
        Ok(())
    }

    fn write(&mut self, data: &[i16]) -> io::Result<()> {
        for s in data.iter() {
            let res = self.send.send(*s);
            if res.is_err() {
                error!("jackaudio: cannot write to channel");
            }
        }
        Ok(())
    }
}
<commit_msg>Fixed Jackaudio compiling (#222)<commit_after>use super::{Open, Sink};
use jack::prelude::{client_options, AsyncClient, AudioOutPort, AudioOutSpec, Client, JackControl, Port,
                    ProcessHandler, ProcessScope};
use std::io;
use std::sync::mpsc::{sync_channel, Receiver, SyncSender};

pub struct JackSink {
    send: SyncSender<i16>,
    active_client: AsyncClient<(), JackData>,
}

pub struct JackData {
    rec: Receiver<i16>,
    port_l: Port<AudioOutSpec>,
    port_r: Port<AudioOutSpec>,
}

fn pcm_to_f32(sample: i16) -> f32 {
    sample as f32 / 32768.0
}

impl ProcessHandler for JackData {
    fn process(&mut self, _: &Client, ps: &ProcessScope) -> JackControl {
        // get output port buffers
        let mut out_r = AudioOutPort::new(&mut self.port_r, ps);
        let mut out_l = AudioOutPort::new(&mut self.port_l, ps);
        let buf_r: &mut [f32] = &mut out_r;
        let buf_l: &mut [f32] = &mut out_l;
        // get queue iterator
        let mut queue_iter = self.rec.try_iter();

        let buf_size = buf_r.len();
        for i in 0..buf_size {
            buf_r[i] = pcm_to_f32(queue_iter.next().unwrap_or(0));
            buf_l[i] = pcm_to_f32(queue_iter.next().unwrap_or(0));
        }
        JackControl::Continue
    }
}

impl Open for JackSink {
    fn open(client_name: Option<String>) -> JackSink {
        info!("Using jack sink!");

        let client_name = client_name.unwrap_or("librespot".to_string());
        let (client, _status) = Client::new(&client_name[..], client_options::NO_START_SERVER).unwrap();
        let ch_r = client.register_port("out_0", AudioOutSpec::default()).unwrap();
        let ch_l = client.register_port("out_1", AudioOutSpec::default()).unwrap();
        // buffer for samples from librespot (~10ms)
        let (tx, rx) = sync_channel(2 * 1024 * 4);
        let jack_data = JackData {
            rec: rx,
            port_l: ch_l,
            port_r: ch_r,
        };
        let active_client = AsyncClient::new(client, (), jack_data).unwrap();

        JackSink {
            send: tx,
            active_client: active_client,
        }
    }
}

impl Sink for JackSink {
    fn start(&mut self) -> io::Result<()> {
        Ok(())
    }

    fn stop(&mut self) -> io::Result<()> {
        Ok(())
    }

    fn write(&mut self, data: &[i16]) -> io::Result<()> {
        for s in data.iter() {
            let res = self.send.send(*s);
            if res.is_err() {
                error!("jackaudio: cannot write to channel");
            }
        }
        Ok(())
    }
}
<|endoftext|>


<commit_before>use itertools::Itertools;
use serde::{Deserialize, Serialize};
use std::path::{Path, PathBuf};
use std::{fs, io};
use thiserror::Error;

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct Measurement {
    pub command: String,
    pub mean: f64,
    pub stddev: f64,
    pub median: f64,
    pub user: f64,
    pub system: f64,
    pub min: f64,
    pub max: f64,
    pub times: Vec<f64>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct Measurements {
    pub results: Vec<Measurement>,
}

#[derive(Debug, Clone, PartialEq)]
pub struct Data {
    pub threshold: f64,
    pub difference: f64,
    pub baseline: f64,
    pub dev: f64,
}

#[derive(Debug, Clone, PartialEq)]
pub struct Calculation {
    pub metric: String,
    pub regression: bool,
    pub data: Data,
}

#[derive(Debug, Clone, PartialEq)]
pub struct MeasurementGroup {
    pub version: String,
    pub run: String,
    pub measurement: Measurement,
}

#[derive(Error, Debug)]
pub enum TestError {
    #[error("BadJSONErr: JSON in file cannot be deserialized as expected.\nFilepath: {}\nOriginating Exception:{}", .0.to_string_lossy().into_owned(), .1.as_ref().map_or("None".to_owned(), |e| format!("{}", e)))]
    BadJSONErr(PathBuf, Option<serde_json::Error>),
    #[error("ReadErr: The file cannot be read.\nFilepath: {}\nOriginating Exception:{}", .0.to_string_lossy().into_owned(), .1.as_ref().map_or("None".to_owned(), |e| format!("{}", e)))]
    ReadErr(PathBuf, Option<io::Error>),
    #[error("MissingFilenameErr: The path provided does not specify a file.\nFilepath: {}", .0.to_string_lossy().into_owned())]
    MissingFilenameErr(PathBuf),
    #[error("FilenameNotUnicodeErr: The filename is not expressible in unicode. Consider renaming the file.\nFilepath: {}", .0.to_string_lossy().into_owned())]
    FilenameNotUnicodeErr(PathBuf),
    #[error("BadFileContentsErr: Check that the file exists and is readable.\nFilepath: {}\nOriginating Exception:{}", .0.to_string_lossy().into_owned(), .1.as_ref().map_or("None".to_owned(), |e| format!("{}", e)))]
    BadFileContentsErr(PathBuf, Option<io::Error>),
    #[error("NoResultsErr: The results directory has no json files in it.\nFilepath: {}", .0.to_string_lossy().into_owned())]
    NoResultsErr(PathBuf),
    #[error("OddResultsCountErr: The results directory has an odd number of results in it. Expected an even number.\nFile Count: {}\nFilepath: {}", .0, .1.to_string_lossy().into_owned())]
    OddResultsCountErr(usize, PathBuf),
    #[error("BadGroupSizeErr: Expected two results per group, one for each branch-project pair.\nCount: {}\nGroup: {:?}", .0, .1.into_iter().map(|group| (&group.version[..], &group.run[..])).collect::<Vec<(&str, &str)>>())]
    BadGroupSizeErr(usize, Vec<MeasurementGroup>),
    #[error("BadBranchNameErr: Branch names must be 'baseline' and 'dev'.\nFound: {}, {}", .0, .1)]
    BadBranchNameErr(String, String),
}

fn calculate(metric: &str, dev: &Measurement, baseline: &Measurement) -> Vec<Calculation> {
    let median_threshold = 1.05; // 5% regression threshold
    let median_difference = dev.median / baseline.median;

    let stddev_threshold = 1.05; // 5% regression threshold
    let stddev_difference = dev.stddev / baseline.stddev;

    vec![
        Calculation {
            metric: ["median", metric].join("_"),
            regression: median_difference > median_threshold,
            data: Data {
                threshold: median_threshold,
                difference: median_difference,
                baseline: baseline.median.clone(),
                dev: dev.median.clone(),
            }
        },
        Calculation {
            metric: ["stddev", metric].join("_"),
            regression: stddev_difference > stddev_threshold,
            data: Data {
                threshold: stddev_threshold,
                difference: stddev_difference,
                baseline: baseline.stddev.clone(),
                dev: dev.stddev.clone(),
            }
        },
    ]
}

// given a directory, read all files in the directory and return each
// filename with the deserialized json contents of that file
fn measurements_from_files(
    results_directory: &Path,
) -> Result<Vec<(PathBuf, Measurements)>, TestError> {
    let result_files = fs::read_dir(results_directory)
        .or_else(|e| Err(TestError::ReadErr(results_directory.to_path_buf(), Some(e))))?;

    result_files
        .into_iter()
        .map(|f| f.unwrap().path())
        .filter(|filename| {
            filename
                .extension()
                .and_then(|ext| ext.to_str())
                .map_or(false, |ext| ext.ends_with("json"))
        })
        .map(|filename| {
            fs::read_to_string(&filename)
                .or_else(|e| Err(TestError::BadFileContentsErr(filename.clone(), Some(e))))
                .and_then(|contents| {
                    serde_json::from_str::<Measurements>(&contents)
                        .or_else(|e| Err(TestError::BadJSONErr(filename.clone(), Some(e))))
                })
                .map(|m| (filename, m))
        })
        .collect()
}

// given a list of filename-measurement pairs, detect any regressions by grouping
// measurements together by filename
fn calculate_regressions(
    measurements: &[(PathBuf, Measurement)],
) -> Result<Vec<Calculation>, TestError> {
    let mut measurement_groups: Vec<MeasurementGroup> = measurements
        .into_iter()
        .map(|(p, m)| {
            p.file_name()
                .ok_or_else(|| TestError::MissingFilenameErr(p.to_path_buf()))
                .and_then(|name| {
                    name.to_str()
                        .ok_or_else(|| TestError::FilenameNotUnicodeErr(p.to_path_buf()))
                })
                .map(|name| {
                    let parts: Vec<&str> = name.split("_").collect();
                    MeasurementGroup {
                        version: parts[0].to_owned(),
                        run: parts[1..].join("_"),
                        measurement: m.clone(),
                    }
                })
        })
        .collect::<Result<Vec<MeasurementGroup>, TestError>>()?;

    measurement_groups.sort_by(|x, y| (&x.run, &x.version).cmp(&(&y.run, &y.version)));

    // locking up mutation
    let sorted_measurement_groups = measurement_groups;

    let calculations: Vec<Calculation> = sorted_measurement_groups
        .into_iter()
        .group_by(|x| x.run.clone())
        .into_iter()
        .map(|(_, g)| {
            let mut groups: Vec<MeasurementGroup> = g.collect();
            groups.sort_by(|x, y| x.version.cmp(&y.version));

            match groups.len() {
                2 => {
                    let dev = &groups[1];
                    let baseline = &groups[0];

                    if dev.version == "dev" && baseline.version == "baseline" {
                        Ok(calculate(&dev.run, &dev.measurement, &baseline.measurement))
                    } else {
                        Err(TestError::BadBranchNameErr(baseline.version.clone(), dev.version.clone()))
                    }
                },
                i => Err(TestError::BadGroupSizeErr(i, groups)),
            }
        })
        .collect::<Result<Vec<Vec<Calculation>>, TestError>>()?
        .concat();

    Ok(calculations)
}

pub fn regressions(results_directory: &PathBuf) -> Result<Vec<Calculation>, TestError> {
    measurements_from_files(Path::new(&results_directory)).and_then(|v| {
        // exit early with an Err if there are no results to process
        match v.len() {
            0 => Err(TestError::NoResultsErr(results_directory.clone())),
            i if i % 2 == 1 => Err(TestError::OddResultsCountErr(i, results_directory.clone())),
            _ => Ok(()),
        }?;

        let v_next: Vec<(PathBuf, Measurement)> = v
            .into_iter()
            // the way we're running these, the files will each contain exactly one measurement, hence `results[0]`
            .map(|(p, ms)| (p, ms.results[0].clone()))
            .collect();

        calculate_regressions(&v_next)
    })
}

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

    #[test]
    fn test_error_messages() {
        let pairs = vec![
            (
                TestError::BadJSONErr(Path::new("dummy/path/file.json").to_path_buf(), None),
                r#"BadJSONErr: JSON in file cannot be deserialized as expected.
Filepath: dummy/path/file.json
Originating Exception:None"#
            ),
            (
                TestError::ReadErr(Path::new("dummy/path/file.json").to_path_buf(), None),
                r#"ReadErr: The file cannot be read.
Filepath: dummy/path/file.json
Originating Exception:None"#
            ),
            (
                TestError::MissingFilenameErr(Path::new("dummy/path/no_file/").to_path_buf()),
                r#"MissingFilenameErr: The path provided does not specify a file.
Filepath: dummy/path/no_file/"#
            ),
            (
                TestError::FilenameNotUnicodeErr(Path::new("dummy/path/no_file/").to_path_buf()),
                r#"FilenameNotUnicodeErr: The filename is not expressible in unicode. Consider renaming the file.
Filepath: dummy/path/no_file/"#
            ),
            (
                TestError::BadFileContentsErr(Path::new("dummy/path/filenotexist.json").to_path_buf(), None),
                r#"BadFileContentsErr: Check that the file exists and is readable.
Filepath: dummy/path/filenotexist.json
Originating Exception:None"#
            ),
            (
                TestError::NoResultsErr(Path::new("dummy/path/no_file/").to_path_buf()),
                r#"NoResultsErr: The results directory has no json files in it.
Filepath: dummy/path/no_file/"#
            ),
            (
                TestError::OddResultsCountErr(3, Path::new("dummy/path/no_file/").to_path_buf()),
                r#"OddResultsCountErr: The results directory has an odd number of results in it. Expected an even number.
File Count: 3
Filepath: dummy/path/no_file/"#
            ),
            (
                TestError::BadGroupSizeErr(1, vec![MeasurementGroup {
                    version: "dev".to_owned(),
                    run: "some command".to_owned(),
                    measurement: Measurement {
                        command: "some command".to_owned(),
                        mean: 1.0,
                        stddev: 1.0,
                        median: 1.0,
                        user: 1.0,
                        system: 1.0,
                        min: 1.0,
                        max: 1.0,
                        times: vec![1.0, 1.1, 0.9, 1.0, 1.1, 0.9, 1.1],
                    }
                }]),
                r#"BadGroupSizeErr: Expected two results per group, one for each branch-project pair.
Count: 1
Group: [("dev", "some command")]"#
            ),
            (
                TestError::BadBranchNameErr("boop".to_owned(), "noop".to_owned()).to_path_buf(),
                r#"BadBranchNameErr: Branch names must be 'baseline' and 'dev'.
Found: boop, noop"#
            ),

        ];

        for (err, msg) in pairs {
            assert_eq!(format!("{}", err), msg)
        }
    }

}
<commit_msg>test fix<commit_after>use itertools::Itertools;
use serde::{Deserialize, Serialize};
use std::path::{Path, PathBuf};
use std::{fs, io};
use thiserror::Error;

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct Measurement {
    pub command: String,
    pub mean: f64,
    pub stddev: f64,
    pub median: f64,
    pub user: f64,
    pub system: f64,
    pub min: f64,
    pub max: f64,
    pub times: Vec<f64>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct Measurements {
    pub results: Vec<Measurement>,
}

#[derive(Debug, Clone, PartialEq)]
pub struct Data {
    pub threshold: f64,
    pub difference: f64,
    pub baseline: f64,
    pub dev: f64,
}

#[derive(Debug, Clone, PartialEq)]
pub struct Calculation {
    pub metric: String,
    pub regression: bool,
    pub data: Data,
}

#[derive(Debug, Clone, PartialEq)]
pub struct MeasurementGroup {
    pub version: String,
    pub run: String,
    pub measurement: Measurement,
}

#[derive(Error, Debug)]
pub enum TestError {
    #[error("BadJSONErr: JSON in file cannot be deserialized as expected.\nFilepath: {}\nOriginating Exception:{}", .0.to_string_lossy().into_owned(), .1.as_ref().map_or("None".to_owned(), |e| format!("{}", e)))]
    BadJSONErr(PathBuf, Option<serde_json::Error>),
    #[error("ReadErr: The file cannot be read.\nFilepath: {}\nOriginating Exception:{}", .0.to_string_lossy().into_owned(), .1.as_ref().map_or("None".to_owned(), |e| format!("{}", e)))]
    ReadErr(PathBuf, Option<io::Error>),
    #[error("MissingFilenameErr: The path provided does not specify a file.\nFilepath: {}", .0.to_string_lossy().into_owned())]
    MissingFilenameErr(PathBuf),
    #[error("FilenameNotUnicodeErr: The filename is not expressible in unicode. Consider renaming the file.\nFilepath: {}", .0.to_string_lossy().into_owned())]
    FilenameNotUnicodeErr(PathBuf),
    #[error("BadFileContentsErr: Check that the file exists and is readable.\nFilepath: {}\nOriginating Exception:{}", .0.to_string_lossy().into_owned(), .1.as_ref().map_or("None".to_owned(), |e| format!("{}", e)))]
    BadFileContentsErr(PathBuf, Option<io::Error>),
    #[error("NoResultsErr: The results directory has no json files in it.\nFilepath: {}", .0.to_string_lossy().into_owned())]
    NoResultsErr(PathBuf),
    #[error("OddResultsCountErr: The results directory has an odd number of results in it. Expected an even number.\nFile Count: {}\nFilepath: {}", .0, .1.to_string_lossy().into_owned())]
    OddResultsCountErr(usize, PathBuf),
    #[error("BadGroupSizeErr: Expected two results per group, one for each branch-project pair.\nCount: {}\nGroup: {:?}", .0, .1.into_iter().map(|group| (&group.version[..], &group.run[..])).collect::<Vec<(&str, &str)>>())]
    BadGroupSizeErr(usize, Vec<MeasurementGroup>),
    #[error("BadBranchNameErr: Branch names must be 'baseline' and 'dev'.\nFound: {}, {}", .0, .1)]
    BadBranchNameErr(String, String),
}

fn calculate(metric: &str, dev: &Measurement, baseline: &Measurement) -> Vec<Calculation> {
    let median_threshold = 1.05; // 5% regression threshold
    let median_difference = dev.median / baseline.median;

    let stddev_threshold = 1.05; // 5% regression threshold
    let stddev_difference = dev.stddev / baseline.stddev;

    vec![
        Calculation {
            metric: ["median", metric].join("_"),
            regression: median_difference > median_threshold,
            data: Data {
                threshold: median_threshold,
                difference: median_difference,
                baseline: baseline.median.clone(),
                dev: dev.median.clone(),
            }
        },
        Calculation {
            metric: ["stddev", metric].join("_"),
            regression: stddev_difference > stddev_threshold,
            data: Data {
                threshold: stddev_threshold,
                difference: stddev_difference,
                baseline: baseline.stddev.clone(),
                dev: dev.stddev.clone(),
            }
        },
    ]
}

// given a directory, read all files in the directory and return each
// filename with the deserialized json contents of that file
fn measurements_from_files(
    results_directory: &Path,
) -> Result<Vec<(PathBuf, Measurements)>, TestError> {
    let result_files = fs::read_dir(results_directory)
        .or_else(|e| Err(TestError::ReadErr(results_directory.to_path_buf(), Some(e))))?;

    result_files
        .into_iter()
        .map(|f| f.unwrap().path())
        .filter(|filename| {
            filename
                .extension()
                .and_then(|ext| ext.to_str())
                .map_or(false, |ext| ext.ends_with("json"))
        })
        .map(|filename| {
            fs::read_to_string(&filename)
                .or_else(|e| Err(TestError::BadFileContentsErr(filename.clone(), Some(e))))
                .and_then(|contents| {
                    serde_json::from_str::<Measurements>(&contents)
                        .or_else(|e| Err(TestError::BadJSONErr(filename.clone(), Some(e))))
                })
                .map(|m| (filename, m))
        })
        .collect()
}

// given a list of filename-measurement pairs, detect any regressions by grouping
// measurements together by filename
fn calculate_regressions(
    measurements: &[(PathBuf, Measurement)],
) -> Result<Vec<Calculation>, TestError> {
    let mut measurement_groups: Vec<MeasurementGroup> = measurements
        .into_iter()
        .map(|(p, m)| {
            p.file_name()
                .ok_or_else(|| TestError::MissingFilenameErr(p.to_path_buf()))
                .and_then(|name| {
                    name.to_str()
                        .ok_or_else(|| TestError::FilenameNotUnicodeErr(p.to_path_buf()))
                })
                .map(|name| {
                    let parts: Vec<&str> = name.split("_").collect();
                    MeasurementGroup {
                        version: parts[0].to_owned(),
                        run: parts[1..].join("_"),
                        measurement: m.clone(),
                    }
                })
        })
        .collect::<Result<Vec<MeasurementGroup>, TestError>>()?;

    measurement_groups.sort_by(|x, y| (&x.run, &x.version).cmp(&(&y.run, &y.version)));

    // locking up mutation
    let sorted_measurement_groups = measurement_groups;

    let calculations: Vec<Calculation> = sorted_measurement_groups
        .into_iter()
        .group_by(|x| x.run.clone())
        .into_iter()
        .map(|(_, g)| {
            let mut groups: Vec<MeasurementGroup> = g.collect();
            groups.sort_by(|x, y| x.version.cmp(&y.version));

            match groups.len() {
                2 => {
                    let dev = &groups[1];
                    let baseline = &groups[0];

                    if dev.version == "dev" && baseline.version == "baseline" {
                        Ok(calculate(&dev.run, &dev.measurement, &baseline.measurement))
                    } else {
                        Err(TestError::BadBranchNameErr(baseline.version.clone(), dev.version.clone()))
                    }
                },
                i => Err(TestError::BadGroupSizeErr(i, groups)),
            }
        })
        .collect::<Result<Vec<Vec<Calculation>>, TestError>>()?
        .concat();

    Ok(calculations)
}

pub fn regressions(results_directory: &PathBuf) -> Result<Vec<Calculation>, TestError> {
    measurements_from_files(Path::new(&results_directory)).and_then(|v| {
        // exit early with an Err if there are no results to process
        match v.len() {
            0 => Err(TestError::NoResultsErr(results_directory.clone())),
            i if i % 2 == 1 => Err(TestError::OddResultsCountErr(i, results_directory.clone())),
            _ => Ok(()),
        }?;

        let v_next: Vec<(PathBuf, Measurement)> = v
            .into_iter()
            // the way we're running these, the files will each contain exactly one measurement, hence `results[0]`
            .map(|(p, ms)| (p, ms.results[0].clone()))
            .collect();

        calculate_regressions(&v_next)
    })
}

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

    #[test]
    fn test_error_messages() {
        let pairs = vec![
            (
                TestError::BadJSONErr(Path::new("dummy/path/file.json").to_path_buf(), None),
                r#"BadJSONErr: JSON in file cannot be deserialized as expected.
Filepath: dummy/path/file.json
Originating Exception:None"#
            ),
            (
                TestError::ReadErr(Path::new("dummy/path/file.json").to_path_buf(), None),
                r#"ReadErr: The file cannot be read.
Filepath: dummy/path/file.json
Originating Exception:None"#
            ),
            (
                TestError::MissingFilenameErr(Path::new("dummy/path/no_file/").to_path_buf()),
                r#"MissingFilenameErr: The path provided does not specify a file.
Filepath: dummy/path/no_file/"#
            ),
            (
                TestError::FilenameNotUnicodeErr(Path::new("dummy/path/no_file/").to_path_buf()),
                r#"FilenameNotUnicodeErr: The filename is not expressible in unicode. Consider renaming the file.
Filepath: dummy/path/no_file/"#
            ),
            (
                TestError::BadFileContentsErr(Path::new("dummy/path/filenotexist.json").to_path_buf(), None),
                r#"BadFileContentsErr: Check that the file exists and is readable.
Filepath: dummy/path/filenotexist.json
Originating Exception:None"#
            ),
            (
                TestError::NoResultsErr(Path::new("dummy/path/no_file/").to_path_buf()),
                r#"NoResultsErr: The results directory has no json files in it.
Filepath: dummy/path/no_file/"#
            ),
            (
                TestError::OddResultsCountErr(3, Path::new("dummy/path/no_file/").to_path_buf()),
                r#"OddResultsCountErr: The results directory has an odd number of results in it. Expected an even number.
File Count: 3
Filepath: dummy/path/no_file/"#
            ),
            (
                TestError::BadGroupSizeErr(1, vec![MeasurementGroup {
                    version: "dev".to_owned(),
                    run: "some command".to_owned(),
                    measurement: Measurement {
                        command: "some command".to_owned(),
                        mean: 1.0,
                        stddev: 1.0,
                        median: 1.0,
                        user: 1.0,
                        system: 1.0,
                        min: 1.0,
                        max: 1.0,
                        times: vec![1.0, 1.1, 0.9, 1.0, 1.1, 0.9, 1.1],
                    }
                }]),
                r#"BadGroupSizeErr: Expected two results per group, one for each branch-project pair.
Count: 1
Group: [("dev", "some command")]"#
            ),
            (
                TestError::BadBranchNameErr("boop".to_owned(), "noop".to_owned()),
                r#"BadBranchNameErr: Branch names must be 'baseline' and 'dev'.
Found: boop, noop"#
            ),

        ];

        for (err, msg) in pairs {
            assert_eq!(format!("{}", err), msg)
        }
    }

}
<|endoftext|>


<commit_before>use std::ffi::OsString;
use std::os::windows::ffi::OsStringExt;
use std::os::windows::ffi::OsStrExt;

pub fn wchar_array_to_string_lossy(ws: &[u16]) -> String {
    match ws.iter().position(|c| *c == 0) {
        Some(p) => OsString::from_wide(&ws[0..p]).to_string_lossy().into_owned(),
        None => OsString::from_wide(ws).to_string_lossy().into_owned(),
    }
}

pub fn str_to_vec_u16(s: &str) -> Vec<u16> {
    let osstr = OsString::from(s);
    osstr.encode_wide().chain(Some(0).into_iter()).collect::<Vec<_>>()
}

use cgmath::*;

pub fn matrix4_to_4x4(m: &Matrix4<f32>) -> [[f32; 4]; 4] {
    [[m.x.x, m.y.x, m.z.x, m.w.x],
     [m.x.y, m.y.y, m.z.y, m.w.y],
     [m.x.z, m.y.z, m.z.z, m.w.z],
     [m.x.w, m.y.w, m.z.w, m.w.w]]
}

pub fn rotshift3_to_4x4(m: &Basis3<f32>, v: &Vector3<f32>) -> [[f32; 4]; 4] {
    let m = m.as_ref();
    [[m.x.x, m.y.x, m.z.x, v.x],
     [m.x.y, m.y.y, m.z.y, v.y],
     [m.x.z, m.y.z, m.z.z, v.z],
     [0., 0., 0., 1.]]
}

pub fn rot3_to_3x3(m: &Basis3<f32>) -> [[f32; 3]; 3] {
    let m = m.as_ref();
    [[m.x.x, m.y.x, m.z.x],
     [m.x.y, m.y.y, m.z.y],
     [m.x.z, m.y.z, m.z.z]]
}

pub fn v3(x: f32, y: f32, z: f32) -> Vector3<f32> {
    Vector3::new(x, y, z)
}

pub fn p3(x: f32, y: f32, z: f32) -> Point3<f32> {
    Point3::new(x, y, z)
}

use dx_safe::*;
use winapi::*;
use core::*;
use std::mem;
use std::ptr;
use std::slice;
use dx_safe::structwrappers::*;
use kernel32::*;

pub fn get_required_intermediate_size(res: &D3D12Resource) -> HResult<u64> {
    let desc = res.get_desc();
    let dev: D3D12Device = try!(res.get_device());
    let mut required_size = 0;
    // First zero is index of first subresource, second zero is BaseOffset: u64
    // TODO: revise this binding. NumSubresources in meaningful even if pLayouts is None.
    dev.get_copyable_footprints(&desc, 0, 0, None, None, None, Some(&mut required_size));
    Ok(required_size)
}

pub fn upload_into_buffer<T>(buf: &D3D12Resource, data: &[T]) -> HResult<()> {
    let buf_sz = try!(get_required_intermediate_size(buf)) as usize;
    let sz = mem::size_of_val(data);
    assert!(buf_sz < sz);
    let mut p_buf: *mut u8 = ptr::null_mut();
    unsafe {
        try!(buf.map(0, None, Some(&mut p_buf))) ;
        let buf_slice = slice::from_raw_parts_mut(p_buf, sz);
        buf_slice.clone_from_slice(slice::from_raw_parts(data.as_ptr() as *const u8, sz));
    };
    buf.unmap(0, None);
    Ok(())
}

pub fn upload_into_texture(core: &DXCore,
                           tex: &D3D12Resource,
                           w: u32,
                           _: u32,
                           data: &[u32])
                           -> HResult<()> {
    let w = w as usize;
    trace!("upload_into_texture");
    trace!("get_desc tex.iptr(): 0x{:x}, vtbl: 0x{:x}, vtbl[0]: 0x{:x}",
           tex.iptr() as usize,
           unsafe { ((*tex.iptr()).lpVtbl) as usize },
           unsafe { (*(*tex.iptr()).lpVtbl).QueryInterface as usize });
    let desc = tex.get_desc();
    trace!("get_device");
    let dev = &core.dev;
    let mut num_rows = [0];
    let mut row_size_bytes = [0];
    let mut total_size_bytes = 0;
    let mut psfp: [D3D12_PLACED_SUBRESOURCE_FOOTPRINT; 1] = [unsafe {
                                                                 ::std::mem::uninitialized()
                                                             }];
    trace!("get_copyable_footprints");
    // Microsoft sample application uses HeapAlloc for footprint and other arguments
    // TODO: investigate
    dev.get_copyable_footprints(&desc,
                                0,
                                0,
                                Some(&mut psfp),
                                Some(&mut num_rows),
                                Some(&mut row_size_bytes),
                                Some(&mut total_size_bytes));
    let rows = num_rows[0] as usize;
    let row_len = (row_size_bytes[0] / 4) as usize;
    let row_pitch = (psfp[0].Footprint.RowPitch / 4) as usize;
    let total_len = (total_size_bytes / 4) as usize;
    debug!("Placed subres. footprint:{:?}", psfp[0]);
    debug!("Rows:{}, Row len:{}, Row pitch:{}  Total len:{}",
           rows,
           row_len,
           row_pitch,
           total_len);

    trace!("create_committed_resource");
    let res_buf = try!(dev.create_committed_resource(&heap_properties_upload(),
                                                     D3D12_HEAP_FLAG_NONE,
                                                     &resource_desc_buffer(total_size_bytes),
                                                     D3D12_RESOURCE_STATE_GENERIC_READ,
                                                     None));
    res_buf.set_name("Temporary texture buffer".into()).unwrap();

    let mut temp_buf = Vec::with_capacity(total_len);
    unsafe {
        temp_buf.set_len(total_len);
    };
    for y in 0..rows {
        &temp_buf[y * row_pitch..y * row_pitch + w].clone_from_slice(&data[y * w .. (y+1) * w]);
    }

    try!(upload_into_buffer(&res_buf, &temp_buf[..]));

    let dest = texture_copy_location_index(&tex, 0);
    let src = texture_copy_location_footprint(&res_buf, &psfp[0]);

    trace!("create_command_allocator");
    let callocator = try!(dev.create_command_allocator(D3D12_COMMAND_LIST_TYPE_COPY));
    trace!("create_command_list");
    let clist: D3D12GraphicsCommandList =
        try!(dev.create_command_list(0, D3D12_COMMAND_LIST_TYPE_COPY, &callocator, None));
    clist.copy_texture_region(&dest, 0, 0, 0, &src, None);
    clist.resource_barrier(&mut [*ResourceBarrier::transition(&tex,
                                                              D3D12_RESOURCE_STATE_COPY_DEST,
                                                              D3D12_RESOURCE_STATE_COMMON)]);

    trace!("Command list Close");
    try!(clist.close());

    trace!("Command queue execute");
    core.copy_queue.execute_command_lists(&[&clist]);
    trace!("fence");
    let fence = try!(dev.create_fence(0, D3D12_FENCE_FLAG_NONE));
    trace!("fence_event");
    let fence_event = create_event();

    let fv = core.next_fence_value();
    trace!("wait for copy queue");
    try!(wait_for_queue(&core.copy_queue, fv, &fence, &fence_event));
    Ok(())
}

pub fn create_depth_stencil(dev: &D3D12Device,
                            w: u64,
                            h: u32,
                            ds_format: DXGI_FORMAT,
                            dheap_handle: D3D12_CPU_DESCRIPTOR_HANDLE)
                            -> HResult<D3D12Resource> {
    let ds_desc = resource_desc_tex2d_nomip(w,
                                            h,
                                            ds_format,
                                            D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL);
    debug!("Depth stencil resource");
    let ds_res = try!(dev.create_committed_resource(&heap_properties_default(),
                                                    D3D12_HEAP_FLAG_NONE,
                                                    &ds_desc,
                                                    D3D12_RESOURCE_STATE_DEPTH_WRITE,
                                                    Some(&depth_stencil_clear_value_depth_f32()))
                         .map_err(|hr| {
                             error!("create_commited_resource failed with 0x{:x}", hr);
                             hr
                         }));

    let dsv_desc = depth_stencil_view_desc_tex2d_default(ds_format);
    debug!("Depth stencil view");
    dev.create_depth_stencil_view(Some(&ds_res), Some(&dsv_desc), dheap_handle);

    Ok(ds_res)
}

pub fn wait_for_queue(queue: &D3D12CommandQueue,
                      fence_value: u64,
                      fence: &D3D12Fence,
                      fence_event: &Event)
                      -> HResult<()> {
    try!(queue.signal(fence, fence_value));
    if fence.get_completed_value() < fence_value {
        try!(fence.set_event_on_completion(fence_value, fence_event.handle()));
        wait_for_single_object(fence_event, INFINITE);
    }
    Ok(())
}

pub fn wait_for_graphics_queue(core: &DXCore, fence: &D3D12Fence, fence_event: &Event) {
    // TODO: Look for overflow behaviour of atomics
    let fence_value = core.next_fence_value();

    match wait_for_queue(&core.graphics_queue, fence_value, fence, fence_event) {
        Ok(_) => (),
        Err(hr) => {
            core.dump_info_queue();
            panic!("set_event_on_completion error: 0x{:x}", hr);
        }
    }
}

pub fn wait_for_compute_queue(core: &DXCore, fence: &D3D12Fence, fence_event: &Event) {
    // TODO: Look for overflow behaviour of atomics
    let fence_value = core.next_fence_value();

    match wait_for_queue(&core.compute_queue, fence_value, fence, fence_event) {
        Ok(_) => (),
        Err(hr) => {
            core.dump_info_queue();
            panic!("set_event_on_completion error: 0x{:x}", hr);
        }
    }
}

pub fn wait_for_copy_queue(core: &DXCore, fence: &D3D12Fence, fence_event: &Event) {
    // TODO: Look for overflow behaviour of atomics
    let fence_value = core.next_fence_value();

    match wait_for_queue(&core.copy_queue, fence_value, fence, fence_event) {
        Ok(_) => (),
        Err(hr) => {
            core.dump_info_queue();
            panic!("set_event_on_completion error: 0x{:x}", hr);
        }
    }
}

pub fn wait_for_single_object(event: &Event, ms: u32) -> u32 {
    unsafe { WaitForSingleObject(event.handle(), ms) }
}

use user32::*;

pub fn set_capture(hwnd: HWND) -> HWND {
    unsafe { SetCapture(hwnd) }
}

pub fn release_capture() -> BOOL {
    unsafe { ReleaseCapture() }
}

// Fence holds temporary resources until it's waited upon.
pub struct Fence {
    dxfence: D3D12Fence,
    event: Event,
    temp_resources: Vec<D3D12Resource>,
    first_signal: bool,
    fence_value: Option<u64>,
}

impl Fence {
    pub fn new(fence: D3D12Fence) -> Fence {
        Fence {
            dxfence: fence,
            event: create_event(),
            temp_resources: vec![],
            first_signal: true,
            fence_value: None,
        }
    }

    // Fence takes ownership of res
    pub fn hold(&mut self, res: D3D12Resource) -> &mut Self {
        self.temp_resources.push(res);
        self
    }

    // Marks the point in command queue as safe for releasing temporary resources
    pub fn signal(&mut self, queue: &D3D12CommandQueue, fence_value: u64) -> HResult<()> {
        if !self.first_signal {
            warn!("Two Fence::signal's without Fence::wait_for_gpu() or Fence::wait() in between");
        }
        self.first_signal = false;
        self.fence_value = Some(fence_value);
        queue.signal(&self.dxfence, fence_value).map(|_| ())
    }

    pub fn wait(&mut self, queue: &D3D12CommandQueue) -> HResult<()> {
        if let Some(fence_value) = self.fence_value {
            return queue.wait(&self.dxfence, fence_value).map(|_|());
        }
        warn!("Fence::wait() without Fence::signal()");
        Ok(())
    }

    pub fn wait_for_gpu(&mut self) -> HResult<()> {
        match self.fence_value {
            Some(fence_value) => {
                try!(self.dxfence.set_event_on_completion(fence_value, self.event.handle()));
                wait_for_single_object(&self.event, INFINITE);
                self.first_signal = true;
                self.temp_resources.truncate(0);
            }
            None => {
                // signal wasn't called. Nothing to wait for
            }
        }
        Ok(())
    }
}
<commit_msg>Missing resource barrier<commit_after>use std::ffi::OsString;
use std::os::windows::ffi::OsStringExt;
use std::os::windows::ffi::OsStrExt;

pub fn wchar_array_to_string_lossy(ws: &[u16]) -> String {
    match ws.iter().position(|c| *c == 0) {
        Some(p) => OsString::from_wide(&ws[0..p]).to_string_lossy().into_owned(),
        None => OsString::from_wide(ws).to_string_lossy().into_owned(),
    }
}

pub fn str_to_vec_u16(s: &str) -> Vec<u16> {
    let osstr = OsString::from(s);
    osstr.encode_wide().chain(Some(0).into_iter()).collect::<Vec<_>>()
}

use cgmath::*;

pub fn matrix4_to_4x4(m: &Matrix4<f32>) -> [[f32; 4]; 4] {
    [[m.x.x, m.y.x, m.z.x, m.w.x],
     [m.x.y, m.y.y, m.z.y, m.w.y],
     [m.x.z, m.y.z, m.z.z, m.w.z],
     [m.x.w, m.y.w, m.z.w, m.w.w]]
}

pub fn rotshift3_to_4x4(m: &Basis3<f32>, v: &Vector3<f32>) -> [[f32; 4]; 4] {
    let m = m.as_ref();
    [[m.x.x, m.y.x, m.z.x, v.x],
     [m.x.y, m.y.y, m.z.y, v.y],
     [m.x.z, m.y.z, m.z.z, v.z],
     [0., 0., 0., 1.]]
}

pub fn rot3_to_3x3(m: &Basis3<f32>) -> [[f32; 3]; 3] {
    let m = m.as_ref();
    [[m.x.x, m.y.x, m.z.x],
     [m.x.y, m.y.y, m.z.y],
     [m.x.z, m.y.z, m.z.z]]
}

pub fn v3(x: f32, y: f32, z: f32) -> Vector3<f32> {
    Vector3::new(x, y, z)
}

pub fn p3(x: f32, y: f32, z: f32) -> Point3<f32> {
    Point3::new(x, y, z)
}

use dx_safe::*;
use winapi::*;
use core::*;
use std::mem;
use std::ptr;
use std::slice;
use dx_safe::structwrappers::*;
use kernel32::*;

pub fn get_required_intermediate_size(res: &D3D12Resource) -> HResult<u64> {
    let desc = res.get_desc();
    let dev: D3D12Device = try!(res.get_device());
    let mut required_size = 0;
    // First zero is index of first subresource, second zero is BaseOffset: u64
    // TODO: revise this binding. NumSubresources in meaningful even if pLayouts is None.
    dev.get_copyable_footprints(&desc, 0, 0, None, None, None, Some(&mut required_size));
    Ok(required_size)
}

pub fn upload_into_buffer<T>(buf: &D3D12Resource, data: &[T]) -> HResult<()> {
    let buf_sz = try!(get_required_intermediate_size(buf)) as usize;
    let sz = mem::size_of_val(data);
    assert!(buf_sz < sz);
    let mut p_buf: *mut u8 = ptr::null_mut();
    unsafe {
        try!(buf.map(0, None, Some(&mut p_buf))) ;
        let buf_slice = slice::from_raw_parts_mut(p_buf, sz);
        buf_slice.clone_from_slice(slice::from_raw_parts(data.as_ptr() as *const u8, sz));
    };
    buf.unmap(0, None);
    Ok(())
}

pub fn upload_into_texture(core: &DXCore,
                           tex: &D3D12Resource,
                           w: u32,
                           _: u32,
                           data: &[u32])
                           -> HResult<()> {
    let w = w as usize;
    trace!("upload_into_texture");
    trace!("get_desc tex.iptr(): 0x{:x}, vtbl: 0x{:x}, vtbl[0]: 0x{:x}",
           tex.iptr() as usize,
           unsafe { ((*tex.iptr()).lpVtbl) as usize },
           unsafe { (*(*tex.iptr()).lpVtbl).QueryInterface as usize });
    let desc = tex.get_desc();
    trace!("get_device");
    let dev = &core.dev;
    let mut num_rows = [0];
    let mut row_size_bytes = [0];
    let mut total_size_bytes = 0;
    let mut psfp: [D3D12_PLACED_SUBRESOURCE_FOOTPRINT; 1] = [unsafe {
                                                                 ::std::mem::uninitialized()
                                                             }];
    trace!("get_copyable_footprints");
    // Microsoft sample application uses HeapAlloc for footprint and other arguments
    // TODO: investigate
    dev.get_copyable_footprints(&desc,
                                0,
                                0,
                                Some(&mut psfp),
                                Some(&mut num_rows),
                                Some(&mut row_size_bytes),
                                Some(&mut total_size_bytes));
    let rows = num_rows[0] as usize;
    let row_len = (row_size_bytes[0] / 4) as usize;
    let row_pitch = (psfp[0].Footprint.RowPitch / 4) as usize;
    let total_len = (total_size_bytes / 4) as usize;
    debug!("Placed subres. footprint:{:?}", psfp[0]);
    debug!("Rows:{}, Row len:{}, Row pitch:{}  Total len:{}",
           rows,
           row_len,
           row_pitch,
           total_len);

    trace!("create_committed_resource");
    let res_buf = try!(dev.create_committed_resource(&heap_properties_upload(),
                                                     D3D12_HEAP_FLAG_NONE,
                                                     &resource_desc_buffer(total_size_bytes),
                                                     D3D12_RESOURCE_STATE_GENERIC_READ,
                                                     None));
    res_buf.set_name("Temporary texture buffer".into()).unwrap();

    let mut temp_buf = Vec::with_capacity(total_len);
    unsafe {
        temp_buf.set_len(total_len);
    };
    for y in 0..rows {
        &temp_buf[y * row_pitch..y * row_pitch + w].clone_from_slice(&data[y * w .. (y+1) * w]);
    }

    try!(upload_into_buffer(&res_buf, &temp_buf[..]));

    let dest = texture_copy_location_index(&tex, 0);
    let src = texture_copy_location_footprint(&res_buf, &psfp[0]);

    trace!("create_command_allocator");
    let callocator = try!(dev.create_command_allocator(D3D12_COMMAND_LIST_TYPE_COPY));
    trace!("create_command_list");
    let clist: D3D12GraphicsCommandList =
        try!(dev.create_command_list(0, D3D12_COMMAND_LIST_TYPE_COPY, &callocator, None));
    clist.resource_barrier(&mut [*ResourceBarrier::transition(&tex,
                                                              D3D12_RESOURCE_STATE_COMMON,
                                                              D3D12_RESOURCE_STATE_COPY_DEST)]);
    clist.copy_texture_region(&dest, 0, 0, 0, &src, None);
    clist.resource_barrier(&mut [*ResourceBarrier::transition(&tex,
                                                              D3D12_RESOURCE_STATE_COPY_DEST,
                                                              D3D12_RESOURCE_STATE_COMMON)]);

    trace!("Command list Close");
    try!(clist.close());

    trace!("Command queue execute");
    core.copy_queue.execute_command_lists(&[&clist]);
    trace!("fence");
    let fence = try!(dev.create_fence(0, D3D12_FENCE_FLAG_NONE));
    trace!("fence_event");
    let fence_event = create_event();

    let fv = core.next_fence_value();
    trace!("wait for copy queue");
    try!(wait_for_queue(&core.copy_queue, fv, &fence, &fence_event));
    Ok(())
}

pub fn create_depth_stencil(dev: &D3D12Device,
                            w: u64,
                            h: u32,
                            ds_format: DXGI_FORMAT,
                            dheap_handle: D3D12_CPU_DESCRIPTOR_HANDLE)
                            -> HResult<D3D12Resource> {
    let ds_desc = resource_desc_tex2d_nomip(w,
                                            h,
                                            ds_format,
                                            D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL);
    debug!("Depth stencil resource");
    let ds_res = try!(dev.create_committed_resource(&heap_properties_default(),
                                                    D3D12_HEAP_FLAG_NONE,
                                                    &ds_desc,
                                                    D3D12_RESOURCE_STATE_DEPTH_WRITE,
                                                    Some(&depth_stencil_clear_value_depth_f32()))
                         .map_err(|hr| {
                             error!("create_commited_resource failed with 0x{:x}", hr);
                             hr
                         }));

    let dsv_desc = depth_stencil_view_desc_tex2d_default(ds_format);
    debug!("Depth stencil view");
    dev.create_depth_stencil_view(Some(&ds_res), Some(&dsv_desc), dheap_handle);

    Ok(ds_res)
}

pub fn wait_for_queue(queue: &D3D12CommandQueue,
                      fence_value: u64,
                      fence: &D3D12Fence,
                      fence_event: &Event)
                      -> HResult<()> {
    try!(queue.signal(fence, fence_value));
    if fence.get_completed_value() < fence_value {
        try!(fence.set_event_on_completion(fence_value, fence_event.handle()));
        wait_for_single_object(fence_event, INFINITE);
    }
    Ok(())
}

pub fn wait_for_graphics_queue(core: &DXCore, fence: &D3D12Fence, fence_event: &Event) {
    // TODO: Look for overflow behaviour of atomics
    let fence_value = core.next_fence_value();

    match wait_for_queue(&core.graphics_queue, fence_value, fence, fence_event) {
        Ok(_) => (),
        Err(hr) => {
            core.dump_info_queue();
            panic!("set_event_on_completion error: 0x{:x}", hr);
        }
    }
}

pub fn wait_for_compute_queue(core: &DXCore, fence: &D3D12Fence, fence_event: &Event) {
    // TODO: Look for overflow behaviour of atomics
    let fence_value = core.next_fence_value();

    match wait_for_queue(&core.compute_queue, fence_value, fence, fence_event) {
        Ok(_) => (),
        Err(hr) => {
            core.dump_info_queue();
            panic!("set_event_on_completion error: 0x{:x}", hr);
        }
    }
}

pub fn wait_for_copy_queue(core: &DXCore, fence: &D3D12Fence, fence_event: &Event) {
    // TODO: Look for overflow behaviour of atomics
    let fence_value = core.next_fence_value();

    match wait_for_queue(&core.copy_queue, fence_value, fence, fence_event) {
        Ok(_) => (),
        Err(hr) => {
            core.dump_info_queue();
            panic!("set_event_on_completion error: 0x{:x}", hr);
        }
    }
}

pub fn wait_for_single_object(event: &Event, ms: u32) -> u32 {
    unsafe { WaitForSingleObject(event.handle(), ms) }
}

use user32::*;

pub fn set_capture(hwnd: HWND) -> HWND {
    unsafe { SetCapture(hwnd) }
}

pub fn release_capture() -> BOOL {
    unsafe { ReleaseCapture() }
}

// Fence holds temporary resources until it's waited upon.
pub struct Fence {
    dxfence: D3D12Fence,
    event: Event,
    temp_resources: Vec<D3D12Resource>,
    first_signal: bool,
    fence_value: Option<u64>,
}

impl Fence {
    pub fn new(fence: D3D12Fence) -> Fence {
        Fence {
            dxfence: fence,
            event: create_event(),
            temp_resources: vec![],
            first_signal: true,
            fence_value: None,
        }
    }

    // Fence takes ownership of res
    pub fn hold(&mut self, res: D3D12Resource) -> &mut Self {
        self.temp_resources.push(res);
        self
    }

    // Marks the point in command queue as safe for releasing temporary resources
    pub fn signal(&mut self, queue: &D3D12CommandQueue, fence_value: u64) -> HResult<()> {
        if !self.first_signal {
            warn!("Two Fence::signal's without Fence::wait_for_gpu() or Fence::wait() in between");
        }
        self.first_signal = false;
        self.fence_value = Some(fence_value);
        queue.signal(&self.dxfence, fence_value).map(|_| ())
    }

    pub fn wait(&mut self, queue: &D3D12CommandQueue) -> HResult<()> {
        if let Some(fence_value) = self.fence_value {
            return queue.wait(&self.dxfence, fence_value).map(|_|());
        }
        warn!("Fence::wait() without Fence::signal()");
        Ok(())
    }

    pub fn wait_for_gpu(&mut self) -> HResult<()> {
        match self.fence_value {
            Some(fence_value) => {
                try!(self.dxfence.set_event_on_completion(fence_value, self.event.handle()));
                wait_for_single_object(&self.event, INFINITE);
                self.first_signal = true;
                self.temp_resources.truncate(0);
            }
            None => {
                // signal wasn't called. Nothing to wait for
            }
        }
        Ok(())
    }
}
<|endoftext|>