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

use enums;
use parse;

pub fn write_descriptor_sets(doc: &parse::Spirv) -> String {
    // TODO: not implemented

    // finding all the descriptors
    let mut descriptors = Vec::new();
    struct Descriptor {
        name: String,
        desc_ty: String,
        bind_ty: String,
        bind_start: String,
        bind_end: String,
        set: u32,
        binding: u32,
    }

    // looping to find all the elements that have the `DescriptorSet` decoration
    for instruction in doc.instructions.iter() {
        let (variable_id, descriptor_set) = match instruction {
            &parse::Instruction::Decorate { target_id, decoration: enums::Decoration::DecorationDescriptorSet, ref params } => {
                (target_id, params[0])
            },
            _ => continue
        };

        // find which type is pointed to by this variable
        let pointed_ty = pointer_variable_ty(doc, variable_id);
        // name of the variable
        let name = ::name_from_id(doc, variable_id);

        // find the binding point of this descriptor
        let binding = doc.instructions.iter().filter_map(|i| {
            match i {
                &parse::Instruction::Decorate { target_id, decoration: enums::Decoration::DecorationBinding, ref params } if target_id == variable_id => {
                    Some(params[0])
                },
                _ => None,      // TODO: other types
            }
        }).next().expect(&format!("Uniform `{}` is missing a binding", name));

        // find informations about the kind of binding for this descriptor
        let (desc_ty, bind_ty, bind_start, bind_end) = doc.instructions.iter().filter_map(|i| {
            match i {
                &parse::Instruction::TypeStruct { result_id, .. } if result_id == pointed_ty => {
                    Some((
                        "::vulkano::descriptor_set::DescriptorType::UniformBuffer",
                        "::vulkano::buffer::AbstractBuffer",
                        "::vulkano::descriptor_set::DescriptorBind::UniformBuffer { buffer: ",
                        ", offset: 0, size: 128 /* FIXME */ }"
                    ))
                },
                &parse::Instruction::TypeImage { result_id, sampled_type_id, ref dim, arrayed, ms,
                                                 sampled, ref format, ref access, .. }
                                        if result_id == pointed_ty && sampled == Some(true) =>
                {
                    Some((
                        "::vulkano::descriptor_set::DescriptorType::SampledImage",
                        "::vulkano::image::AbstractImageView",
                        "::vulkano::descriptor_set::DescriptorBind::UniformBuffer",      // FIXME:
                        ""
                    ))
                },
                &parse::Instruction::TypeImage { result_id, sampled_type_id, ref dim, arrayed, ms,
                                                 sampled, ref format, ref access, .. }
                                        if result_id == pointed_ty && sampled == Some(false) =>
                {
                    Some((
                        "::vulkano::descriptor_set::DescriptorType::InputAttachment",       // FIXME: can be `StorageImage`
                        "::vulkano::image::AbstractImageView",
                        "::vulkano::descriptor_set::DescriptorBind::InputAttachment(",      // FIXME:
                        ", ::vulkano::image::Layout::ShaderReadOnlyOptimal)"     // FIXME:
                    ))
                },
                &parse::Instruction::TypeSampledImage { result_id, image_type_id }
                                                                    if result_id == pointed_ty =>
                {
                    Some((
                        "::vulkano::descriptor_set::DescriptorType::SampledImage",
                        "::vulkano::image::AbstractImageView",
                        "::vulkano::descriptor_set::DescriptorBind::UniformBuffer",      // FIXME:
                        ""
                    ))
                },
                _ => None,      // TODO: other types
            }
        }).next().expect(&format!("Couldn't find relevant type for uniform `{}` (type {}, maybe unimplemented)", name, pointed_ty));

        descriptors.push(Descriptor {
            name: name,
            desc_ty: desc_ty.to_owned(),
            bind_ty: bind_ty.to_owned(),
            bind_start: bind_start.to_owned(),
            bind_end: bind_end.to_owned(),
            set: descriptor_set,
            binding: binding,
        });
    }

    let sets_list = descriptors.iter().map(|d| d.set).collect::<HashSet<u32>>();

    let mut output = String::new();

    // iterate once per set that is defined somewhere
    for set in sets_list.iter() {
        let write_ty = descriptors.iter().filter(|d| d.set == *set)
                                  .map(|d| format!("::std::sync::Arc<{}>", d.bind_ty))
                                  .collect::<Vec<_>>();

        let writes = descriptors.iter().enumerate().filter(|&(_, d)| d.set == *set)
                                .map(|(entry, d)| {
                                    let entry = if write_ty.len() == 1 {
                                        "".to_owned()
                                    } else {
                                        format!(".{}", entry)
                                    };

                                    format!("::vulkano::descriptor_set::DescriptorWrite {{
                                                 binding: {binding},
                                                 array_element: 0,
                                                 content: {bind_start} data{entry} {bind_end},
                                             }}", binding = d.binding, bind_start = d.bind_start,
                                                  bind_end = d.bind_end,
                                                  entry = entry)
                                })
                                .collect::<Vec<_>>();

        let write_ty = if write_ty.len() == 1 {
            write_ty.into_iter().next().unwrap()
        } else {
            format!("({})", write_ty.join(", "))
        };

        let descr = descriptors.iter().enumerate().filter(|&(_, d)| d.set == *set)
                               .map(|(entry, d)| {
                                   format!("::vulkano::descriptor_set::DescriptorDesc {{
                                                binding: {binding},
                                                ty: {desc_ty},
                                                array_count: 1,
                                                stages: ::vulkano::descriptor_set::ShaderStages::all_graphics(),        // TODO:
                                            }}", binding = d.binding, desc_ty = d.desc_ty)
                               })
                               .collect::<Vec<_>>();

        output.push_str(&format!(r#"
#[derive(Default)]
pub struct Set{set};

unsafe impl ::vulkano::descriptor_set::SetLayout for Set{set} {{
    fn descriptors(&self) -> Vec<::vulkano::descriptor_set::DescriptorDesc> {{
        vec![
            {descr}
        ]
    }}
}}

unsafe impl ::vulkano::descriptor_set::SetLayoutWrite<{write_ty}> for Set{set} {{
    fn decode(&self, data: {write_ty}) -> Vec<::vulkano::descriptor_set::DescriptorWrite> {{
        vec![
            {writes}
        ]
    }}
}}

unsafe impl ::vulkano::descriptor_set::SetLayoutInit<{write_ty}> for Set{set} {{
    fn decode(&self, data: {write_ty}) -> Vec<::vulkano::descriptor_set::DescriptorWrite> {{
        ::vulkano::descriptor_set::SetLayoutWrite::decode(self, data)
    }}
}}

"#, set = set, write_ty = write_ty, writes = writes.join(","), descr = descr.join(",")));
    }

    let max_set = sets_list.iter().cloned().max().map(|v| v + 1).unwrap_or(0);

    let sets_defs = (0 .. max_set).map(|num| {
        if sets_list.contains(&num) {
            format!("::std::sync::Arc<::vulkano::descriptor_set::DescriptorSet<Set{}>>", num)
        } else {
            "()".to_owned()
        }
    }).collect::<Vec<_>>();

    let sets = (0 .. max_set).map(|num| {
        if sets_list.contains(&num) {
            if sets_defs.len() == 1 {
                format!("sets")
            } else {
                format!("sets.{}", num)
            }
        } else {
            "()".to_owned()     // FIXME: wrong
        }
    }).collect::<Vec<_>>();

    let layouts_defs = (0 .. max_set).map(|num| {
        if sets_list.contains(&num) {
            format!("::std::sync::Arc<::vulkano::descriptor_set::DescriptorSetLayout<Set{}>>", num)
        } else {
            "()".to_owned()
        }
    }).collect::<Vec<_>>();

    let layouts = (0 .. max_set).map(|num| {
        if sets_list.contains(&num) {
            if layouts_defs.len() == 1 {
                format!("layouts")
            } else {
                format!("layouts.{}", num)
            }
        } else {
            "()".to_owned()     // FIXME: wrong
        }
    }).collect::<Vec<_>>();

    output.push_str(&format!(r#"
#[derive(Default)]
pub struct Layout;

unsafe impl ::vulkano::descriptor_set::Layout for Layout {{
    type DescriptorSets = ({sets_defs});
    type DescriptorSetLayouts = ({layouts_defs});
    type PushConstants = ();

    fn decode_descriptor_set_layouts(&self, layouts: Self::DescriptorSetLayouts)
        -> Vec<::std::sync::Arc<::vulkano::descriptor_set::AbstractDescriptorSetLayout>>
    {{
        vec![
            {layouts}
        ]
    }}

    fn decode_descriptor_sets(&self, sets: Self::DescriptorSets)
        -> Vec<::std::sync::Arc<::vulkano::descriptor_set::AbstractDescriptorSet>>
    {{
        vec![
            {sets}
        ]
    }}
}}
"#, sets_defs = sets_defs.join(","), layouts_defs = layouts_defs.join(","),
    layouts = layouts.join(","), sets = sets.join(",")));

    output
}

/// Assumes that `variable` is a variable with a `TypePointer` and returns the id of the pointed
/// type.
fn pointer_variable_ty(doc: &parse::Spirv, variable: u32) -> u32 {
    let var_ty = doc.instructions.iter().filter_map(|i| {
        match i {
            &parse::Instruction::Variable { result_type_id, result_id, .. } if result_id == variable => {
                Some(result_type_id)
            },
            _ => None
        }
    }).next().unwrap();

    doc.instructions.iter().filter_map(|i| {
        match i {
            &parse::Instruction::TypePointer { result_id, type_id, .. } if result_id == var_ty => {
                Some(type_id)
            },
            _ => None
        }
    }).next().unwrap()
}
<commit_msg>Minor fix in shaders analyser<commit_after>use std::collections::HashSet;

use enums;
use parse;

pub fn write_descriptor_sets(doc: &parse::Spirv) -> String {
    // TODO: not implemented

    // finding all the descriptors
    let mut descriptors = Vec::new();
    struct Descriptor {
        name: String,
        desc_ty: String,
        bind_ty: String,
        bind_start: String,
        bind_end: String,
        set: u32,
        binding: u32,
    }

    // looping to find all the elements that have the `DescriptorSet` decoration
    for instruction in doc.instructions.iter() {
        let (variable_id, descriptor_set) = match instruction {
            &parse::Instruction::Decorate { target_id, decoration: enums::Decoration::DecorationDescriptorSet, ref params } => {
                (target_id, params[0])
            },
            _ => continue
        };

        // find which type is pointed to by this variable
        let pointed_ty = pointer_variable_ty(doc, variable_id);
        // name of the variable
        let name = ::name_from_id(doc, variable_id);

        // find the binding point of this descriptor
        let binding = doc.instructions.iter().filter_map(|i| {
            match i {
                &parse::Instruction::Decorate { target_id, decoration: enums::Decoration::DecorationBinding, ref params } if target_id == variable_id => {
                    Some(params[0])
                },
                _ => None,      // TODO: other types
            }
        }).next().expect(&format!("Uniform `{}` is missing a binding", name));

        // find informations about the kind of binding for this descriptor
        let (desc_ty, bind_ty, bind_start, bind_end) = doc.instructions.iter().filter_map(|i| {
            match i {
                &parse::Instruction::TypeStruct { result_id, .. } if result_id == pointed_ty => {
                    Some((
                        "::vulkano::descriptor_set::DescriptorType::UniformBuffer",
                        "::vulkano::buffer::AbstractBuffer",
                        "::vulkano::descriptor_set::DescriptorBind::UniformBuffer { buffer: ",
                        ", offset: 0, size: 128 /* FIXME */ }"
                    ))
                },
                &parse::Instruction::TypeImage { result_id, sampled_type_id, ref dim, arrayed, ms,
                                                 sampled, ref format, ref access, .. }
                                        if result_id == pointed_ty && sampled == Some(true) =>
                {
                    Some((
                        "::vulkano::descriptor_set::DescriptorType::SampledImage",
                        "::vulkano::image::AbstractImageView",
                        "::vulkano::descriptor_set::DescriptorBind::SampledImage(",
                        ", ::vulkano::image::Layout::ShaderReadOnlyOptimal)"      // FIXME:
                    ))
                },
                &parse::Instruction::TypeImage { result_id, sampled_type_id, ref dim, arrayed, ms,
                                                 sampled, ref format, ref access, .. }
                                        if result_id == pointed_ty && sampled == Some(false) =>
                {
                    Some((
                        "::vulkano::descriptor_set::DescriptorType::InputAttachment",       // FIXME: can be `StorageImage`
                        "::vulkano::image::AbstractImageView",
                        "::vulkano::descriptor_set::DescriptorBind::InputAttachment(",      // FIXME:
                        ", ::vulkano::image::Layout::ShaderReadOnlyOptimal)"     // FIXME:
                    ))
                },
                &parse::Instruction::TypeSampledImage { result_id, image_type_id }
                                                                    if result_id == pointed_ty =>
                {
                    Some((
                        "::vulkano::descriptor_set::DescriptorType::SampledImage",
                        "::vulkano::image::AbstractImageView",
                        "::vulkano::descriptor_set::DescriptorBind::SampledImage(",
                        ", ::vulkano::image::Layout::ShaderReadOnlyOptimal)"      // FIXME:
                    ))
                },
                _ => None,      // TODO: other types
            }
        }).next().expect(&format!("Couldn't find relevant type for uniform `{}` (type {}, maybe unimplemented)", name, pointed_ty));

        descriptors.push(Descriptor {
            name: name,
            desc_ty: desc_ty.to_owned(),
            bind_ty: bind_ty.to_owned(),
            bind_start: bind_start.to_owned(),
            bind_end: bind_end.to_owned(),
            set: descriptor_set,
            binding: binding,
        });
    }

    let sets_list = descriptors.iter().map(|d| d.set).collect::<HashSet<u32>>();

    let mut output = String::new();

    // iterate once per set that is defined somewhere
    for set in sets_list.iter() {
        let write_ty = descriptors.iter().filter(|d| d.set == *set)
                                  .map(|d| format!("::std::sync::Arc<{}>", d.bind_ty))
                                  .collect::<Vec<_>>();

        let writes = descriptors.iter().enumerate().filter(|&(_, d)| d.set == *set)
                                .map(|(entry, d)| {
                                    let entry = if write_ty.len() == 1 {
                                        "".to_owned()
                                    } else {
                                        format!(".{}", entry)
                                    };

                                    format!("::vulkano::descriptor_set::DescriptorWrite {{
                                                 binding: {binding},
                                                 array_element: 0,
                                                 content: {bind_start} data{entry} {bind_end},
                                             }}", binding = d.binding, bind_start = d.bind_start,
                                                  bind_end = d.bind_end,
                                                  entry = entry)
                                })
                                .collect::<Vec<_>>();

        let write_ty = if write_ty.len() == 1 {
            write_ty.into_iter().next().unwrap()
        } else {
            format!("({})", write_ty.join(", "))
        };

        let descr = descriptors.iter().enumerate().filter(|&(_, d)| d.set == *set)
                               .map(|(entry, d)| {
                                   format!("::vulkano::descriptor_set::DescriptorDesc {{
                                                binding: {binding},
                                                ty: {desc_ty},
                                                array_count: 1,
                                                stages: ::vulkano::descriptor_set::ShaderStages::all_graphics(),        // TODO:
                                            }}", binding = d.binding, desc_ty = d.desc_ty)
                               })
                               .collect::<Vec<_>>();

        output.push_str(&format!(r#"
#[derive(Default)]
pub struct Set{set};

unsafe impl ::vulkano::descriptor_set::SetLayout for Set{set} {{
    fn descriptors(&self) -> Vec<::vulkano::descriptor_set::DescriptorDesc> {{
        vec![
            {descr}
        ]
    }}
}}

unsafe impl ::vulkano::descriptor_set::SetLayoutWrite<{write_ty}> for Set{set} {{
    fn decode(&self, data: {write_ty}) -> Vec<::vulkano::descriptor_set::DescriptorWrite> {{
        vec![
            {writes}
        ]
    }}
}}

unsafe impl ::vulkano::descriptor_set::SetLayoutInit<{write_ty}> for Set{set} {{
    fn decode(&self, data: {write_ty}) -> Vec<::vulkano::descriptor_set::DescriptorWrite> {{
        ::vulkano::descriptor_set::SetLayoutWrite::decode(self, data)
    }}
}}

"#, set = set, write_ty = write_ty, writes = writes.join(","), descr = descr.join(",")));
    }

    let max_set = sets_list.iter().cloned().max().map(|v| v + 1).unwrap_or(0);

    let sets_defs = (0 .. max_set).map(|num| {
        if sets_list.contains(&num) {
            format!("::std::sync::Arc<::vulkano::descriptor_set::DescriptorSet<Set{}>>", num)
        } else {
            "()".to_owned()
        }
    }).collect::<Vec<_>>();

    let sets = (0 .. max_set).map(|num| {
        if sets_list.contains(&num) {
            if sets_defs.len() == 1 {
                format!("sets")
            } else {
                format!("sets.{}", num)
            }
        } else {
            "()".to_owned()     // FIXME: wrong
        }
    }).collect::<Vec<_>>();

    let layouts_defs = (0 .. max_set).map(|num| {
        if sets_list.contains(&num) {
            format!("::std::sync::Arc<::vulkano::descriptor_set::DescriptorSetLayout<Set{}>>", num)
        } else {
            "()".to_owned()
        }
    }).collect::<Vec<_>>();

    let layouts = (0 .. max_set).map(|num| {
        if sets_list.contains(&num) {
            if layouts_defs.len() == 1 {
                format!("layouts")
            } else {
                format!("layouts.{}", num)
            }
        } else {
            "()".to_owned()     // FIXME: wrong
        }
    }).collect::<Vec<_>>();

    output.push_str(&format!(r#"
#[derive(Default)]
pub struct Layout;

unsafe impl ::vulkano::descriptor_set::Layout for Layout {{
    type DescriptorSets = ({sets_defs});
    type DescriptorSetLayouts = ({layouts_defs});
    type PushConstants = ();

    fn decode_descriptor_set_layouts(&self, layouts: Self::DescriptorSetLayouts)
        -> Vec<::std::sync::Arc<::vulkano::descriptor_set::AbstractDescriptorSetLayout>>
    {{
        vec![
            {layouts}
        ]
    }}

    fn decode_descriptor_sets(&self, sets: Self::DescriptorSets)
        -> Vec<::std::sync::Arc<::vulkano::descriptor_set::AbstractDescriptorSet>>
    {{
        vec![
            {sets}
        ]
    }}
}}
"#, sets_defs = sets_defs.join(","), layouts_defs = layouts_defs.join(","),
    layouts = layouts.join(","), sets = sets.join(",")));

    output
}

/// Assumes that `variable` is a variable with a `TypePointer` and returns the id of the pointed
/// type.
fn pointer_variable_ty(doc: &parse::Spirv, variable: u32) -> u32 {
    let var_ty = doc.instructions.iter().filter_map(|i| {
        match i {
            &parse::Instruction::Variable { result_type_id, result_id, .. } if result_id == variable => {
                Some(result_type_id)
            },
            _ => None
        }
    }).next().unwrap();

    doc.instructions.iter().filter_map(|i| {
        match i {
            &parse::Instruction::TypePointer { result_id, type_id, .. } if result_id == var_ty => {
                Some(type_id)
            },
            _ => None
        }
    }).next().unwrap()
}
<|endoftext|>


<commit_before>// I think borrowed boxing is necessary for objekt::clone_box to work
#![allow(clippy::borrowed_box)]

//! UI event handling

use crate::cmd::{parse_cmd, CmdArgs, ParseCmdResult};
use crate::config;
use futures::stream::StreamExt;
use libtiny_client::Client;
use libtiny_ui::{MsgSource, MsgTarget, UI};
use std::path::{Path, PathBuf};
use tokio::sync::mpsc;

pub(crate) async fn task(
    config_path: PathBuf,
    defaults: config::Defaults,
    ui: Box<dyn UI>,
    mut clients: Vec<Client>,
    mut rcv_ev: mpsc::Receiver<libtiny_ui::Event>,
) {
    while let Some(ev) = rcv_ev.next().await {
        handle_input_ev(&config_path, &defaults, &ui, &mut clients, ev);
        ui.draw();
    }
}

fn handle_input_ev(
    config_path: &Path,
    defaults: &config::Defaults,
    ui: &Box<dyn UI>,
    clients: &mut Vec<Client>,
    ev: libtiny_ui::Event,
) {
    use libtiny_ui::Event::*;
    match ev {
        Abort => {
            for client in clients {
                client.quit(None);
            }
        }
        Msg { msg, source } => {
            send_msg(&**ui, clients, &source, msg, false);
        }
        Lines { lines, source } => {
            for line in lines.into_iter() {
                send_msg(&**ui, clients, &source, line, false)
            }
        }
        Cmd { cmd, source } => handle_cmd(config_path, defaults, ui, clients, source, &cmd),
    }
}

fn handle_cmd(
    config_path: &Path,
    defaults: &config::Defaults,
    ui: &Box<dyn UI>,
    clients: &mut Vec<Client>,
    src: MsgSource,
    cmd: &str,
) {
    match parse_cmd(cmd) {
        ParseCmdResult::Ok { cmd, rest } => {
            let cmd_args = CmdArgs {
                args: rest,
                config_path,
                defaults,
                ui,
                clients,
                src,
            };
            (cmd.cmd_fn)(cmd_args);
        }
        // ParseCmdResult::Ambiguous(vec) => {
        //     self.ui.add_client_err_msg(
        //         &format!("Unsupported command: \"/{}\"", msg),
        //         &MsgTarget::CurrentTab,
        //     );
        //     self.ui.add_client_err_msg(
        //         &format!("Did you mean one of {:?} ?", vec),
        //         &MsgTarget::CurrentTab,
        //     );
        // },
        ParseCmdResult::Unknown => ui.add_client_err_msg(
            &format!("Unsupported command: \"/{}\"", cmd),
            &MsgTarget::CurrentTab,
        ),
    }
}

// TODO: move this somewhere else
pub(crate) fn send_msg(
    ui: &dyn UI,
    clients: &mut Vec<Client>,
    src: &MsgSource,
    msg: String,
    is_action: bool,
) {
    eprintln!("src={:?}, msg={:?}, is_action={}", src, msg, is_action);

    if src.serv_name() == "mentions" {
        ui.add_client_err_msg(
            "Use `/connect <server>` to connect to a server",
            &MsgTarget::CurrentTab,
        );
        return;
    }

    let client = clients
        .iter_mut()
        .find(|client| client.get_serv_name() == src.serv_name())
        .unwrap();

    // TODO: For errors:
    //
    // ui.add_client_err_msg(
    //     &format!("Can't find server: {}", serv),
    //     &MsgTarget::CurrentTab,
    // );

    // `ui_target`: Where to show the message on ui
    // `msg_target`: Actual PRIVMSG target to send to the server
    let (ui_target, msg_target) = {
        match src {
            MsgSource::Serv { .. } => {
                // we don't split raw messages to 512-bytes long chunks
                client.raw_msg(&msg);
                return;
            }

            MsgSource::Chan { ref serv, ref chan } => (MsgTarget::Chan { serv, chan }, chan),

            MsgSource::User { ref serv, ref nick } => {
                let msg_target = if nick.eq_ignore_ascii_case("nickserv")
                    || nick.eq_ignore_ascii_case("chanserv")
                {
                    MsgTarget::Server { serv }
                } else {
                    MsgTarget::User { serv, nick }
                };
                (msg_target, nick)
            }
        }
    };

    let ts = time::now();
    let extra_len = msg_target.len()
        + if is_action {
            9 // "\0x1ACTION \0x1".len()
        } else {
            0
        };
    for msg in client.split_privmsg(extra_len, &msg) {
        client.privmsg(msg_target, msg, is_action);
        ui.add_privmsg(&client.get_nick(), msg, ts, &ui_target, false, is_action);
    }
}
<commit_msg>Remove accidentally committed debug print<commit_after>// I think borrowed boxing is necessary for objekt::clone_box to work
#![allow(clippy::borrowed_box)]

//! UI event handling

use crate::cmd::{parse_cmd, CmdArgs, ParseCmdResult};
use crate::config;
use futures::stream::StreamExt;
use libtiny_client::Client;
use libtiny_ui::{MsgSource, MsgTarget, UI};
use std::path::{Path, PathBuf};
use tokio::sync::mpsc;

pub(crate) async fn task(
    config_path: PathBuf,
    defaults: config::Defaults,
    ui: Box<dyn UI>,
    mut clients: Vec<Client>,
    mut rcv_ev: mpsc::Receiver<libtiny_ui::Event>,
) {
    while let Some(ev) = rcv_ev.next().await {
        handle_input_ev(&config_path, &defaults, &ui, &mut clients, ev);
        ui.draw();
    }
}

fn handle_input_ev(
    config_path: &Path,
    defaults: &config::Defaults,
    ui: &Box<dyn UI>,
    clients: &mut Vec<Client>,
    ev: libtiny_ui::Event,
) {
    use libtiny_ui::Event::*;
    match ev {
        Abort => {
            for client in clients {
                client.quit(None);
            }
        }
        Msg { msg, source } => {
            send_msg(&**ui, clients, &source, msg, false);
        }
        Lines { lines, source } => {
            for line in lines.into_iter() {
                send_msg(&**ui, clients, &source, line, false)
            }
        }
        Cmd { cmd, source } => handle_cmd(config_path, defaults, ui, clients, source, &cmd),
    }
}

fn handle_cmd(
    config_path: &Path,
    defaults: &config::Defaults,
    ui: &Box<dyn UI>,
    clients: &mut Vec<Client>,
    src: MsgSource,
    cmd: &str,
) {
    match parse_cmd(cmd) {
        ParseCmdResult::Ok { cmd, rest } => {
            let cmd_args = CmdArgs {
                args: rest,
                config_path,
                defaults,
                ui,
                clients,
                src,
            };
            (cmd.cmd_fn)(cmd_args);
        }
        // ParseCmdResult::Ambiguous(vec) => {
        //     self.ui.add_client_err_msg(
        //         &format!("Unsupported command: \"/{}\"", msg),
        //         &MsgTarget::CurrentTab,
        //     );
        //     self.ui.add_client_err_msg(
        //         &format!("Did you mean one of {:?} ?", vec),
        //         &MsgTarget::CurrentTab,
        //     );
        // },
        ParseCmdResult::Unknown => ui.add_client_err_msg(
            &format!("Unsupported command: \"/{}\"", cmd),
            &MsgTarget::CurrentTab,
        ),
    }
}

// TODO: move this somewhere else
pub(crate) fn send_msg(
    ui: &dyn UI,
    clients: &mut Vec<Client>,
    src: &MsgSource,
    msg: String,
    is_action: bool,
) {
    if src.serv_name() == "mentions" {
        ui.add_client_err_msg(
            "Use `/connect <server>` to connect to a server",
            &MsgTarget::CurrentTab,
        );
        return;
    }

    let client = clients
        .iter_mut()
        .find(|client| client.get_serv_name() == src.serv_name())
        .unwrap();

    // TODO: For errors:
    //
    // ui.add_client_err_msg(
    //     &format!("Can't find server: {}", serv),
    //     &MsgTarget::CurrentTab,
    // );

    // `ui_target`: Where to show the message on ui
    // `msg_target`: Actual PRIVMSG target to send to the server
    let (ui_target, msg_target) = {
        match src {
            MsgSource::Serv { .. } => {
                // we don't split raw messages to 512-bytes long chunks
                client.raw_msg(&msg);
                return;
            }

            MsgSource::Chan { ref serv, ref chan } => (MsgTarget::Chan { serv, chan }, chan),

            MsgSource::User { ref serv, ref nick } => {
                let msg_target = if nick.eq_ignore_ascii_case("nickserv")
                    || nick.eq_ignore_ascii_case("chanserv")
                {
                    MsgTarget::Server { serv }
                } else {
                    MsgTarget::User { serv, nick }
                };
                (msg_target, nick)
            }
        }
    };

    let ts = time::now();
    let extra_len = msg_target.len()
        + if is_action {
            9 // "\0x1ACTION \0x1".len()
        } else {
            0
        };
    for msg in client.split_privmsg(extra_len, &msg) {
        client.privmsg(msg_target, msg, is_action);
        ui.add_privmsg(&client.get_nick(), msg, ts, &ui_target, false, is_action);
    }
}
<|endoftext|>


<commit_before>/// List of material font icons. Use this to generate a font Icon.
// emacs: use C-x 8 Ret => #xf207 ("BUS"-Glyph)
// pub const FLOPPY_FONT_ICON: &str = "";
// pub const ADD_FONT_ICON: &str = "";
// pub const LOAD_FONT_ICON: &str = "";
// pub const SAVE_FONT_ICON: &str = "";
// pub const KEYBOARD_ARROW_LEFT_FONT_ICON: &str = "";
// pub const KEYBOARD_ARROW_RIGHT_FONT_ICON: &str = "";
// pub const REMOVE_ICON: &str = "";
// pub const ARROW_DOWN_ICON: &str = "";

// pub const MENU_FONT_ICON: &str = "";
// pub const SETTINGS_FONT_ICON: &str = "";
// pub const HELP_OUTLINE_FONT_ICON: &str = "";

// // category: Social
// pub const ANDROID_FONT_ICON: &str = "";
// pub const APPLE_FONT_ICON: &str = "";
// pub const GITHUB_FONT_ICON: &str = "";
// pub const GOOGLE_FONT_ICON: &str = "";
// pub const GOOGLE_DRIVE_FONT_ICON: &str = "";
// pub const GOOGLE_MAPS_FONT_ICON: &str = "";
// pub const LANGUAGE_JAVASCRIPT_FONT_ICON: &str = "";
// pub const LANGUAGE_HTML5_FONT_ICON: &str = "";
// pub const LANGUAGE_PYTHON_ICON: &str = "";
// pub const PAYPAL_FONT_ICON: &str = "";
// pub const POCKET_FONT_ICON: &str = "";
// pub const RSS_FONT_ICON: &str = "";
// pub const SHARE_FONT_ICON: &str = "";
// pub const STEAM_FONT_ICON: &str = "";
// pub const TWITTER_BOX_FONT_ICON: &str = "";
// pub const TWITTER_FONT_ICON: &str = "";
// pub const WIKIPEDIA_FONT_ICON: &str = "";
// pub const LINKEDIN_FONT_ICON: &str = "";
// pub const REDDIT_FONT_ICON: &str = "";
// pub const VIMEO_FONT_ICON: &str = "";
// pub const WINDOWS_FONT_ICON: &str = "";

// // category: Person
// pub const ACCOUNT_FONT_ICON: &str = "";
// pub const ACCOUNT_ADD_FONT_ICON: &str = "";
// pub const ACCOUNT_BOX_FONT_ICON: &str = "";
// pub const ACCOUNT_CIRCLE_FONT_ICON: &str = "";
// pub const ACCOUNTS_ADD_FONT_ICON: &str = "";
// pub const ACCOUNTS_FONT_ICON: &str = "";
// pub const MALE_FONT_ICON: &str = "";
// pub const FEMALE_FONT_ICON: &str = "";
// pub const MALE_FEMALE_FONT_ICON: &str = "";
// pub const RUN_FONT_ICON: &str = "";
// pub const WALK_FONT_ICON: &str = "";

// // category: File
// pub const CLOUD_FONT_ICON: &str = "";
// pub const DOWNLOAD_FONT_ICON: &str = "";
// pub const UPLOAD_FONT_ICON: &str = "";
// pub const FILE_FONT_ICON: &str = "";
// pub const FOLDER_FONT_ICON: &str = "";
// pub const FOLDER_OUTLINE_FONT_ICON: &str = "";
// pub const CLOUD_DONE_FONT_ICON: &str = "";
// pub const CLOUD_OFF_FONT_ICON: &str = "";
// pub const CLOUD_DOWNLOAD_FONT_ICON: &str = "";
// pub const CLOUD_OUTLINE_FONT_ICON: &str = "";
// pub const CLOUD_UPLOAD_FONT_ICON: &str = "";

// // category: Comment
// pub const COMMENT_FONT_ICON: &str = "";
// pub const COMMENT_LIST_FONT_ICON: &str = "";
// pub const COMMENT_IMAGE_FONT_ICON: &str = "";
// pub const COMMENT_EDIT_FONT_ICON: &str = "";
// pub const COMMENT_ALERT_FONT_ICON: &str = "";
// pub const COMMENT_MORE_FONT_ICON: &str = "";
// pub const COMMENT_OUTLINE_FONT_ICON: &str = "";
// pub const COMMENTS_FONT_ICON: &str = "";
// pub const COMMENT_VIDEO_TEXT_FONT_ICON: &str = "";
// pub const COMMENT_TEXT_FONT_ICON: &str = "";

// // category: Form
// pub const CHECK_ALL_FONT_ICON: &str = "";
// pub const CHECK_CIRCLE_U_FONT_ICON: &str = "";
// pub const CHECK_CIRCLE_FONT_ICON: &str = "";
// pub const CHECK_SQUARE_FONT_ICON: &str = "";
// pub const CHECK_FONT_ICON: &str = "";
// //pub const CHECK_FONT_ICON: &str = "";
// pub const CIRCLE_O_FONT_ICON: &str = "";
// pub const CIRCLE_FONT_ICON: &str = "";
// pub const DOT_CIRCLE_ALT_FONT_ICON: &str = "";
// pub const DOT_CIRCLE_FONT_ICON: &str = "";
// pub const MINUS_CIRCLE_OUTLINE_FONT_ICON: &str = "";
// pub const MINUS_CIRCLE_FONT_ICON: &str = "";
// pub const MINUS_SQUARE_FONT_ICON: &str = "";
// pub const MINUS_FONT_ICON: &str = "";
// //pub const MINUS_FONT_ICON: &str = "";
// pub const PLUS_CIRCLE_O_DUPLICATE_FONT_ICON: &str = "";
// pub const PLUS_CIRCLE_O_FONT_ICON: &str = "";
// pub const PLUS_CIRCLE_FONT_ICON: &str = "";
// pub const PLUS_SQUARE_FONT_ICON: &str = "";
// pub const PLUS_FONT_ICON: &str = "";
// pub const SQUARE_O_FONT_ICON: &str = "";
// pub const STAR_CIRCLE_FONT_ICON: &str = "";
// pub const STAR_HALF_FONT_ICON: &str = "";
// pub const STAR_OUTLINE_FONT_ICON: &str = "";
// pub const STAR_STAR_FONT_ICON: &str = "";

/// List of material font icons. Use this to generate a font Icon.
pub const FLOPPY_FONT_ICON: &str = "";
pub const CHECK_FONT_ICON: &str = "";
pub const ADD_FONT_ICON: &str = "";
pub const MINUS_FONT_ICON: &str = "";
pub const LOAD_FONT_ICON: &str = "";
pub const SAVE_FONT_ICON: &str = "";
pub const KEYBOARD_ARROW_LEFT_FONT_ICON: &str = "";
pub const KEYBOARD_ARROW_RIGHT_FONT_ICON: &str = "";
pub const REMOVE_ICON: &str = "";
pub const ARROW_DOWN_ICON: &str = "";<commit_msg>theme: vector_graphics: update material_icons_font_ttf.rs<commit_after>/// List of material font icons. Use this to generate a font Icon.

// This is kept for compability
// Please use meterial_icons_font.rs instead
pub const FLOPPY_FONT_ICON: &str = "";
pub const CHECK_FONT_ICON: &str = "";
pub const ADD_FONT_ICON: &str = "";
pub const MINUS_FONT_ICON: &str = "";
pub const LOAD_FONT_ICON: &str = "";
pub const SAVE_FONT_ICON: &str = "";
pub const KEYBOARD_ARROW_LEFT_FONT_ICON: &str = "";
pub const KEYBOARD_ARROW_RIGHT_FONT_ICON: &str = "";
pub const REMOVE_ICON: &str = "";
pub const ARROW_DOWN_ICON: &str = "";
<|endoftext|>


<commit_before>// Copyright 2020 Google LLC
//
// Use of this source code is governed by an MIT-style license that can be found
// in the LICENSE file or at https://opensource.org/licenses/MIT.

mod convert;

pub use convert::{FromLossy, IntoLossy};

pub fn convert<T, U>(item: T) -> U
where
    T: IntoLossy<U>,
{
    item.into_lossy()
}

include!(concat!(env!("OUT_DIR"), "/grr.rs"));

impl From<bool> for DataBlob {

    fn from(value: bool) -> DataBlob {
        DataBlob {
            boolean: Some(value),
            ..Default::default()
        }
    }
}

impl From<i64> for DataBlob {

    fn from(value: i64) -> DataBlob {
        DataBlob {
            integer: Some(value),
            ..Default::default()
        }
    }
}

impl From<f32> for DataBlob {

    fn from(value: f32) -> DataBlob {
        DataBlob {
            float: Some(value),
            ..Default::default()
        }
    }
}

impl From<Vec<u8>> for DataBlob {

    fn from(value: Vec<u8>) -> DataBlob {
        DataBlob {
            data: Some(value),
            ..Default::default()
        }
    }
}

impl From<String> for DataBlob {

    fn from(value: String) -> DataBlob  {
        DataBlob {
            string: Some(value),
            ..Default::default()
        }
    }
}

impl KeyValue {

    /// Creates an empty key-value.
    ///
    /// Both the key and the value are going to be equal to `None`.
    ///
    /// # Examples
    ///
    /// ```
    /// use rrg_proto::KeyValue;
    ///
    /// let entry = KeyValue::empty();
    /// assert_eq!(entry.k, None);
    /// assert_eq!(entry.v, None);
    /// ```
    pub fn empty() -> KeyValue {
        KeyValue {
            k: None,
            v: None,
        }
    }

    /// Creates a key-value pair.
    ///
    /// Both the key and the value are going to be equal to the given values.
    ///
    /// # Examples
    ///
    /// ```
    /// use rrg_proto::KeyValue;
    ///
    /// let entry = KeyValue::pair(String::from("foo"), 42i64);
    /// assert_eq!(entry.k.unwrap().string, Some(String::from("foo")));
    /// assert_eq!(entry.v.unwrap().integer, Some(42));
    /// ```
    pub fn pair<K, V>(key: K, value: V) -> KeyValue
    where
        K: Into<DataBlob>,
        V: Into<DataBlob>,
    {
        KeyValue {
            k: Some(key.into()),
            v: Some(value.into()),
        }
    }

    /// Creates a key-only key-value.
    ///
    /// The key is going to be equal to the given value and the key will be
    /// `None`.
    ///
    /// # Examples
    ///
    /// ```
    /// use rrg_proto::KeyValue;
    ///
    /// let entry = KeyValue::key(String::from("quux"));
    /// assert_eq!(entry.k.unwrap().string, Some(String::from("quux")));
    /// assert_eq!(entry.v, None);
    /// ```
    pub fn key<K>(key: K) -> KeyValue
    where
        K: Into<DataBlob>,
    {
        KeyValue {
            k: Some(key.into()),
            v: None,
        }
    }
}

impl std::iter::FromIterator<KeyValue> for AttributedDict {

    fn from_iter<I>(iter: I) -> AttributedDict
    where
        I: IntoIterator<Item = KeyValue>,
    {
        AttributedDict {
            dat: iter.into_iter().collect(),
        }
    }
}

impl<K, V> std::iter::FromIterator<(K, V)> for AttributedDict
where
    K: Into<DataBlob>,
    V: Into<DataBlob>,
{
    fn from_iter<I>(iter: I) -> AttributedDict
    where
        I: IntoIterator<Item = (K, V)>,
    {
        let pair = |(key, value)| KeyValue::pair(key, value);
        iter.into_iter().map(pair).collect()
    }
}

impl FromLossy<std::fs::Metadata> for StatEntry {

    fn from_lossy(metadata: std::fs::Metadata) -> StatEntry {
        use std::convert::{TryFrom, TryInto};
        #[cfg(target_family = "unix")]
        use std::os::unix::fs::MetadataExt;

        // TODO: Fix definition of `StatEntry`.
        // `StatEntry` defines insufficient integer width for some fields. For
        // now we just ignore errors, but the definition should be improved.
        let some = |value: u64| Some(value.try_into().unwrap_or(0));

        // TODO: Move this into some utility module.
        // TODO: Add support for other log types.
        macro_rules! ack {
            { $expr:expr, error: $message:literal } => {
                match $expr {
                    Ok(value) => Some(value),
                    Err(err) => {
                        ::log::error!(concat!($message, ": {}"), err);
                        None
                    },
                }
            };
        }

        let atime_secs = ack! {
            metadata.accessed(),
            error: "failed to obtain file access time"
        }.and_then(|atime| ack! {
            secs(atime),
            error: "failed to convert access time to seconds"
        });

        let mtime_secs = ack! {
            metadata.modified(),
            error: "failed to obtain file modification time"
        }.and_then(|mtime| ack! {
            secs(mtime),
            error: "failed to convert modification time to seconds"
        });

        let btime_secs = ack! {
            metadata.created(),
            error: "failed to obtain file creation time"
        }.and_then(|crtime| ack! {
            secs(crtime),
            error: "failed to convert creation time to seconds"
        });

        #[cfg(target_family = "unix")]
        let ctime_secs = ack! {
            u64::try_from(metadata.ctime()),
            error: "negative inode change time"
        };

        StatEntry {
            #[cfg(target_family = "unix")]
            st_mode: Some(metadata.mode().into()),
            #[cfg(target_family = "unix")]
            st_ino: some(metadata.ino()),
            #[cfg(target_family = "unix")]
            st_dev: some(metadata.dev()),
            #[cfg(target_family = "unix")]
            st_rdev: some(metadata.rdev()),
            #[cfg(target_family = "unix")]
            st_nlink: some(metadata.nlink()),
            #[cfg(target_family = "unix")]
            st_uid: Some(metadata.uid()),
            #[cfg(target_family = "unix")]
            st_gid: Some(metadata.gid()),
            st_size: Some(metadata.len()),
            st_atime: atime_secs,
            st_mtime: mtime_secs,
            #[cfg(target_family = "unix")]
            st_ctime: ctime_secs,
            st_crtime: btime_secs,
            #[cfg(target_family = "unix")]
            st_blocks: some(metadata.blocks()),
            #[cfg(target_family = "unix")]
            st_blksize: some(metadata.blksize()),
            ..Default::default()
        }
    }
}

/// An error type for failures of converting timestamps to microseconds.
#[derive(Clone, Debug)]
pub enum MicrosError {
    /// Attempted to convert pre-epoch system time.
    Epoch(std::time::SystemTimeError),
    /// Attempted to convert a value outside of 64-bit unsigned integer range.
    Overflow(std::num::TryFromIntError),
}

impl std::fmt::Display for MicrosError {

    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
        use MicrosError::*;

        match *self {
            Epoch(ref error) => {
                write!(fmt, "pre-epoch system time: {}", error)
            }
            Overflow(ref error) => {
                write!(fmt, "system time value too big: {}", error)
            }
        }
    }
}

impl std::error::Error for MicrosError {

    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        use MicrosError::*;

        match *self {
            Epoch(ref error) => Some(error),
            Overflow(ref error) => Some(error),
        }
    }
}

impl From<std::time::SystemTimeError> for MicrosError {

    fn from(error: std::time::SystemTimeError) -> MicrosError {
        MicrosError::Epoch(error)
    }
}

impl From<std::num::TryFromIntError> for MicrosError {

    fn from(error: std::num::TryFromIntError) -> MicrosError {
        MicrosError::Overflow(error)
    }
}

/// An error type for failures of converting timestamps to seconds.
#[derive(Clone, Debug)]
pub struct SecsError {
    error: MicrosError,
}

impl std::fmt::Display for SecsError {

    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
        self.error.fmt(fmt)
    }
}

impl From<MicrosError> for SecsError {

    fn from(error: MicrosError) -> SecsError {
        SecsError {
            error: error,
        }
    }
}

impl From<SecsError> for MicrosError {

    fn from(error: SecsError) -> MicrosError {
        error.error
    }
}

/// Converts system time into epoch microseconds.
///
/// Because most GRR messages use epoch microseconds for representing timestamps
/// this function be be useful to convert from more idiomatic representations.
///
/// # Examples
///
/// ```
/// use rrg_proto::micros;
///
/// assert_eq!(micros(std::time::UNIX_EPOCH).unwrap(), 0);
/// ```
pub fn micros(time: std::time::SystemTime) -> Result<u64, MicrosError> {
    let time_micros = time.duration_since(std::time::UNIX_EPOCH)?.as_micros();
    Ok(std::convert::TryInto::try_into(time_micros)?)
}

/// Converts system time into epoch seconds.
///
/// Some GRR messages use epoch seconds for representing timestamps. In such
/// cases this function can be useful to convert from more idiomatic types.
///
/// # Examples
///
/// ```
/// use rrg_proto::secs;
///
/// assert_eq!(secs(std::time::UNIX_EPOCH).unwrap(), 0);
/// ```
pub fn secs(time: std::time::SystemTime) -> Result<u64, SecsError> {
    Ok(micros(time)? / 1_000_000)
}
<commit_msg>Expose the conversion module.<commit_after>// Copyright 2020 Google LLC
//
// Use of this source code is governed by an MIT-style license that can be found
// in the LICENSE file or at https://opensource.org/licenses/MIT.

pub mod convert;

use convert::{FromLossy, IntoLossy};

pub fn convert<T, U>(item: T) -> U
where
    T: IntoLossy<U>,
{
    item.into_lossy()
}

include!(concat!(env!("OUT_DIR"), "/grr.rs"));

impl From<bool> for DataBlob {

    fn from(value: bool) -> DataBlob {
        DataBlob {
            boolean: Some(value),
            ..Default::default()
        }
    }
}

impl From<i64> for DataBlob {

    fn from(value: i64) -> DataBlob {
        DataBlob {
            integer: Some(value),
            ..Default::default()
        }
    }
}

impl From<f32> for DataBlob {

    fn from(value: f32) -> DataBlob {
        DataBlob {
            float: Some(value),
            ..Default::default()
        }
    }
}

impl From<Vec<u8>> for DataBlob {

    fn from(value: Vec<u8>) -> DataBlob {
        DataBlob {
            data: Some(value),
            ..Default::default()
        }
    }
}

impl From<String> for DataBlob {

    fn from(value: String) -> DataBlob  {
        DataBlob {
            string: Some(value),
            ..Default::default()
        }
    }
}

impl KeyValue {

    /// Creates an empty key-value.
    ///
    /// Both the key and the value are going to be equal to `None`.
    ///
    /// # Examples
    ///
    /// ```
    /// use rrg_proto::KeyValue;
    ///
    /// let entry = KeyValue::empty();
    /// assert_eq!(entry.k, None);
    /// assert_eq!(entry.v, None);
    /// ```
    pub fn empty() -> KeyValue {
        KeyValue {
            k: None,
            v: None,
        }
    }

    /// Creates a key-value pair.
    ///
    /// Both the key and the value are going to be equal to the given values.
    ///
    /// # Examples
    ///
    /// ```
    /// use rrg_proto::KeyValue;
    ///
    /// let entry = KeyValue::pair(String::from("foo"), 42i64);
    /// assert_eq!(entry.k.unwrap().string, Some(String::from("foo")));
    /// assert_eq!(entry.v.unwrap().integer, Some(42));
    /// ```
    pub fn pair<K, V>(key: K, value: V) -> KeyValue
    where
        K: Into<DataBlob>,
        V: Into<DataBlob>,
    {
        KeyValue {
            k: Some(key.into()),
            v: Some(value.into()),
        }
    }

    /// Creates a key-only key-value.
    ///
    /// The key is going to be equal to the given value and the key will be
    /// `None`.
    ///
    /// # Examples
    ///
    /// ```
    /// use rrg_proto::KeyValue;
    ///
    /// let entry = KeyValue::key(String::from("quux"));
    /// assert_eq!(entry.k.unwrap().string, Some(String::from("quux")));
    /// assert_eq!(entry.v, None);
    /// ```
    pub fn key<K>(key: K) -> KeyValue
    where
        K: Into<DataBlob>,
    {
        KeyValue {
            k: Some(key.into()),
            v: None,
        }
    }
}

impl std::iter::FromIterator<KeyValue> for AttributedDict {

    fn from_iter<I>(iter: I) -> AttributedDict
    where
        I: IntoIterator<Item = KeyValue>,
    {
        AttributedDict {
            dat: iter.into_iter().collect(),
        }
    }
}

impl<K, V> std::iter::FromIterator<(K, V)> for AttributedDict
where
    K: Into<DataBlob>,
    V: Into<DataBlob>,
{
    fn from_iter<I>(iter: I) -> AttributedDict
    where
        I: IntoIterator<Item = (K, V)>,
    {
        let pair = |(key, value)| KeyValue::pair(key, value);
        iter.into_iter().map(pair).collect()
    }
}

impl FromLossy<std::fs::Metadata> for StatEntry {

    fn from_lossy(metadata: std::fs::Metadata) -> StatEntry {
        use std::convert::{TryFrom, TryInto};
        #[cfg(target_family = "unix")]
        use std::os::unix::fs::MetadataExt;

        // TODO: Fix definition of `StatEntry`.
        // `StatEntry` defines insufficient integer width for some fields. For
        // now we just ignore errors, but the definition should be improved.
        let some = |value: u64| Some(value.try_into().unwrap_or(0));

        // TODO: Move this into some utility module.
        // TODO: Add support for other log types.
        macro_rules! ack {
            { $expr:expr, error: $message:literal } => {
                match $expr {
                    Ok(value) => Some(value),
                    Err(err) => {
                        ::log::error!(concat!($message, ": {}"), err);
                        None
                    },
                }
            };
        }

        let atime_secs = ack! {
            metadata.accessed(),
            error: "failed to obtain file access time"
        }.and_then(|atime| ack! {
            secs(atime),
            error: "failed to convert access time to seconds"
        });

        let mtime_secs = ack! {
            metadata.modified(),
            error: "failed to obtain file modification time"
        }.and_then(|mtime| ack! {
            secs(mtime),
            error: "failed to convert modification time to seconds"
        });

        let btime_secs = ack! {
            metadata.created(),
            error: "failed to obtain file creation time"
        }.and_then(|crtime| ack! {
            secs(crtime),
            error: "failed to convert creation time to seconds"
        });

        #[cfg(target_family = "unix")]
        let ctime_secs = ack! {
            u64::try_from(metadata.ctime()),
            error: "negative inode change time"
        };

        StatEntry {
            #[cfg(target_family = "unix")]
            st_mode: Some(metadata.mode().into()),
            #[cfg(target_family = "unix")]
            st_ino: some(metadata.ino()),
            #[cfg(target_family = "unix")]
            st_dev: some(metadata.dev()),
            #[cfg(target_family = "unix")]
            st_rdev: some(metadata.rdev()),
            #[cfg(target_family = "unix")]
            st_nlink: some(metadata.nlink()),
            #[cfg(target_family = "unix")]
            st_uid: Some(metadata.uid()),
            #[cfg(target_family = "unix")]
            st_gid: Some(metadata.gid()),
            st_size: Some(metadata.len()),
            st_atime: atime_secs,
            st_mtime: mtime_secs,
            #[cfg(target_family = "unix")]
            st_ctime: ctime_secs,
            st_crtime: btime_secs,
            #[cfg(target_family = "unix")]
            st_blocks: some(metadata.blocks()),
            #[cfg(target_family = "unix")]
            st_blksize: some(metadata.blksize()),
            ..Default::default()
        }
    }
}

/// An error type for failures of converting timestamps to microseconds.
#[derive(Clone, Debug)]
pub enum MicrosError {
    /// Attempted to convert pre-epoch system time.
    Epoch(std::time::SystemTimeError),
    /// Attempted to convert a value outside of 64-bit unsigned integer range.
    Overflow(std::num::TryFromIntError),
}

impl std::fmt::Display for MicrosError {

    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
        use MicrosError::*;

        match *self {
            Epoch(ref error) => {
                write!(fmt, "pre-epoch system time: {}", error)
            }
            Overflow(ref error) => {
                write!(fmt, "system time value too big: {}", error)
            }
        }
    }
}

impl std::error::Error for MicrosError {

    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        use MicrosError::*;

        match *self {
            Epoch(ref error) => Some(error),
            Overflow(ref error) => Some(error),
        }
    }
}

impl From<std::time::SystemTimeError> for MicrosError {

    fn from(error: std::time::SystemTimeError) -> MicrosError {
        MicrosError::Epoch(error)
    }
}

impl From<std::num::TryFromIntError> for MicrosError {

    fn from(error: std::num::TryFromIntError) -> MicrosError {
        MicrosError::Overflow(error)
    }
}

/// An error type for failures of converting timestamps to seconds.
#[derive(Clone, Debug)]
pub struct SecsError {
    error: MicrosError,
}

impl std::fmt::Display for SecsError {

    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
        self.error.fmt(fmt)
    }
}

impl From<MicrosError> for SecsError {

    fn from(error: MicrosError) -> SecsError {
        SecsError {
            error: error,
        }
    }
}

impl From<SecsError> for MicrosError {

    fn from(error: SecsError) -> MicrosError {
        error.error
    }
}

/// Converts system time into epoch microseconds.
///
/// Because most GRR messages use epoch microseconds for representing timestamps
/// this function be be useful to convert from more idiomatic representations.
///
/// # Examples
///
/// ```
/// use rrg_proto::micros;
///
/// assert_eq!(micros(std::time::UNIX_EPOCH).unwrap(), 0);
/// ```
pub fn micros(time: std::time::SystemTime) -> Result<u64, MicrosError> {
    let time_micros = time.duration_since(std::time::UNIX_EPOCH)?.as_micros();
    Ok(std::convert::TryInto::try_into(time_micros)?)
}

/// Converts system time into epoch seconds.
///
/// Some GRR messages use epoch seconds for representing timestamps. In such
/// cases this function can be useful to convert from more idiomatic types.
///
/// # Examples
///
/// ```
/// use rrg_proto::secs;
///
/// assert_eq!(secs(std::time::UNIX_EPOCH).unwrap(), 0);
/// ```
pub fn secs(time: std::time::SystemTime) -> Result<u64, SecsError> {
    Ok(micros(time)? / 1_000_000)
}
<|endoftext|>


<commit_before>extern crate chrono;
extern crate elf;
extern crate getopts;
extern crate tar;

use getopts::Options;
use std::{fs, path};
use std::cmp;
use std::env;
use std::fmt::Write as fmtwrite;
use std::io;
use std::io::{Seek, Write};
use std::mem;

#[macro_use]
mod util;
mod header;

fn main() {
    let args: Vec<String> = env::args().collect();
    let program = args[0].clone();
    let mut opts = Options::new();
    opts.reqopt("o", "", "set output file name", "OUTFILE");
    opts.optopt("n", "", "set package name", "PACKAGE_NAME");
    opts.reqopt("", "stack", "set stack size in bytes", "STACK_SIZE");
    opts.reqopt(
        "",
        "app-heap",
        "set app heap size in bytes",
        "APP_HEAP_SIZE",
    );
    opts.reqopt(
        "",
        "kernel-heap",
        "set kernel heap size in bytes",
        "KERNEL_HEAP_SIZE",
    );
    opts.optflag("", "crt0-header", "include crt0 header for PIC fixups");
    opts.optflag("v", "verbose", "be verbose");

    let matches = match opts.parse(&args[1..]) {
        Ok(m) => m,
        Err(f) => panic!(f.to_string()),
    };

    let output = matches.opt_str("o");
    let package_name = matches.opt_str("n");
    let verbose = matches.opt_present("v");

    // Get the memory requirements from the app.
    let stack_len = matches
        .opt_str("stack")
        .unwrap()
        .parse::<u32>()
        .expect("Stack size must be an integer.");
    let app_heap_len = matches
        .opt_str("app-heap")
        .unwrap()
        .parse::<u32>()
        .expect("App heap size must be an integer.");
    let kernel_heap_len = matches
        .opt_str("kernel-heap")
        .unwrap()
        .parse::<u32>()
        .expect("Kernel heap size must be an integer.");

    // Check that we have at least one input file elf to process.
    if matches.free.is_empty() {
        print_usage(&program, opts);
        return;
    };

    // Create the metadata.toml file needed for the TAB file.
    let mut metadata_toml = String::new();
    write!(
        &mut metadata_toml,
        "tab-version = 1
name = \"{}\"
only-for-boards = \"\"
build-date = {}",
        package_name.clone().unwrap(),
        chrono::prelude::Utc::now().to_rfc3339_opts(chrono::SecondsFormat::Secs, true)
    ).unwrap();

    // Start creating a tar archive which will be the .tab file.
    let tab_name = fs::File::create(path::Path::new(&output.unwrap()))
        .expect("Could not create the output file.");
    let mut tab = tar::Builder::new(tab_name);

    // Add the metadata file without creating a real file on the filesystem.
    let mut header = tar::Header::new_gnu();
    header.set_size(metadata_toml.as_bytes().len() as u64);
    header.set_mode(0o644);
    header.set_cksum();
    tab.append_data(&mut header, "metadata.toml", metadata_toml.as_bytes())
        .unwrap();

    // Iterate all input elfs. Convert them to Tock friendly binaries and then
    // add them to the TAB file.
    for input_elf in matches.free {
        let elf_path = path::Path::new(&input_elf);
        let bin_path = path::Path::new(&input_elf).with_extension("bin");

        let elffile = elf::File::open_path(&elf_path).expect("Could not open the .elf file.");
        // Get output file as both read/write for creating the binary and
        // adding it to the TAB tar file.
        let mut outfile: fs::File = fs::OpenOptions::new()
            .read(true)
            .write(true)
            .create(true)
            .truncate(true)
            .open(bin_path.clone())
            .unwrap();

        // Do the conversion to a tock binary.
        elf_to_tbf(
            &elffile,
            &mut outfile,
            package_name.clone(),
            verbose,
            stack_len,
            app_heap_len,
            kernel_heap_len,
        ).unwrap();

        // Add the file to the TAB tar file.
        outfile.seek(io::SeekFrom::Start(0)).unwrap();
        tab.append_file(bin_path.file_name().unwrap(), &mut outfile)
            .unwrap();
    }
}

fn print_usage(program: &str, opts: Options) {
    let brief = format!("Usage: {} [-o OUTFILE] FILE [FILE...]", program);
    print!("{}", opts.usage(&brief));
}

/// Convert an ELF file to a TBF (Tock Binary Format) binary file.
///
/// This will place all writeable and executable sections from the ELF file
/// into a binary and prepend a TBF header to it. For all writeable sections,
/// if there is a .rel.X section it will be included at the end with a 32 bit
/// length parameter first.
///
/// Assumptions:
/// - Sections in a segment that is RW and set to be loaded will be in RAM and
///   should count towards minimum required RAM.
/// - Sections that are writeable flash regions include .wfr in their name.
fn elf_to_tbf(
    input: &elf::File,
    output: &mut Write,
    package_name: Option<String>,
    verbose: bool,
    stack_len: u32,
    app_heap_len: u32,
    kernel_heap_len: u32,
) -> io::Result<()> {
    let package_name = package_name.unwrap_or(String::new());

    // Get an array of the sections sorted so we place them in the proper order
    // in the binary.
    let mut sections_sort: Vec<(usize, usize)> = Vec::new();
    for (i, section) in input.sections.iter().enumerate() {
        sections_sort.push((i, section.shdr.offset as usize));
    }
    sections_sort.sort_by_key(|s| s.1);

    // Keep track of how much RAM this app will need.
    let mut minimum_ram_size: u32 = 0;

    // Find the ELF segment for the RAM segment. That will tell us how much
    // RAM we need to reserve for when those are copied into memory.
    for segment in input.phdrs.iter() {
        if segment.progtype == elf::types::PT_LOAD
            && segment.flags.0 == elf::types::PF_W.0 + elf::types::PF_R.0
        {
            minimum_ram_size = segment.memsz as u32;
            break;
        }
    }
    if verbose {
        println!(
            "Min RAM size from sections in ELF: {} bytes",
            minimum_ram_size
        );
    }

    // Add in room the app is asking us to reserve for the stack and heaps to
    // the minimum required RAM size.
    minimum_ram_size += align8!(stack_len) + align4!(app_heap_len) + align4!(kernel_heap_len);

    // Need an array of sections to look for relocation data to include.
    let mut rel_sections: Vec<String> = Vec::new();

    // Iterate the sections in the ELF file to find properties of the app that
    // are required to go in the TBF header.
    let mut writeable_flash_regions_count = 0;

    for s in sections_sort.iter() {
        let section = &input.sections[s.0];

        // Count write only sections as writeable flash regions.
        if section.shdr.name.contains(".wfr") && section.shdr.size > 0 {
            writeable_flash_regions_count += 1;
        }

        // Check write+alloc sections for possible .rel.X sections.
        if section.shdr.flags.0 == elf::types::SHF_WRITE.0 + elf::types::SHF_ALLOC.0 {
            // This section is also one we might need to include relocation
            // data for.
            rel_sections.push(section.shdr.name.clone());
        }
    }
    if verbose {
        println!(
            "Number of writeable flash regions: {}",
            writeable_flash_regions_count
        );
    }

    // Keep track of an index of where we are in creating the app binary.
    let mut binary_index = 0;

    // Now we can create the first pass TBF header. This is mostly to get the
    // size of the header since we have to fill in some of the offsets later.
    let mut tbfheader = header::TbfHeader::new();
    let header_length = tbfheader.create(
        minimum_ram_size,
        writeable_flash_regions_count,
        package_name,
    );
    let protected_region_size = header_length;
    binary_index += protected_region_size;

    // The init function is where the app will start executing, defined as an
    // offset from the end of protected region at the beginning of the app in
    // flash. Typically the protected region only includes the TBF header. To
    // calculate the offset we need to find which section includes the entry
    // function and then determine its offset relative to the end of the
    // protected region.
    let mut init_fn_offset: u32 = 0;

    // Need a place to put the app sections before we know the true TBF header.
    let mut binary: Vec<u8> = Vec::new();

    // Iterate the sections in the ELF file and add them to the binary as needed
    for s in sections_sort.iter() {
        let section = &input.sections[s.0];

        // Determine if this is the section where the entry point is in. If it
        // is, then we need to calculate the correct init_fn_offset.
        if input.ehdr.entry >= section.shdr.addr
            && input.ehdr.entry < (section.shdr.addr + section.shdr.size)
        {
            if verbose {
                println!("Entry point is in {} section", section.shdr.name);
            }
            init_fn_offset = (input.ehdr.entry - section.shdr.addr) as u32
                + (binary_index - protected_region_size) as u32
        }

        // If this is writeable, executable, or allocated, is nonzero length,
        // and is type `PROGBITS` we want to add it to the binary.
        if (section.shdr.flags.0
            & (elf::types::SHF_WRITE.0 + elf::types::SHF_EXECINSTR.0 + elf::types::SHF_ALLOC.0)
            != 0) && section.shdr.shtype == elf::types::SHT_PROGBITS
            && section.shdr.size > 0
        {
            if verbose {
                println!(
                    "Including the {} section at offset {} (0x{:x})",
                    section.shdr.name, binary_index, binary_index
                );
            }
            if align4needed!(binary_index) != 0 {
                println!(
                    "Warning! Placing section {} at 0x{:x}, which is not 4-byte aligned.",
                    section.shdr.name, binary_index
                );
            }
            binary.extend(&section.data);

            // Check if this is a writeable flash region. If so, we need to
            // set the offset and size in the header.
            if section.shdr.name.contains(".wfr") && section.shdr.size > 0 {
                tbfheader.set_writeable_flash_region_values(
                    binary_index as u32,
                    section.shdr.size as u32,
                );
            }

            // Now increment where we are in the binary.
            binary_index += section.shdr.size as usize;
        }
    }

    // Now that we have checked all of the sections, we can set the
    // init_fn_offset.
    tbfheader.set_init_fn_offset(init_fn_offset);

    // Next we have to add in any relocation data.
    let mut relocation_binary: Vec<u8> = Vec::new();

    // For each section that might have relocation data, check if a .rel.X
    // section exists and if so include it.
    if verbose {
        println!("Searching for .rel.X sections to add.");
    }
    for relocation_section_name in rel_sections.iter() {
        let mut name: String = ".rel".to_owned();
        name.push_str(relocation_section_name);

        let rel_data = input
            .sections
            .iter()
            .find(|section| section.shdr.name == name)
            .map(|section| section.data.as_ref())
            .unwrap_or(&[] as &[u8]);

        relocation_binary.extend(rel_data);

        if verbose && rel_data.len() > 0 {
            println!(
                "  Adding {} section. Length: {} bytes at {} (0x{:x})",
                name,
                rel_data.len(),
                binary_index + mem::size_of::<u32>() + rel_data.len(),
                binary_index + mem::size_of::<u32>() + rel_data.len()
            );
        }
        if rel_data.len() > 0 && align4needed!(binary_index) != 0 {
            println!(
                "Warning! Placing section {} at 0x{:x}, which is not 4-byte aligned.",
                name, binary_index
            );
        }
    }

    // Add the relocation data to our total length. Also include the 4 bytes for
    // the relocation data length.
    binary_index += relocation_binary.len() + mem::size_of::<u32>();

    // That is everything that we are going to include in our app binary. Now
    // we need to pad the binary to a power of 2 in size, and make sure it is
    // at least 512 bytes in size.
    let post_content_pad = if binary_index.count_ones() > 1 {
        let power2len = cmp::max(1 << (32 - (binary_index as u32).leading_zeros()), 512);
        power2len - binary_index
    } else {
        0
    };
    binary_index += post_content_pad;
    let total_size = binary_index;

    // Now set the total size of the app in the header.
    tbfheader.set_total_size(total_size as u32);

    if verbose {
        print!("{}", tbfheader);
    }

    // Write the header and actual app to a binary file.
    output.write_all(tbfheader.generate().unwrap().get_ref())?;
    output.write_all(binary.as_ref())?;

    let rel_data_len: [u8; 4] = [
        (relocation_binary.len() & 0xff) as u8,
        (relocation_binary.len() >> 8 & 0xff) as u8,
        (relocation_binary.len() >> 16 & 0xff) as u8,
        (relocation_binary.len() >> 24 & 0xff) as u8,
    ];
    output.write_all(&rel_data_len)?;
    output.write_all(relocation_binary.as_ref())?;

    // Pad to get a power of 2 sized flash app.
    util::do_pad(output, post_content_pad as usize)?;

    Ok(())
}
<commit_msg>Compile processes to .tbf extension not .bin<commit_after>extern crate chrono;
extern crate elf;
extern crate getopts;
extern crate tar;

use getopts::Options;
use std::{fs, path};
use std::cmp;
use std::env;
use std::fmt::Write as fmtwrite;
use std::io;
use std::io::{Seek, Write};
use std::mem;

#[macro_use]
mod util;
mod header;

fn main() {
    let args: Vec<String> = env::args().collect();
    let program = args[0].clone();
    let mut opts = Options::new();
    opts.reqopt("o", "", "set output file name", "OUTFILE");
    opts.optopt("n", "", "set package name", "PACKAGE_NAME");
    opts.reqopt("", "stack", "set stack size in bytes", "STACK_SIZE");
    opts.reqopt(
        "",
        "app-heap",
        "set app heap size in bytes",
        "APP_HEAP_SIZE",
    );
    opts.reqopt(
        "",
        "kernel-heap",
        "set kernel heap size in bytes",
        "KERNEL_HEAP_SIZE",
    );
    opts.optflag("", "crt0-header", "include crt0 header for PIC fixups");
    opts.optflag("v", "verbose", "be verbose");

    let matches = match opts.parse(&args[1..]) {
        Ok(m) => m,
        Err(f) => panic!(f.to_string()),
    };

    let output = matches.opt_str("o");
    let package_name = matches.opt_str("n");
    let verbose = matches.opt_present("v");

    // Get the memory requirements from the app.
    let stack_len = matches
        .opt_str("stack")
        .unwrap()
        .parse::<u32>()
        .expect("Stack size must be an integer.");
    let app_heap_len = matches
        .opt_str("app-heap")
        .unwrap()
        .parse::<u32>()
        .expect("App heap size must be an integer.");
    let kernel_heap_len = matches
        .opt_str("kernel-heap")
        .unwrap()
        .parse::<u32>()
        .expect("Kernel heap size must be an integer.");

    // Check that we have at least one input file elf to process.
    if matches.free.is_empty() {
        print_usage(&program, opts);
        return;
    };

    // Create the metadata.toml file needed for the TAB file.
    let mut metadata_toml = String::new();
    write!(
        &mut metadata_toml,
        "tab-version = 1
name = \"{}\"
only-for-boards = \"\"
build-date = {}",
        package_name.clone().unwrap(),
        chrono::prelude::Utc::now().to_rfc3339_opts(chrono::SecondsFormat::Secs, true)
    ).unwrap();

    // Start creating a tar archive which will be the .tab file.
    let tab_name = fs::File::create(path::Path::new(&output.unwrap()))
        .expect("Could not create the output file.");
    let mut tab = tar::Builder::new(tab_name);

    // Add the metadata file without creating a real file on the filesystem.
    let mut header = tar::Header::new_gnu();
    header.set_size(metadata_toml.as_bytes().len() as u64);
    header.set_mode(0o644);
    header.set_cksum();
    tab.append_data(&mut header, "metadata.toml", metadata_toml.as_bytes())
        .unwrap();

    // Iterate all input elfs. Convert them to Tock friendly binaries and then
    // add them to the TAB file.
    for input_elf in matches.free {
        let elf_path = path::Path::new(&input_elf);
        let tbf_path = path::Path::new(&input_elf).with_extension("tbf");

        let elffile = elf::File::open_path(&elf_path).expect("Could not open the .elf file.");
        // Get output file as both read/write for creating the binary and
        // adding it to the TAB tar file.
        let mut outfile: fs::File = fs::OpenOptions::new()
            .read(true)
            .write(true)
            .create(true)
            .truncate(true)
            .open(tbf_path.clone())
            .unwrap();

        // Do the conversion to a tock binary.
        elf_to_tbf(
            &elffile,
            &mut outfile,
            package_name.clone(),
            verbose,
            stack_len,
            app_heap_len,
            kernel_heap_len,
        ).unwrap();

        // Add the file to the TAB tar file.
        outfile.seek(io::SeekFrom::Start(0)).unwrap();
        tab.append_file(tbf_path.file_name().unwrap(), &mut outfile)
            .unwrap();
        outfile.seek(io::SeekFrom::Start(0)).unwrap();
        tab.append_file(
            tbf_path.with_extension("bin").file_name().unwrap(),
            &mut outfile,
        ).unwrap();
    }
}

fn print_usage(program: &str, opts: Options) {
    let brief = format!("Usage: {} [-o OUTFILE] FILE [FILE...]", program);
    print!("{}", opts.usage(&brief));
}

/// Convert an ELF file to a TBF (Tock Binary Format) binary file.
///
/// This will place all writeable and executable sections from the ELF file
/// into a binary and prepend a TBF header to it. For all writeable sections,
/// if there is a .rel.X section it will be included at the end with a 32 bit
/// length parameter first.
///
/// Assumptions:
/// - Sections in a segment that is RW and set to be loaded will be in RAM and
///   should count towards minimum required RAM.
/// - Sections that are writeable flash regions include .wfr in their name.
fn elf_to_tbf(
    input: &elf::File,
    output: &mut Write,
    package_name: Option<String>,
    verbose: bool,
    stack_len: u32,
    app_heap_len: u32,
    kernel_heap_len: u32,
) -> io::Result<()> {
    let package_name = package_name.unwrap_or(String::new());

    // Get an array of the sections sorted so we place them in the proper order
    // in the binary.
    let mut sections_sort: Vec<(usize, usize)> = Vec::new();
    for (i, section) in input.sections.iter().enumerate() {
        sections_sort.push((i, section.shdr.offset as usize));
    }
    sections_sort.sort_by_key(|s| s.1);

    // Keep track of how much RAM this app will need.
    let mut minimum_ram_size: u32 = 0;

    // Find the ELF segment for the RAM segment. That will tell us how much
    // RAM we need to reserve for when those are copied into memory.
    for segment in input.phdrs.iter() {
        if segment.progtype == elf::types::PT_LOAD
            && segment.flags.0 == elf::types::PF_W.0 + elf::types::PF_R.0
        {
            minimum_ram_size = segment.memsz as u32;
            break;
        }
    }
    if verbose {
        println!(
            "Min RAM size from sections in ELF: {} bytes",
            minimum_ram_size
        );
    }

    // Add in room the app is asking us to reserve for the stack and heaps to
    // the minimum required RAM size.
    minimum_ram_size += align8!(stack_len) + align4!(app_heap_len) + align4!(kernel_heap_len);

    // Need an array of sections to look for relocation data to include.
    let mut rel_sections: Vec<String> = Vec::new();

    // Iterate the sections in the ELF file to find properties of the app that
    // are required to go in the TBF header.
    let mut writeable_flash_regions_count = 0;

    for s in sections_sort.iter() {
        let section = &input.sections[s.0];

        // Count write only sections as writeable flash regions.
        if section.shdr.name.contains(".wfr") && section.shdr.size > 0 {
            writeable_flash_regions_count += 1;
        }

        // Check write+alloc sections for possible .rel.X sections.
        if section.shdr.flags.0 == elf::types::SHF_WRITE.0 + elf::types::SHF_ALLOC.0 {
            // This section is also one we might need to include relocation
            // data for.
            rel_sections.push(section.shdr.name.clone());
        }
    }
    if verbose {
        println!(
            "Number of writeable flash regions: {}",
            writeable_flash_regions_count
        );
    }

    // Keep track of an index of where we are in creating the app binary.
    let mut binary_index = 0;

    // Now we can create the first pass TBF header. This is mostly to get the
    // size of the header since we have to fill in some of the offsets later.
    let mut tbfheader = header::TbfHeader::new();
    let header_length = tbfheader.create(
        minimum_ram_size,
        writeable_flash_regions_count,
        package_name,
    );
    let protected_region_size = header_length;
    binary_index += protected_region_size;

    // The init function is where the app will start executing, defined as an
    // offset from the end of protected region at the beginning of the app in
    // flash. Typically the protected region only includes the TBF header. To
    // calculate the offset we need to find which section includes the entry
    // function and then determine its offset relative to the end of the
    // protected region.
    let mut init_fn_offset: u32 = 0;

    // Need a place to put the app sections before we know the true TBF header.
    let mut binary: Vec<u8> = Vec::new();

    // Iterate the sections in the ELF file and add them to the binary as needed
    for s in sections_sort.iter() {
        let section = &input.sections[s.0];

        // Determine if this is the section where the entry point is in. If it
        // is, then we need to calculate the correct init_fn_offset.
        if input.ehdr.entry >= section.shdr.addr
            && input.ehdr.entry < (section.shdr.addr + section.shdr.size)
        {
            if verbose {
                println!("Entry point is in {} section", section.shdr.name);
            }
            init_fn_offset = (input.ehdr.entry - section.shdr.addr) as u32
                + (binary_index - protected_region_size) as u32
        }

        // If this is writeable, executable, or allocated, is nonzero length,
        // and is type `PROGBITS` we want to add it to the binary.
        if (section.shdr.flags.0
            & (elf::types::SHF_WRITE.0 + elf::types::SHF_EXECINSTR.0 + elf::types::SHF_ALLOC.0)
            != 0) && section.shdr.shtype == elf::types::SHT_PROGBITS
            && section.shdr.size > 0
        {
            if verbose {
                println!(
                    "Including the {} section at offset {} (0x{:x})",
                    section.shdr.name, binary_index, binary_index
                );
            }
            if align4needed!(binary_index) != 0 {
                println!(
                    "Warning! Placing section {} at 0x{:x}, which is not 4-byte aligned.",
                    section.shdr.name, binary_index
                );
            }
            binary.extend(&section.data);

            // Check if this is a writeable flash region. If so, we need to
            // set the offset and size in the header.
            if section.shdr.name.contains(".wfr") && section.shdr.size > 0 {
                tbfheader.set_writeable_flash_region_values(
                    binary_index as u32,
                    section.shdr.size as u32,
                );
            }

            // Now increment where we are in the binary.
            binary_index += section.shdr.size as usize;
        }
    }

    // Now that we have checked all of the sections, we can set the
    // init_fn_offset.
    tbfheader.set_init_fn_offset(init_fn_offset);

    // Next we have to add in any relocation data.
    let mut relocation_binary: Vec<u8> = Vec::new();

    // For each section that might have relocation data, check if a .rel.X
    // section exists and if so include it.
    if verbose {
        println!("Searching for .rel.X sections to add.");
    }
    for relocation_section_name in rel_sections.iter() {
        let mut name: String = ".rel".to_owned();
        name.push_str(relocation_section_name);

        let rel_data = input
            .sections
            .iter()
            .find(|section| section.shdr.name == name)
            .map(|section| section.data.as_ref())
            .unwrap_or(&[] as &[u8]);

        relocation_binary.extend(rel_data);

        if verbose && rel_data.len() > 0 {
            println!(
                "  Adding {} section. Length: {} bytes at {} (0x{:x})",
                name,
                rel_data.len(),
                binary_index + mem::size_of::<u32>() + rel_data.len(),
                binary_index + mem::size_of::<u32>() + rel_data.len()
            );
        }
        if rel_data.len() > 0 && align4needed!(binary_index) != 0 {
            println!(
                "Warning! Placing section {} at 0x{:x}, which is not 4-byte aligned.",
                name, binary_index
            );
        }
    }

    // Add the relocation data to our total length. Also include the 4 bytes for
    // the relocation data length.
    binary_index += relocation_binary.len() + mem::size_of::<u32>();

    // That is everything that we are going to include in our app binary. Now
    // we need to pad the binary to a power of 2 in size, and make sure it is
    // at least 512 bytes in size.
    let post_content_pad = if binary_index.count_ones() > 1 {
        let power2len = cmp::max(1 << (32 - (binary_index as u32).leading_zeros()), 512);
        power2len - binary_index
    } else {
        0
    };
    binary_index += post_content_pad;
    let total_size = binary_index;

    // Now set the total size of the app in the header.
    tbfheader.set_total_size(total_size as u32);

    if verbose {
        print!("{}", tbfheader);
    }

    // Write the header and actual app to a binary file.
    output.write_all(tbfheader.generate().unwrap().get_ref())?;
    output.write_all(binary.as_ref())?;

    let rel_data_len: [u8; 4] = [
        (relocation_binary.len() & 0xff) as u8,
        (relocation_binary.len() >> 8 & 0xff) as u8,
        (relocation_binary.len() >> 16 & 0xff) as u8,
        (relocation_binary.len() >> 24 & 0xff) as u8,
    ];
    output.write_all(&rel_data_len)?;
    output.write_all(relocation_binary.as_ref())?;

    // Pad to get a power of 2 sized flash app.
    util::do_pad(output, post_content_pad as usize)?;

    Ok(())
}
<|endoftext|>


<commit_before>use std::marker::PhantomData;
use std::mem;
use std::ops::Range;
use std::sync::Arc;
use std::sync::Mutex;
use std::sync::atomic::AtomicBool;
use std::sync::atomic::Ordering;
use smallvec::SmallVec;

use buffer::traits::Buffer;
use buffer::unsafe_buffer::UnsafeBuffer;
use buffer::unsafe_buffer::Usage;
use command_buffer::Submission;
use device::Device;
use instance::QueueFamily;
use memory::DeviceMemory;
use memory::MappedDeviceMemory;
use sync::Sharing;

use OomError;

pub struct ImmutableBuffer<T: ?Sized> {
    // Inner content.
    inner: UnsafeBuffer,

    memory: MappedDeviceMemory,

    // Queue families allowed to access this buffer.
    queue_families: SmallVec<[u32; 4]>,

    latest_write_submission: Mutex<Option<Arc<Submission>>>,

    started_reading: AtomicBool,

    marker: PhantomData<*const T>,
}

impl<T> ImmutableBuffer<T> {
    pub unsafe fn raw<'a, I>(device: &Arc<Device>, size: usize, usage: &Usage, queue_families: I)
                             -> Result<Arc<ImmutableBuffer<T>>, OomError>
        where I: IntoIterator<Item = QueueFamily<'a>>
    {
        let queue_families = queue_families.into_iter().map(|f| f.id())
                                           .collect::<SmallVec<[u32; 4]>>();

        let (buffer, mem_reqs) = {
            let sharing = if queue_families.len() >= 2 {
                Sharing::Concurrent(queue_families.iter().cloned())
            } else {
                Sharing::Exclusive
            };

            try!(UnsafeBuffer::new(device, size, &usage, sharing))
        };

        let mem_ty = {
            let device_local = device.physical_device().memory_types()
                                     .filter(|t| (mem_reqs.memory_type_bits & (1 << t.id())) != 0)
                                     .filter(|t| t.is_device_local());
            let any = device.physical_device().memory_types()
                            .filter(|t| (mem_reqs.memory_type_bits & (1 << t.id())) != 0);
            device_local.chain(any).next().unwrap()
        };

        // note: alignment doesn't need to be checked because allocating memory is guaranteed to
        //       fulfill any alignment requirement

        let mem = try!(DeviceMemory::alloc_and_map(device, &mem_ty, mem_reqs.size));

        try!(buffer.bind_memory(mem.memory(), 0 .. mem_reqs.size));

        Ok(Arc::new(ImmutableBuffer {
            inner: buffer,
            memory: mem,
            queue_families: queue_families,
            latest_write_submission: Mutex::new(None),
            started_reading: AtomicBool::new(false),
            marker: PhantomData,
        }))
    }
}

unsafe impl<T: ?Sized> Buffer for ImmutableBuffer<T> {
    #[inline]
    fn inner_buffer(&self) -> &UnsafeBuffer {
        &self.inner
    }

    fn needs_fence(&self, _: bool, _: Range<usize>) -> Option<bool> {
        Some(true)
    }

    unsafe fn gpu_access(&self, write: bool, _: Range<usize>, submission: &Arc<Submission>)
                         -> Vec<Arc<Submission>>
    {
        let queue_id = submission.queue().family().id();
        if self.queue_families.iter().find(|&&id| id == queue_id).is_none() {
            panic!()
        }

        if write {
            assert!(self.started_reading.load(Ordering::AcqRel) == false);
        }

        let dependency = {
            let mut latest_submission = self.latest_write_submission.lock().unwrap();

            if write {
                mem::replace(&mut *latest_submission, Some(submission.clone()))
            } else {
                latest_submission.clone()
            }
        };

        if write {
            assert!(self.started_reading.load(Ordering::AcqRel) == false);
        } else {
            self.started_reading.store(true, Ordering::AcqRel);
        }

        if let Some(dependency) = dependency {
            vec![dependency]
        } else {
            vec![]
        }
    }
}
<commit_msg>Fix constructors of ImmutableBuffer<commit_after>use std::marker::PhantomData;
use std::mem;
use std::ops::Range;
use std::sync::Arc;
use std::sync::Mutex;
use std::sync::atomic::AtomicBool;
use std::sync::atomic::Ordering;
use smallvec::SmallVec;

use buffer::traits::Buffer;
use buffer::unsafe_buffer::UnsafeBuffer;
use buffer::unsafe_buffer::Usage;
use command_buffer::Submission;
use device::Device;
use instance::QueueFamily;
use memory::DeviceMemory;
use memory::MappedDeviceMemory;
use sync::Sharing;

use OomError;

pub struct ImmutableBuffer<T: ?Sized> {
    // Inner content.
    inner: UnsafeBuffer,

    memory: MappedDeviceMemory,

    // Queue families allowed to access this buffer.
    queue_families: SmallVec<[u32; 4]>,

    latest_write_submission: Mutex<Option<Arc<Submission>>>,

    started_reading: AtomicBool,

    marker: PhantomData<*const T>,
}

impl<T> ImmutableBuffer<T> {
    #[inline]
    pub fn new<'a, I>(device: &Arc<Device>, usage: &Usage, queue_families: I)
                      -> Result<Arc<ImmutableBuffer<T>>, OomError>
        where I: IntoIterator<Item = QueueFamily<'a>>
    {
        unsafe {
            ImmutableBuffer::raw(device, mem::size_of::<T>(), usage, queue_families)
        }
    }
}

impl<T> ImmutableBuffer<[T]> {
    #[inline]
    pub fn array<'a, I>(device: &Arc<Device>, len: usize, usage: &Usage, queue_families: I)
                      -> Result<Arc<ImmutableBuffer<T>>, OomError>
        where I: IntoIterator<Item = QueueFamily<'a>>
    {
        unsafe {
            ImmutableBuffer::raw(device, len * mem::size_of::<T>(), usage, queue_families)
        }
    }
}

impl<T: ?Sized> ImmutableBuffer<T> {
    pub unsafe fn raw<'a, I>(device: &Arc<Device>, size: usize, usage: &Usage, queue_families: I)
                             -> Result<Arc<ImmutableBuffer<T>>, OomError>
        where I: IntoIterator<Item = QueueFamily<'a>>
    {
        let queue_families = queue_families.into_iter().map(|f| f.id())
                                           .collect::<SmallVec<[u32; 4]>>();

        let (buffer, mem_reqs) = {
            let sharing = if queue_families.len() >= 2 {
                Sharing::Concurrent(queue_families.iter().cloned())
            } else {
                Sharing::Exclusive
            };

            try!(UnsafeBuffer::new(device, size, &usage, sharing))
        };

        let mem_ty = {
            let device_local = device.physical_device().memory_types()
                                     .filter(|t| (mem_reqs.memory_type_bits & (1 << t.id())) != 0)
                                     .filter(|t| t.is_device_local());
            let any = device.physical_device().memory_types()
                            .filter(|t| (mem_reqs.memory_type_bits & (1 << t.id())) != 0);
            device_local.chain(any).next().unwrap()
        };

        // note: alignment doesn't need to be checked because allocating memory is guaranteed to
        //       fulfill any alignment requirement

        let mem = try!(DeviceMemory::alloc_and_map(device, &mem_ty, mem_reqs.size));

        try!(buffer.bind_memory(mem.memory(), 0 .. mem_reqs.size));

        Ok(Arc::new(ImmutableBuffer {
            inner: buffer,
            memory: mem,
            queue_families: queue_families,
            latest_write_submission: Mutex::new(None),
            started_reading: AtomicBool::new(false),
            marker: PhantomData,
        }))
    }
}

unsafe impl<T: ?Sized> Buffer for ImmutableBuffer<T> {
    #[inline]
    fn inner_buffer(&self) -> &UnsafeBuffer {
        &self.inner
    }

    fn needs_fence(&self, _: bool, _: Range<usize>) -> Option<bool> {
        Some(true)
    }

    unsafe fn gpu_access(&self, write: bool, _: Range<usize>, submission: &Arc<Submission>)
                         -> Vec<Arc<Submission>>
    {
        let queue_id = submission.queue().family().id();
        if self.queue_families.iter().find(|&&id| id == queue_id).is_none() {
            panic!()
        }

        if write {
            assert!(self.started_reading.load(Ordering::AcqRel) == false);
        }

        let dependency = {
            let mut latest_submission = self.latest_write_submission.lock().unwrap();

            if write {
                mem::replace(&mut *latest_submission, Some(submission.clone()))
            } else {
                latest_submission.clone()
            }
        };

        if write {
            assert!(self.started_reading.load(Ordering::AcqRel) == false);
        } else {
            self.started_reading.store(true, Ordering::AcqRel);
        }

        if let Some(dependency) = dependency {
            vec![dependency]
        } else {
            vec![]
        }
    }
}
<|endoftext|>


<commit_before><commit_msg>Use xcb's randr bindings rather than ffi<commit_after><|endoftext|>


<commit_before><commit_msg>Re-add utf-8 error back to DecodeError<commit_after><|endoftext|>


<commit_before><commit_msg>Address some warnings generated by rustc 1.22<commit_after><|endoftext|>


<commit_before><commit_msg>Fix Windows command line parsing error<commit_after><|endoftext|>


<commit_before>extern crate regex;

use std::collections::HashMap;
use std::collections::HashSet;
use regex::Regex;

use request::*;
use response::*;

#[derive(PartialEq, Eq, Hash, Debug)]
pub enum ParamType {
    Integer,
    String,
    Float,
}

pub struct Route {
    matcher:     Regex,
    methods:     HashSet<Method>,
    params:      HashMap<String, ParamType>,
    pub handler: fn(&Request) -> Response,
}

impl Route {
    pub fn new(path: &str, mlist: Vec<Method>, handler: fn(&Request) -> Response) -> Route {
        let re = Regex::new(r"^<(?:(int|str):)?([\w_][a-zA-Z0-9_]*)>$").unwrap();
        let parts: Vec<&str> = path.split('/').filter(|&s| s != "").collect();
        let mut matcher: String = String::from(r"^");
        let mut params: HashMap<String, ParamType> = HashMap::new();
        let mut methods: HashSet<Method> = HashSet::new();

        for m in mlist {
            methods.insert(m);
        }

        for part in parts {
            let chunk: String = match re.is_match(part) {
                true  => {
                    let mut rc = String::new();
                    let caps = re.captures(part).unwrap();
                    let param = caps.at(2).unwrap().clone();
                    let ptype: ParamType = match caps.at(1) {
                        Some(x)     => {
                            match x.as_ref() {
                                "string" | "str"    => ParamType::String,
                                "integer" | "int"   => ParamType::Integer,
                                "float"             => ParamType::Float,
                                _                   => ParamType::String,

                            }
                        }
                        None        => ParamType::String,
                    };

                    let mstr: String = match ptype {
                        ParamType::String  => String::from("(?:[^/])+"),
                        ParamType::Integer => String::from("-*[0-9]+"),
                        ParamType::Float   => String::from("-*[0-9]*[.]?[0-9]+"),
                    };

                    rc.push_str("/(?P<");
                    rc.push_str(&param);
                    rc.push_str(">");
                    rc.push_str(&mstr);
                    rc.push_str(")");
                    params.insert(String::from(param), ptype);

                    rc
                },
                false => String::from("/") + part,
            };

            matcher.push_str(&chunk);
        }

        /* end the regex with an optional final slash and a string terminator */
        matcher.push_str("/?$");

        Route {
            matcher: Regex::new(&matcher).unwrap(),
            params:  params,
            methods: methods,
            handler: handler,
        }
    }

    pub fn is_match(&self, req: &Request) -> bool {
        self.matcher.is_match(&req.path) && self.methods.contains(&req.method)
    }

    pub fn parse(&self, path: &str) -> Option<HashMap<String, String>> {
        let mut params: HashMap<String, String> = HashMap::new();

        return match self.matcher.is_match(&path) {
            true  => {
                let caps = self.matcher.captures(path).unwrap();
                for (param, _) in &self.params {
                    params.insert(param.clone(), String::from(caps.name(&param).unwrap()));
                }

                Some(params)
            },
            false => None,
        }
    }

    pub fn err_403(req: &Request) -> Response {
        Response::err_403(&req.path)
    }

    pub fn err_404(req: &Request) -> Response {
        Response::err_404(&req.path)
    }
}
<commit_msg>add a static file handler and an HTTP string escape fixer-upper<commit_after>extern crate regex;

use std::env;
use std::fs::File;
use std::error::Error;
use std::path::PathBuf;
use std::io::prelude::*;
use std::collections::HashMap;
use std::collections::HashSet;
use regex::Regex;

use request::*;
use response::*;

#[derive(PartialEq, Eq, Hash, Debug)]
pub enum ParamType {
    Integer,
    String,
    Float,
    Path,
}

pub struct Route {
    matcher:     Regex,
    methods:     HashSet<Method>,
    params:      HashMap<String, ParamType>,
    pub handler: fn(&Request) -> Response,
}

impl Route {
    pub fn new(path: &str, mlist: Vec<Method>, handler: fn(&Request) -> Response) -> Route {
        let re = Regex::new(r"^<(?:(int|str|float|path):)?([\w_][a-zA-Z0-9_]*)>$").unwrap();
        let parts: Vec<&str> = path.split('/').filter(|&s| s != "").collect();
        let mut matcher: String = String::from(r"^");
        let mut params: HashMap<String, ParamType> = HashMap::new();
        let mut methods: HashSet<Method> = HashSet::new();

        for m in mlist {
            methods.insert(m);
        }

        for part in parts {
            let chunk: String = match re.is_match(part) {
                true  => {
                    let mut rc = String::new();
                    let caps = re.captures(part).unwrap();
                    let param = caps.at(2).unwrap().clone();
                    let ptype: ParamType = match caps.at(1) {
                        Some(x)     => {
                            match x.as_ref() {
                                "int"   => ParamType::Integer,
                                "float" => ParamType::Float,
                                "path"  => ParamType::Path,
                                _       => ParamType::String,

                            }
                        }
                        None        => ParamType::String,
                    };

                    let mstr: String = match ptype {
                        ParamType::String  => String::from(r"(?:[^/])+"),
                        ParamType::Integer => String::from(r"-*[0-9]+"),
                        ParamType::Float   => String::from(r"-*[0-9]*[.]?[0-9]+"),
                        ParamType::Path    => String::from(r".+"),
                    };

                    rc.push_str("/(?P<");
                    rc.push_str(&param);
                    rc.push_str(">");
                    rc.push_str(&mstr);
                    rc.push_str(")");
                    params.insert(String::from(param), ptype);

                    rc
                },
                false => String::from("/") + part,
            };

            matcher.push_str(&chunk);
        }

        /* end the regex with an optional final slash and a string terminator */
        matcher.push_str("/?$");

        Route {
            matcher: Regex::new(&matcher).unwrap(),
            params:  params,
            methods: methods,
            handler: handler,
        }
    }

    pub fn is_match(&self, req: &Request) -> bool {
        self.matcher.is_match(&req.path) && self.methods.contains(&req.method)
    }

    pub fn parse(&self, path: &str) -> Option<HashMap<String, String>> {
        let mut params: HashMap<String, String> = HashMap::new();

        return match self.matcher.is_match(&path) {
            true  => {
                let caps = self.matcher.captures(path).unwrap();
                for (param, _) in &self.params {
                    params.insert(param.clone(), String::from(caps.name(&param).unwrap()));
                }

                Some(params)
            },
            false => None,
        }
    }

    pub fn err_403(req: &Request) -> Response {
        Response::err_403(&req.path)
    }

    pub fn err_404(req: &Request) -> Response {
        Response::err_404(&req.path)
    }

    pub fn static_file(req: &Request) -> Response {
        let mut res = Response::new();

        let cwd = env::current_dir().unwrap();
        let clean = Route::replace_escape(&req.path);
        let mut fpath = PathBuf::from(&cwd);
        let mut fbuf: Vec<u8> = Vec::new();

        for chunk in clean.split('/') {
            if chunk == "" || chunk == "." || chunk == ".." {
                /* bzzzzt */
                continue;
            }

            fpath.push(&chunk);
        }

        let file = File::open(&fpath);

        match file {
            Ok(mut f)   => {
                match f.read_to_end(&mut fbuf) {
                    Ok(_)   => {
                        res.set_code(200);
                        res.set_content_type("text/plain");
                        res.append(fbuf);
                    },
                    Err(e)  => {
                        return Response::err_500(e.description());
                    },
                }
            },
            Err(_)      => {
                return Response::err_404(&req.path);
            }
        }

        res
    }
}
<|endoftext|>