<commit_before>struct options {
    ignore_env: bool,
    null: bool,
    unsets: ~[~str],
    sets: ~[(~str, ~str)],
    program: ~[~str]
}

fn usage(prog: &str) {
    println!("Usage: {:s} [OPTION]... [-] [NAME=VALUE]... [COMMAND [ARG]...]", prog);
    println!("Set each NAME to VALUE in the environment and run COMMAND\n");
    println!("Possible options are:");
    println!("  -i, --ignore-environment\t start with an empty environment");
    println!("  -0, --null              \t end each output line with a 0 byte rather than newline");
    println!("  -u, --unset NAME        \t remove variable from the environment");
    println!("      --help              \t display this help and exit");
    println!("      --version           \t output version information and exit\n");
    println!("A mere - implies -i. If no COMMAND, print the resulting environment");
}

fn version() {
    println!("env 1.0.0");
}

// print name=value env pairs on screen
// if null is true, separate pairs with a \0, \n otherwise
fn print_env(null: bool) {
    let env = std::os::env();

    for &(ref n, ref v) in env.iter() {
        print!("{:s}={:s}{:c}",
            n.as_slice(),
            v.as_slice(),
            if null { '\0' } else { '\n' }
        );
    }
}

fn main() {
    let args = std::os::args();
    let prog = args[0].as_slice();

    // to handle arguments the same way than GNU env, we can't use getopts
    // and have to do this:

    let mut opts = ~options {
        ignore_env: false,
        null: false,
        unsets: ~[],
        sets: ~[],
        program: ~[]
    };

    let mut wait_cmd = false;
    let mut iter = args.iter();
    iter.next(); // skip program
    let mut item = iter.next();

    // the for loop doesn't work here,
    // because we need sometines to read 2 items forward,
    // and the iter can't be borrowed twice
    while item != None {
        let opt = item.unwrap();

        if wait_cmd {
            // we still accept NAME=VAL here but not other options
            let mut sp = opt.splitn_iter('=', 1);
            let name = sp.next();
            let value = sp.next();

            match (name, value) {
                (Some(n), Some(v)) => {
                    opts.sets.push((n.into_owned(), v.into_owned()));
                }
                _ => {
                    // read the program now
                    opts.program.push(opt.to_owned());
                    break;
                }
            }
        }

        else {
            if opt.starts_with("--") {
                match *opt {
                    ~"--help" => { usage(prog); return }
                    ~"--version" => { version(); return }

                    ~"--ignore-environment" => opts.ignore_env = true,
                    ~"--null" => opts.null = true,
                    ~"--unset" => {
                        let var = iter.next();

                        match var {
                            None => println!("{:s}: this option requires an argument: {:s}", prog, opt.as_slice()),
                            Some(s) => opts.unsets.push(s.to_owned())
                        }
                    }

                    _ => {
                        println!("{:s}: invalid option \"{:s}\"", prog, *opt);
                        println!("Type \"{:s} --help\" for detailed informations", prog);
                        return
                    }
                }
            }

            else if opt.starts_with("-") {
                if opt.len() == 0 {
                    // implies -i and stop parsing opts
                    wait_cmd = true;
                    opts.ignore_env = true;
                    continue;
                }

                let mut chars = opt.iter();
                chars.next();

                for c in chars {
                    // short versions of options
                    match c {
                        'i' => opts.ignore_env = true,
                        '0' => opts.null = true,
                        _ => {
                            println!("{:s}: illegal option -- {:c}", prog, c);
                            println!("Type \"{:s} --help\" for detailed informations", prog);
                            return
                        }
                    }
                }
            }

            else {
                // is it a NAME=VALUE like opt ?
                let mut sp = opt.splitn_iter('=', 1);
                let name = sp.next();
                let value = sp.next();

                match (name, value) {
                    (Some(n), Some(v)) => {
                        // yes
                        opts.sets.push((n.into_owned(), v.into_owned()));
                        wait_cmd = true;
                    }
                    // no, its a program-like opt
                    _ => {
                        opts.program.push(opt.to_owned());
                        break;
                    }
                }
            }
        }

        item = iter.next();
    }

    // read program arguments now

    for opt in iter {
        opts.program.push(opt.to_owned());
    }

    let env = std::os::env();

    if opts.ignore_env {
        for &(ref name, _) in env.iter() {
            std::os::unsetenv(name.as_slice())
        }
    }

    for ref name in opts.unsets.iter() {
        std::os::unsetenv(name.as_slice())
    }

    for &(ref name, ref val) in opts.sets.iter() {
        std::os::setenv(name.as_slice(), val.as_slice())
    }

    match opts.program {
        [ref prog, ..args] => {
            let status = std::run::process_status(prog.as_slice(), args.as_slice());
            std::os::set_exit_status(status)
        }

        [] => {
            // no program providen
            print_env(opts.null);
        }
    }
}
<commit_msg>Tweaks for consistency with other commands, added support for -u, -V and -h, refs #4<commit_after>#[link(name="env", vers="1.0.0", author="LeoTestard")];

/*
 * This file is part of the uutils coreutils package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/* last synced with: env (GNU coreutils) 8.13 */

struct options {
    ignore_env: bool,
    null: bool,
    unsets: ~[~str],
    sets: ~[(~str, ~str)],
    program: ~[~str]
}

fn usage(prog: &str) {
    println!("Usage: {:s} [OPTION]... [-] [NAME=VALUE]... [COMMAND [ARG]...]", prog);
    println!("Set each NAME to VALUE in the environment and run COMMAND\n");
    println!("Possible options are:");
    println!("  -i --ignore-environment\t start with an empty environment");
    println!("  -0 --null              \t end each output line with a 0 byte rather than newline");
    println!("  -u --unset NAME        \t remove variable from the environment");
    println!("  -h --help              \t display this help and exit");
    println!("  -V --version           \t output version information and exit\n");
    println!("A mere - implies -i. If no COMMAND, print the resulting environment");
}

fn version() {
    println!("env 1.0.0");
}

// print name=value env pairs on screen
// if null is true, separate pairs with a \0, \n otherwise
fn print_env(null: bool) {
    let env = std::os::env();

    for &(ref n, ref v) in env.iter() {
        print!("{:s}={:s}{:c}",
            n.as_slice(),
            v.as_slice(),
            if null { '\0' } else { '\n' }
        );
    }
}

fn main() {
    let args = std::os::args();
    let prog = args[0].as_slice();

    // to handle arguments the same way than GNU env, we can't use getopts
    let mut opts = ~options {
        ignore_env: false,
        null: false,
        unsets: ~[],
        sets: ~[],
        program: ~[]
    };

    let mut wait_cmd = false;
    let mut iter = args.iter();
    iter.next(); // skip program
    let mut item = iter.next();

    // the for loop doesn't work here,
    // because we need sometines to read 2 items forward,
    // and the iter can't be borrowed twice
    while item != None {
        let opt = item.unwrap();

        if wait_cmd {
            // we still accept NAME=VAL here but not other options
            let mut sp = opt.splitn_iter('=', 1);
            let name = sp.next();
            let value = sp.next();

            match (name, value) {
                (Some(n), Some(v)) => {
                    opts.sets.push((n.into_owned(), v.into_owned()));
                }
                _ => {
                    // read the program now
                    opts.program.push(opt.to_owned());
                    break;
                }
            }
        } else if opt.starts_with("--") {
            match *opt {
                ~"--help" => { usage(prog); return }
                ~"--version" => { version(); return }

                ~"--ignore-environment" => opts.ignore_env = true,
                ~"--null" => opts.null = true,
                ~"--unset" => {
                    let var = iter.next();

                    match var {
                        None => println!("{:s}: this option requires an argument: {:s}", prog, opt.as_slice()),
                        Some(s) => opts.unsets.push(s.to_owned())
                    }
                }

                _ => {
                    println!("{:s}: invalid option \"{:s}\"", prog, *opt);
                    println!("Type \"{:s} --help\" for detailed informations", prog);
                    return
                }
            }
        } else if opt.starts_with("-") {
            if opt.len() == 0 {
                // implies -i and stop parsing opts
                wait_cmd = true;
                opts.ignore_env = true;
                continue;
            }

            let mut chars = opt.iter();
            chars.next();

            for c in chars {
                // short versions of options
                match c {
                    'h' => { usage(prog); return }
                    'V' => { version(); return }
                    'i' => opts.ignore_env = true,
                    '0' => opts.null = true,
                    'u' => {
                        let var = iter.next();

                        match var {
                            None => println!("{:s}: this option requires an argument: {:s}", prog, opt.as_slice()),
                            Some(s) => opts.unsets.push(s.to_owned())
                        }
                    }
                    _ => {
                        println!("{:s}: illegal option -- {:c}", prog, c);
                        println!("Type \"{:s} --help\" for detailed informations", prog);
                        return
                    }
                }
            }
        } else {
            // is it a NAME=VALUE like opt ?
            let mut sp = opt.splitn_iter('=', 1);
            let name = sp.next();
            let value = sp.next();

            match (name, value) {
                (Some(n), Some(v)) => {
                    // yes
                    opts.sets.push((n.into_owned(), v.into_owned()));
                    wait_cmd = true;
                }
                // no, its a program-like opt
                _ => {
                    opts.program.push(opt.to_owned());
                    break;
                }
            }
        }

        item = iter.next();
    }

    // read program arguments
    for opt in iter {
        opts.program.push(opt.to_owned());
    }

    let env = std::os::env();

    if opts.ignore_env {
        for &(ref name, _) in env.iter() {
            std::os::unsetenv(name.as_slice())
        }
    }

    for ref name in opts.unsets.iter() {
        std::os::unsetenv(name.as_slice())
    }

    for &(ref name, ref val) in opts.sets.iter() {
        std::os::setenv(name.as_slice(), val.as_slice())
    }

    match opts.program {
        [ref prog, ..args] => {
            let status = std::run::process_status(prog.as_slice(), args.as_slice());
            std::os::set_exit_status(status)
        }

        [] => {
            // no program provided
            print_env(opts.null);
        }
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2016 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.

use std::collections::HashSet;

use enums;
use parse;

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

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

    // 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 = doc.instructions.iter().filter_map(|i| {
            match i {
                &parse::Instruction::TypeStruct { result_id, .. } if result_id == pointed_ty => {
                    // Determine whether there's a Block or BufferBlock decoration.
                    let is_ssbo = doc.instructions.iter().filter_map(|i| {
                        match i {
                            &parse::Instruction::Decorate { target_id, decoration: enums::Decoration::DecorationBufferBlock, .. } if target_id == pointed_ty => {
                                Some(true)
                            },
                            &parse::Instruction::Decorate { target_id, decoration: enums::Decoration::DecorationBlock, .. } if target_id == pointed_ty => {
                                Some(false)
                            },
                            _ => None,
                        }
                    }).next().expect("Found a buffer uniform with neither the Block nor BufferBlock decorations");

                    Some(if !is_ssbo {
                        "DescriptorType::UniformBuffer"
                    } else {
                        "DescriptorType::StorageBuffer"
                    })
                },
                &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("DescriptorType::SampledImage")
                },
                &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("DescriptorType::InputAttachment")       // FIXME: can be `StorageImage`
                },
                &parse::Instruction::TypeSampledImage { result_id, image_type_id }
                                                                    if result_id == pointed_ty =>
                {
                    Some("DescriptorType::CombinedImageSampler")
                },
                _ => 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(),
            set: descriptor_set,
            binding: binding,
        });
    }

    // Sorting descriptors by binding in order to make sure we're in the right order.
    descriptors.sort_by(|a, b| a.binding.cmp(&b.binding));

    // Computing the list of sets that are needed.
    let sets_list = descriptors.iter().map(|d| d.set).collect::<HashSet<u32>>();

    let mut output = String::new();

    // Iterate once per set.
    for &set in sets_list.iter() {
        let descr = descriptors.iter().enumerate().filter(|&(_, d)| d.set == set)
                               .map(|(entry, d)| {
                                   format!("DescriptorDesc {{
                                                binding: {binding},
                                                ty: {desc_ty},
                                                array_count: 1,
                                                stages: ShaderStages::all(),        // TODO:
                                                readonly: false,                    // TODO:
                                            }}", binding = d.binding, desc_ty = d.desc_ty)
                               })
                               .collect::<Vec<_>>();

        output.push_str(&format!(r#"
            fn set{set}_layout() -> VecIntoIter<DescriptorDesc> {{
                vec![
                    {descr}
                ].into_iter()
            }}
        "#, set = set, descr = descr.join(",")));
    }

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

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

        #[allow(unsafe_code)]
        unsafe impl PipelineLayoutDesc for Layout {{
            type SetsIter = VecIntoIter<Self::DescIter>;
            type DescIter = VecIntoIter<DescriptorDesc>;

            fn descriptors_desc(&self) -> Self::SetsIter {{
                vec![
                    {layouts}
                ].into_iter()
            }}
        }}

        "#, layouts = (0 .. max_set).map(|n| format!("set{}_layout()", n)).collect::<Vec<_>>().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>Correctly set the readonly attribute of descriptor layouts descriptions<commit_after>// Copyright (c) 2016 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.

use std::collections::HashSet;

use enums;
use parse;

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

    // Finding all the descriptors.
    let mut descriptors = Vec::new();
    struct Descriptor {
        name: String,
        set: u32,
        binding: u32,
        desc_ty: String,
        readonly: bool,
    }

    // 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, readonly) = doc.instructions.iter().filter_map(|i| {
            match i {
                &parse::Instruction::TypeStruct { result_id, .. } if result_id == pointed_ty => {
                    // Determine whether there's a Block or BufferBlock decoration.
                    let is_ssbo = doc.instructions.iter().filter_map(|i| {
                        match i {
                            &parse::Instruction::Decorate { target_id, decoration: enums::Decoration::DecorationBufferBlock, .. } if target_id == pointed_ty => {
                                Some(true)
                            },
                            &parse::Instruction::Decorate { target_id, decoration: enums::Decoration::DecorationBlock, .. } if target_id == pointed_ty => {
                                Some(false)
                            },
                            _ => None,
                        }
                    }).next().expect("Found a buffer uniform with neither the Block nor BufferBlock decorations");

                    // Determine whether there's a NonWritable decoration.
                    let non_writable = false;       // TODO: tricky because the decoration is on struct members

                    Some(if !is_ssbo {
                        ("DescriptorType::UniformBuffer", true)
                    } else {
                        ("DescriptorType::StorageBuffer", non_writable)
                    })
                },
                &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(("DescriptorType::SampledImage", true))
                },
                &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(("DescriptorType::InputAttachment", true))       // FIXME: can be `StorageImage`
                },
                &parse::Instruction::TypeSampledImage { result_id, image_type_id }
                                                                    if result_id == pointed_ty =>
                {
                    Some(("DescriptorType::CombinedImageSampler", true))
                },
                _ => 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(),
            set: descriptor_set,
            binding: binding,
            readonly: readonly,
        });
    }

    // Sorting descriptors by binding in order to make sure we're in the right order.
    descriptors.sort_by(|a, b| a.binding.cmp(&b.binding));

    // Computing the list of sets that are needed.
    let sets_list = descriptors.iter().map(|d| d.set).collect::<HashSet<u32>>();

    let mut output = String::new();

    // Iterate once per set.
    for &set in sets_list.iter() {
        let descr = descriptors.iter().enumerate().filter(|&(_, d)| d.set == set)
                               .map(|(entry, d)| {
                                   format!("DescriptorDesc {{
                                                binding: {binding},
                                                ty: {desc_ty},
                                                array_count: 1,
                                                stages: ShaderStages::all(),        // TODO:
                                                readonly: {readonly},
                                            }}", binding = d.binding, desc_ty = d.desc_ty,
                                                 readonly = if d.readonly { "true" } else { "false" })
                               })
                               .collect::<Vec<_>>();

        output.push_str(&format!(r#"
            fn set{set}_layout() -> VecIntoIter<DescriptorDesc> {{
                vec![
                    {descr}
                ].into_iter()
            }}
        "#, set = set, descr = descr.join(",")));
    }

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

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

        #[allow(unsafe_code)]
        unsafe impl PipelineLayoutDesc for Layout {{
            type SetsIter = VecIntoIter<Self::DescIter>;
            type DescIter = VecIntoIter<DescriptorDesc>;

            fn descriptors_desc(&self) -> Self::SetsIter {{
                vec![
                    {layouts}
                ].into_iter()
            }}
        }}

        "#, layouts = (0 .. max_set).map(|n| format!("set{}_layout()", n)).collect::<Vec<_>>().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>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! Memory profiling functions.

use libc::{c_char,c_int,c_void,size_t};
use std::borrow::ToOwned;
use std::ffi::CString;
use std::io::timer::sleep;
#[cfg(target_os="linux")]
use std::io::File;
use std::mem;
use std::mem::size_of;
#[cfg(target_os="linux")]
use std::os::page_size;
use std::ptr::null_mut;
use std::sync::mpsc::{Sender, channel, Receiver};
use std::time::duration::Duration;
use task::spawn_named;
#[cfg(target_os="macos")]
use task_info::task_basic_info::{virtual_size,resident_size};

pub struct MemoryProfilerChan(pub Sender<MemoryProfilerMsg>);

impl MemoryProfilerChan {
    pub fn send(&self, msg: MemoryProfilerMsg) {
        let MemoryProfilerChan(ref c) = *self;
        c.send(msg).unwrap();
    }
}

pub enum MemoryProfilerMsg {
    /// Message used to force print the memory profiling metrics.
    Print,
    /// Tells the memory profiler to shut down.
    Exit,
}

pub struct MemoryProfiler {
    pub port: Receiver<MemoryProfilerMsg>,
}

impl MemoryProfiler {
    pub fn create(period: Option<f64>) -> MemoryProfilerChan {
        let (chan, port) = channel();
        match period {
            Some(period) => {
                let period = Duration::milliseconds((period * 1000f64) as i64);
                let chan = chan.clone();
                spawn_named("Memory profiler timer".to_owned(), move || {
                    loop {
                        sleep(period);
                        if chan.send(MemoryProfilerMsg::Print).is_err() {
                            break;
                        }
                    }
                });
                // Spawn the memory profiler.
                spawn_named("Memory profiler".to_owned(), move || {
                    let memory_profiler = MemoryProfiler::new(port);
                    memory_profiler.start();
                });
            }
            None => {
                // No-op to handle messages when the memory profiler is
                // inactive.
                spawn_named("Memory profiler".to_owned(), move || {
                    loop {
                        match port.recv() {
                            Err(_) | Ok(MemoryProfilerMsg::Exit) => break,
                            _ => {}
                        }
                    }
                });
            }
        }

        MemoryProfilerChan(chan)
    }

    pub fn new(port: Receiver<MemoryProfilerMsg>) -> MemoryProfiler {
        MemoryProfiler {
            port: port
        }
    }

    pub fn start(&self) {
        loop {
            match self.port.recv() {
               Ok(msg) => {
                   if !self.handle_msg(msg) {
                       break
                   }
               }
               _ => break
            }
        }
    }

    fn handle_msg(&self, msg: MemoryProfilerMsg) -> bool {
        match msg {
            MemoryProfilerMsg::Print => {
                self.handle_print_msg();
                true
            },
            MemoryProfilerMsg::Exit => false
        }
    }

    fn print_measurement(path: &str, nbytes: Option<u64>) {
        match nbytes {
            Some(nbytes) => {
                let mebi = 1024f64 * 1024f64;
                println!("{:16}: {:12.2}", path, (nbytes as f64) / mebi);
            }
            None => {
                println!("{:16}: {:>12}", path, "???");
            }
        }
    }

    fn handle_print_msg(&self) {
        println!("{:16}: {:12}", "_category_", "_size (MiB)_");

        // Virtual and physical memory usage, as reported by the OS.
        MemoryProfiler::print_measurement("vsize",          get_vsize());
        MemoryProfiler::print_measurement("resident",       get_resident());

        // The descriptions of the jemalloc measurements are taken directly
        // from the jemalloc documentation.

        // Total number of bytes allocated by the application.
        MemoryProfiler::print_measurement("heap-allocated", get_jemalloc_stat("stats.allocated"));

        // Total number of bytes in active pages allocated by the application.
        // This is a multiple of the page size, and greater than or equal to
        // |stats.allocated|.
        MemoryProfiler::print_measurement("heap-active",    get_jemalloc_stat("stats.active"));

        // Total number of bytes in chunks mapped on behalf of the application.
        // This is a multiple of the chunk size, and is at least as large as
        // |stats.active|. This does not include inactive chunks.
        MemoryProfiler::print_measurement("heap-mapped",    get_jemalloc_stat("stats.mapped"));

        println!("");
    }
}

extern {
    fn je_mallctl(name: *const c_char, oldp: *mut c_void, oldlenp: *mut size_t,
                  newp: *mut c_void, newlen: size_t) -> c_int;
}

fn get_jemalloc_stat(name: &'static str) -> Option<u64> {
    let mut old: size_t = 0;
    let c_name = CString::from_slice(name.as_bytes());
    let oldp = &mut old as *mut _ as *mut c_void;
    let mut oldlen = size_of::<size_t>() as size_t;
    let rv: c_int;
    unsafe {
        rv = je_mallctl(c_name.as_ptr(), oldp, &mut oldlen, null_mut(), 0);
        mem::forget(c_name); // XXX correct?
    }
    if rv == 0 { Some(old as u64) } else { None }
}

// Like std::macros::try!, but for Option<>.
macro_rules! option_try(
    ($e:expr) => (match $e { Some(e) => e, None => return None })
);

#[cfg(target_os="linux")]
fn get_proc_self_statm_field(field: uint) -> Option<u64> {
    let mut f = File::open(&Path::new("/proc/self/statm"));
    match f.read_to_string() {
        Ok(contents) => {
            let s = option_try!(contents.as_slice().words().nth(field));
            let npages: u64 = option_try!(s.parse());
            Some(npages * (page_size() as u64))
        }
        Err(_) => None
    }
}

#[cfg(target_os="linux")]
fn get_vsize() -> Option<u64> {
    get_proc_self_statm_field(0)
}

#[cfg(target_os="linux")]
fn get_resident() -> Option<u64> {
    get_proc_self_statm_field(1)
}

#[cfg(target_os="macos")]
fn get_vsize() -> Option<u64> {
    virtual_size()
}

#[cfg(target_os="macos")]
fn get_resident() -> Option<u64> {
    resident_size()
}

#[cfg(not(any(target_os="linux", target_os = "macos")))]
fn get_vsize() -> Option<u64> {
    None
}

#[cfg(not(any(target_os="linux", target_os = "macos")))]
fn get_resident() -> Option<u64> {
    None
}
<commit_msg>auto merge of #4858 : nnethercote/servo/mallinfo, r=glennw<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! Memory profiling functions.

use libc::{c_char,c_int,c_void,size_t};
use std::borrow::ToOwned;
use std::ffi::CString;
use std::io::timer::sleep;
#[cfg(target_os="linux")]
use std::io::File;
use std::mem;
use std::mem::size_of;
#[cfg(target_os="linux")]
use std::os::page_size;
use std::ptr::null_mut;
use std::sync::mpsc::{Sender, channel, Receiver};
use std::time::duration::Duration;
use task::spawn_named;
#[cfg(target_os="macos")]
use task_info::task_basic_info::{virtual_size,resident_size};

pub struct MemoryProfilerChan(pub Sender<MemoryProfilerMsg>);

impl MemoryProfilerChan {
    pub fn send(&self, msg: MemoryProfilerMsg) {
        let MemoryProfilerChan(ref c) = *self;
        c.send(msg).unwrap();
    }
}

pub enum MemoryProfilerMsg {
    /// Message used to force print the memory profiling metrics.
    Print,
    /// Tells the memory profiler to shut down.
    Exit,
}

pub struct MemoryProfiler {
    pub port: Receiver<MemoryProfilerMsg>,
}

impl MemoryProfiler {
    pub fn create(period: Option<f64>) -> MemoryProfilerChan {
        let (chan, port) = channel();
        match period {
            Some(period) => {
                let period = Duration::milliseconds((period * 1000f64) as i64);
                let chan = chan.clone();
                spawn_named("Memory profiler timer".to_owned(), move || {
                    loop {
                        sleep(period);
                        if chan.send(MemoryProfilerMsg::Print).is_err() {
                            break;
                        }
                    }
                });
                // Spawn the memory profiler.
                spawn_named("Memory profiler".to_owned(), move || {
                    let memory_profiler = MemoryProfiler::new(port);
                    memory_profiler.start();
                });
            }
            None => {
                // No-op to handle messages when the memory profiler is
                // inactive.
                spawn_named("Memory profiler".to_owned(), move || {
                    loop {
                        match port.recv() {
                            Err(_) | Ok(MemoryProfilerMsg::Exit) => break,
                            _ => {}
                        }
                    }
                });
            }
        }

        MemoryProfilerChan(chan)
    }

    pub fn new(port: Receiver<MemoryProfilerMsg>) -> MemoryProfiler {
        MemoryProfiler {
            port: port
        }
    }

    pub fn start(&self) {
        loop {
            match self.port.recv() {
               Ok(msg) => {
                   if !self.handle_msg(msg) {
                       break
                   }
               }
               _ => break
            }
        }
    }

    fn handle_msg(&self, msg: MemoryProfilerMsg) -> bool {
        match msg {
            MemoryProfilerMsg::Print => {
                self.handle_print_msg();
                true
            },
            MemoryProfilerMsg::Exit => false
        }
    }

    fn print_measurement(path: &str, nbytes: Option<u64>) {
        match nbytes {
            Some(nbytes) => {
                let mebi = 1024f64 * 1024f64;
                println!("{:24}: {:12.2}", path, (nbytes as f64) / mebi);
            }
            None => {
                println!("{:24}: {:>12}", path, "???");
            }
        }
    }

    fn handle_print_msg(&self) {
        println!("{:24}: {:12}", "_category_", "_size (MiB)_");

        // Virtual and physical memory usage, as reported by the OS.
        MemoryProfiler::print_measurement("vsize", get_vsize());
        MemoryProfiler::print_measurement("resident", get_resident());

        // Total number of bytes allocated by the application on the system
        // heap.
        MemoryProfiler::print_measurement("system-heap-allocated",
                                          get_system_heap_allocated());

        // The descriptions of the following jemalloc measurements are taken
        // directly from the jemalloc documentation.

        // "Total number of bytes allocated by the application."
        MemoryProfiler::print_measurement("jemalloc-heap-allocated",
                                          get_jemalloc_stat("stats.allocated"));

        // "Total number of bytes in active pages allocated by the application.
        // This is a multiple of the page size, and greater than or equal to
        // |stats.allocated|."
        MemoryProfiler::print_measurement("jemalloc-heap-active",
                                          get_jemalloc_stat("stats.active"));

        // "Total number of bytes in chunks mapped on behalf of the application.
        // This is a multiple of the chunk size, and is at least as large as
        // |stats.active|. This does not include inactive chunks."
        MemoryProfiler::print_measurement("jemalloc-heap-mapped",
                                          get_jemalloc_stat("stats.mapped"));

        println!("");
    }
}

#[cfg(target_os="linux")]
extern {
    fn mallinfo() -> struct_mallinfo;
}

#[cfg(target_os="linux")]
#[repr(C)]
pub struct struct_mallinfo {
    arena:    c_int,
    ordblks:  c_int,
    smblks:   c_int,
    hblks:    c_int,
    hblkhd:   c_int,
    usmblks:  c_int,
    fsmblks:  c_int,
    uordblks: c_int,
    fordblks: c_int,
    keepcost: c_int,
}

#[cfg(target_os="linux")]
fn get_system_heap_allocated() -> Option<u64> {
    let mut info: struct_mallinfo;
    unsafe {
        info = mallinfo();
    }
    // The documentation in the glibc man page makes it sound like |uordblks|
    // would suffice, but that only gets the small allocations that are put in
    // the brk heap. We need |hblkhd| as well to get the larger allocations
    // that are mmapped.
    Some((info.hblkhd + info.uordblks) as u64)
}

#[cfg(not(target_os="linux"))]
fn get_system_heap_allocated() -> Option<u64> {
    None
}

extern {
    fn je_mallctl(name: *const c_char, oldp: *mut c_void, oldlenp: *mut size_t,
                  newp: *mut c_void, newlen: size_t) -> c_int;
}

fn get_jemalloc_stat(name: &'static str) -> Option<u64> {
    let mut old: size_t = 0;
    let c_name = CString::from_slice(name.as_bytes());
    let oldp = &mut old as *mut _ as *mut c_void;
    let mut oldlen = size_of::<size_t>() as size_t;
    let rv: c_int;
    unsafe {
        rv = je_mallctl(c_name.as_ptr(), oldp, &mut oldlen, null_mut(), 0);
        mem::forget(c_name); // XXX correct?
    }
    if rv == 0 { Some(old as u64) } else { None }
}

// Like std::macros::try!, but for Option<>.
macro_rules! option_try(
    ($e:expr) => (match $e { Some(e) => e, None => return None })
);

#[cfg(target_os="linux")]
fn get_proc_self_statm_field(field: uint) -> Option<u64> {
    let mut f = File::open(&Path::new("/proc/self/statm"));
    match f.read_to_string() {
        Ok(contents) => {
            let s = option_try!(contents.as_slice().words().nth(field));
            let npages: u64 = option_try!(s.parse());
            Some(npages * (page_size() as u64))
        }
        Err(_) => None
    }
}

#[cfg(target_os="linux")]
fn get_vsize() -> Option<u64> {
    get_proc_self_statm_field(0)
}

#[cfg(target_os="linux")]
fn get_resident() -> Option<u64> {
    get_proc_self_statm_field(1)
}

#[cfg(target_os="macos")]
fn get_vsize() -> Option<u64> {
    virtual_size()
}

#[cfg(target_os="macos")]
fn get_resident() -> Option<u64> {
    resident_size()
}

#[cfg(not(any(target_os="linux", target_os = "macos")))]
fn get_vsize() -> Option<u64> {
    None
}

#[cfg(not(any(target_os="linux", target_os = "macos")))]
fn get_resident() -> Option<u64> {
    None
}
<|endoftext|>


<commit_before>//! [`BuildContext`] is a (mostly) static information about a build task.

use crate::core::compiler::unit_graph::UnitGraph;
use crate::core::compiler::{BuildConfig, CompileKind, Unit};
use crate::core::profiles::Profiles;
use crate::core::PackageSet;
use crate::core::Workspace;
use crate::util::config::Config;
use crate::util::errors::CargoResult;
use crate::util::interning::InternedString;
use crate::util::Rustc;
use std::collections::{HashMap, HashSet};
use std::path::PathBuf;

mod target_info;
pub use self::target_info::{
    FileFlavor, FileType, RustDocFingerprint, RustcTargetData, TargetInfo,
};

/// The build context, containing complete infomration needed for a build task
/// before it gets started.
///
/// It is intended that this is mostly static information. Stuff that mutates
/// during the build can be found in the parent [`Context`]. (I say mostly,
/// because this has internal caching, but nothing that should be observable
/// or require &mut.)
///
/// As a result, almost every field on `BuildContext` is public, including
///
/// * a resolved [`UnitGraph`] of your dependencies,
/// * a [`Profiles`] containing compiler flags presets,
/// * a [`RustcTargetData`] containing host and target platform information,
/// * and a [`PackageSet`] for further package downloads,
///
/// just to name a few. Learn more on each own documentation.
///
/// # How to use
///
/// To prepare a build task, you may not want to use [`BuildContext::new`] directly,
/// since it is often too lower-level.
/// Instead, [`ops::create_bcx`] is usually what you are looking for.
///
/// [`Context`]: crate::core::compiler::Context
/// [`ops::create_bcx`]: crate::ops::create_bcx
pub struct BuildContext<'a, 'cfg> {
    /// The workspace the build is for.
    pub ws: &'a Workspace<'cfg>,

    /// The cargo configuration.
    pub config: &'cfg Config,

    /// This contains a collection of compiler flags presets.
    pub profiles: Profiles,

    /// Configuration information for a rustc build.
    pub build_config: &'a BuildConfig,

    /// Extra compiler args for either `rustc` or `rustdoc`.
    pub extra_compiler_args: HashMap<Unit, Vec<String>>,

    /// Package downloader.
    ///
    /// This holds ownership of the `Package` objects.
    pub packages: PackageSet<'cfg>,

    /// Information about rustc and the target platform.
    pub target_data: RustcTargetData<'cfg>,

    /// The root units of `unit_graph` (units requested on the command-line).
    pub roots: Vec<Unit>,

    /// The dependency graph of units to compile.
    pub unit_graph: UnitGraph,

    /// Reverse-dependencies of documented units, used by the `rustdoc --scrape-examples` flag.
    pub scrape_units: Vec<Unit>,

    /// The list of all kinds that are involved in this build
    pub all_kinds: HashSet<CompileKind>,
}

impl<'a, 'cfg> BuildContext<'a, 'cfg> {
    pub fn new(
        ws: &'a Workspace<'cfg>,
        packages: PackageSet<'cfg>,
        build_config: &'a BuildConfig,
        profiles: Profiles,
        extra_compiler_args: HashMap<Unit, Vec<String>>,
        target_data: RustcTargetData<'cfg>,
        roots: Vec<Unit>,
        unit_graph: UnitGraph,
        scrape_units: Vec<Unit>,
    ) -> CargoResult<BuildContext<'a, 'cfg>> {
        let all_kinds = unit_graph
            .keys()
            .map(|u| u.kind)
            .chain(build_config.requested_kinds.iter().copied())
            .chain(std::iter::once(CompileKind::Host))
            .collect();

        Ok(BuildContext {
            ws,
            config: ws.config(),
            packages,
            build_config,
            profiles,
            extra_compiler_args,
            target_data,
            roots,
            unit_graph,
            scrape_units,
            all_kinds,
        })
    }

    /// Information of the `rustc` this build task will use.
    pub fn rustc(&self) -> &Rustc {
        &self.target_data.rustc
    }

    /// Gets the user-specified linker for a particular host or target.
    pub fn linker(&self, kind: CompileKind) -> Option<PathBuf> {
        self.target_data
            .target_config(kind)
            .linker
            .as_ref()
            .map(|l| l.val.clone().resolve_program(self.config))
    }

    /// Gets the host architecture triple.
    ///
    /// For example, x86_64-unknown-linux-gnu, would be
    /// - machine: x86_64,
    /// - hardware-platform: unknown,
    /// - operating system: linux-gnu.
    pub fn host_triple(&self) -> InternedString {
        self.target_data.rustc.host
    }

    /// Gets the number of jobs specified for this build.
    pub fn jobs(&self) -> u32 {
        self.build_config.jobs
    }

    /// Extra compiler flags to pass to `rustc` for a given unit.
    ///
    /// Although it depends on the caller, in the current Cargo implementation,
    /// these flags take precendence over those from [`BuildContext::extra_args_for`].
    ///
    /// As of now, these flags come from environment variables and configurations.
    /// See [`TargetInfo.rustflags`] for more on how Cargo collects them.
    ///
    /// [`TargetInfo.rustflags`]: TargetInfo::rustflags
    pub fn rustflags_args(&self, unit: &Unit) -> &[String] {
        &self.target_data.info(unit.kind).rustflags
    }

    /// Extra compiler flags to pass to `rustdoc` for a given unit.
    ///
    /// Although it depends on the caller, in the current Cargo implementation,
    /// these flags take precendence over those from [`BuildContext::extra_args_for`].
    ///
    /// As of now, these flags come from environment variables and configurations.
    /// See [`TargetInfo.rustdocflags`] for more on how Cargo collects them.
    ///
    /// [`TargetInfo.rustdocflags`]: TargetInfo::rustdocflags
    pub fn rustdocflags_args(&self, unit: &Unit) -> &[String] {
        &self.target_data.info(unit.kind).rustdocflags
    }

    /// Extra compiler args for either `rustc` or `rustdoc`.
    ///
    /// As of now, these flags come from the trailing args of either
    /// `cargo rustc` or `cargo rustdoc`.
    pub fn extra_args_for(&self, unit: &Unit) -> Option<&Vec<String>> {
        self.extra_compiler_args.get(unit)
    }
}
<commit_msg>Update mod.rs<commit_after>//! [`BuildContext`] is a (mostly) static information about a build task.

use crate::core::compiler::unit_graph::UnitGraph;
use crate::core::compiler::{BuildConfig, CompileKind, Unit};
use crate::core::profiles::Profiles;
use crate::core::PackageSet;
use crate::core::Workspace;
use crate::util::config::Config;
use crate::util::errors::CargoResult;
use crate::util::interning::InternedString;
use crate::util::Rustc;
use std::collections::{HashMap, HashSet};
use std::path::PathBuf;

mod target_info;
pub use self::target_info::{
    FileFlavor, FileType, RustDocFingerprint, RustcTargetData, TargetInfo,
};

/// The build context, containing complete information needed for a build task
/// before it gets started.
///
/// It is intended that this is mostly static information. Stuff that mutates
/// during the build can be found in the parent [`Context`]. (I say mostly,
/// because this has internal caching, but nothing that should be observable
/// or require &mut.)
///
/// As a result, almost every field on `BuildContext` is public, including
///
/// * a resolved [`UnitGraph`] of your dependencies,
/// * a [`Profiles`] containing compiler flags presets,
/// * a [`RustcTargetData`] containing host and target platform information,
/// * and a [`PackageSet`] for further package downloads,
///
/// just to name a few. Learn more on each own documentation.
///
/// # How to use
///
/// To prepare a build task, you may not want to use [`BuildContext::new`] directly,
/// since it is often too lower-level.
/// Instead, [`ops::create_bcx`] is usually what you are looking for.
///
/// [`Context`]: crate::core::compiler::Context
/// [`ops::create_bcx`]: crate::ops::create_bcx
pub struct BuildContext<'a, 'cfg> {
    /// The workspace the build is for.
    pub ws: &'a Workspace<'cfg>,

    /// The cargo configuration.
    pub config: &'cfg Config,

    /// This contains a collection of compiler flags presets.
    pub profiles: Profiles,

    /// Configuration information for a rustc build.
    pub build_config: &'a BuildConfig,

    /// Extra compiler args for either `rustc` or `rustdoc`.
    pub extra_compiler_args: HashMap<Unit, Vec<String>>,

    /// Package downloader.
    ///
    /// This holds ownership of the `Package` objects.
    pub packages: PackageSet<'cfg>,

    /// Information about rustc and the target platform.
    pub target_data: RustcTargetData<'cfg>,

    /// The root units of `unit_graph` (units requested on the command-line).
    pub roots: Vec<Unit>,

    /// The dependency graph of units to compile.
    pub unit_graph: UnitGraph,

    /// Reverse-dependencies of documented units, used by the `rustdoc --scrape-examples` flag.
    pub scrape_units: Vec<Unit>,

    /// The list of all kinds that are involved in this build
    pub all_kinds: HashSet<CompileKind>,
}

impl<'a, 'cfg> BuildContext<'a, 'cfg> {
    pub fn new(
        ws: &'a Workspace<'cfg>,
        packages: PackageSet<'cfg>,
        build_config: &'a BuildConfig,
        profiles: Profiles,
        extra_compiler_args: HashMap<Unit, Vec<String>>,
        target_data: RustcTargetData<'cfg>,
        roots: Vec<Unit>,
        unit_graph: UnitGraph,
        scrape_units: Vec<Unit>,
    ) -> CargoResult<BuildContext<'a, 'cfg>> {
        let all_kinds = unit_graph
            .keys()
            .map(|u| u.kind)
            .chain(build_config.requested_kinds.iter().copied())
            .chain(std::iter::once(CompileKind::Host))
            .collect();

        Ok(BuildContext {
            ws,
            config: ws.config(),
            packages,
            build_config,
            profiles,
            extra_compiler_args,
            target_data,
            roots,
            unit_graph,
            scrape_units,
            all_kinds,
        })
    }

    /// Information of the `rustc` this build task will use.
    pub fn rustc(&self) -> &Rustc {
        &self.target_data.rustc
    }

    /// Gets the user-specified linker for a particular host or target.
    pub fn linker(&self, kind: CompileKind) -> Option<PathBuf> {
        self.target_data
            .target_config(kind)
            .linker
            .as_ref()
            .map(|l| l.val.clone().resolve_program(self.config))
    }

    /// Gets the host architecture triple.
    ///
    /// For example, x86_64-unknown-linux-gnu, would be
    /// - machine: x86_64,
    /// - hardware-platform: unknown,
    /// - operating system: linux-gnu.
    pub fn host_triple(&self) -> InternedString {
        self.target_data.rustc.host
    }

    /// Gets the number of jobs specified for this build.
    pub fn jobs(&self) -> u32 {
        self.build_config.jobs
    }

    /// Extra compiler flags to pass to `rustc` for a given unit.
    ///
    /// Although it depends on the caller, in the current Cargo implementation,
    /// these flags take precedence over those from [`BuildContext::extra_args_for`].
    ///
    /// As of now, these flags come from environment variables and configurations.
    /// See [`TargetInfo.rustflags`] for more on how Cargo collects them.
    ///
    /// [`TargetInfo.rustflags`]: TargetInfo::rustflags
    pub fn rustflags_args(&self, unit: &Unit) -> &[String] {
        &self.target_data.info(unit.kind).rustflags
    }

    /// Extra compiler flags to pass to `rustdoc` for a given unit.
    ///
    /// Although it depends on the caller, in the current Cargo implementation,
    /// these flags take precedence over those from [`BuildContext::extra_args_for`].
    ///
    /// As of now, these flags come from environment variables and configurations.
    /// See [`TargetInfo.rustdocflags`] for more on how Cargo collects them.
    ///
    /// [`TargetInfo.rustdocflags`]: TargetInfo::rustdocflags
    pub fn rustdocflags_args(&self, unit: &Unit) -> &[String] {
        &self.target_data.info(unit.kind).rustdocflags
    }

    /// Extra compiler args for either `rustc` or `rustdoc`.
    ///
    /// As of now, these flags come from the trailing args of either
    /// `cargo rustc` or `cargo rustdoc`.
    pub fn extra_args_for(&self, unit: &Unit) -> Option<&Vec<String>> {
        self.extra_compiler_args.get(unit)
    }
}
<|endoftext|>


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

//! Computing deltas between two ropes.

use std::collections::HashMap;

use compare::RopeScanner;
use delta::{Delta, DeltaElement};
use interval::Interval;
use rope::{LinesMetric, Rope, RopeInfo, RopeDelta};
use tree::{Node, NodeInfo};

use memchr::memchr;

/// A trait implemented by various diffing strategies.
pub trait Diff<N: NodeInfo> {
    fn compute_delta(base: &Node<N>, target: &Node<N>) -> Delta<N>;
}

/// The minimum length of non-whitespace characters in a line before
/// we consider it for diffing purposes.
const MIN_SIZE: usize = 32;

/// A line-oriented, hash based diff algorithm.
///
/// This works by taking a hash of each line in either document that
/// has a length, ignoring leading whitespace, above some threshold.
///
/// Lines in the target document are matched against lines in the
/// base document. When a match is found, it is extended forwards
/// and backwards as far as possible.
///
/// This runs in O(n+m) in the lengths of the two ropes, and produces
/// results on a variety of workloads that are comparable in quality
/// (measured in terms of serialized diff size) with the results from
/// using a suffix array, while being an order of magnitude faster.
pub struct LineHashDiff;

impl Diff<RopeInfo> for LineHashDiff {
    fn compute_delta(base: &Rope, target: &Rope) -> RopeDelta {
        let mut builder = DiffBuilder::default();

        // before doing anything, scan top down and bottom up for like-ness.
        let mut scanner = RopeScanner::new(base, target);
        let (start_offset, diff_end) = scanner.find_min_diff_range();
        let target_end = target.len() - diff_end;

        if start_offset > 0 {
            builder.copy(0, 0, start_offset);
        }

        // if our preliminary scan finds no differences we're done
        if start_offset == base.len() && target.len() == base.len() {
            return builder.to_delta(base, target);
        }

        // TODO: because of how `lines_raw` returns Cows, we can't easily build
        // the lookup table without allocating. The eventual solution would be
        // to have a custom iter on the rope that returns suitable chunks.
        let base_string = String::from(base);
        let line_hashes = make_line_hashes(&base_string, MIN_SIZE);

        let line_count = target.measure::<LinesMetric>() + 1;
        let mut matches = Vec::with_capacity(line_count);

        let mut targ_line_offset = 0;
        let mut prev_base = 0;

        let mut needs_subseq = false;
        for line in target.lines_raw(start_offset..target_end) {
            let non_ws = non_ws_offset(&line);
            if line.len() - non_ws >= MIN_SIZE {
                if let Some(base_off) = line_hashes.get(&line[non_ws..]) {
                    let targ_off = targ_line_offset + non_ws;
                    matches.push((start_offset + targ_off, *base_off));
                    if *base_off < prev_base {
                        needs_subseq = true;
                    }
                    prev_base = *base_off;
                }
            }
            targ_line_offset += line.len();
        }

        // we now have an ordered list of matches and their positions.
        // to ensure that our delta only copies non-decreasing base regions,
        // we take the longest increasing subsequence.
        // TODO: a possible optimization here would be to expand matches
        // to adjacent lines first? this would be at best a small win though..

        let longest_subseq = if needs_subseq {
            longest_increasing_region_set(&matches)
        } else {
            matches
        };

        // for each matching region, we extend it forwards and backwards.
        // we keep track of how far forward we extend it each time, to avoid
        // having a subsequent scan extend backwards over the same region.
        let mut prev_end = start_offset;

        for (targ_off, base_off) in longest_subseq {
            if targ_off <= prev_end { continue; }
            let (left_dist, mut right_dist) = expand_match(base, target, base_off,
                                                           targ_off, prev_end);
            if targ_off + right_dist > target_end {
                // don't let last match expand past target_end
                right_dist = target_end - targ_off;
            }

            let targ_start = targ_off - left_dist;
            let base_start = base_off - left_dist;
            let len = left_dist + right_dist;
            prev_end = targ_start + len;

            builder.copy(base_start, targ_start, len);
        }

        if diff_end > 0 {
            builder.copy(base.len() - diff_end,
            target.len() - diff_end, diff_end);
        }

        builder.to_delta(base, target)
    }
}

/// Given two ropes and the offsets of two equal bytes, finds the largest
/// identical substring shared between the two ropes which contains the offset.
///
/// The return value is a pair of offsets, each of which represents an absolute
/// distance. That is to say, the position of the start and end boundaries
/// relative to the input offset.
fn expand_match(base: &Rope, target: &Rope, base_off: usize, targ_off: usize,
                prev_match_targ_end: usize) -> (usize, usize) {

    let mut scanner = RopeScanner::new(base, target);
    let max_left = targ_off - prev_match_targ_end;
    let start = scanner.find_ne_char_left(base_off, targ_off, max_left);
    debug_assert!(start <= max_left, "{} <= {}", start, max_left);
    let end = scanner.find_ne_char_right(base_off, targ_off, None);
    (start.min(max_left), end)
}

/// Finds the longest increasing subset of copyable regions. This is essentially
/// the longest increasing subsequence problem. This implementation is adapted
/// from https://codereview.stackexchange.com/questions/187337/longest-increasing-subsequence-algorithm
fn longest_increasing_region_set(items: &[(usize, usize)]) -> Vec<(usize, usize)> {
    let mut result = vec![0];
    let mut prev_chain = vec![0; items.len()];

    for i in 1..items.len() {
        // If the next item is greater than the last item of the current longest
        // subsequence, push its index at the end of the result and continue.
        let last_idx = *result.last().unwrap();
        if items[last_idx].1 < items[i].1 {
            prev_chain[i] = last_idx;
            result.push(i);
            continue;
        }

        let next_idx = match result.binary_search_by(
            |&j| items[j].1.cmp(&items[i].1)) {
                Ok(_) => continue, // we ignore duplicates
                Err(idx) => idx,
        };

        if &items[i].1 < &items[result[next_idx]].1 {
            if next_idx > 0 {
                prev_chain[i] = result[next_idx - 1];
            }
            result[next_idx] = i;
        }
    }

    // walk backwards from the last item in result to build the final sequence
    let mut u = result.len();
    let mut v = *result.last().unwrap();
    while u != 0 {
        u -= 1;
        result[u] = v;
        v = prev_chain[v];
    }
    result.iter().map(|i| items[*i]).collect()
}

#[inline]
fn non_ws_offset(s: &str) -> usize {
    s.as_bytes()
        .iter()
        .take_while(|b| **b == b' ' || **b == b'\t')
        .count()
}

/// Represents copying `len` bytes from base to target.
#[derive(Debug, Clone, Copy)]
struct DiffOp {
    target_idx: usize,
    base_idx: usize,
    len: usize,
}

/// Keeps track of copy ops during diff construction.
#[derive(Debug, Clone, Default)]
pub struct DiffBuilder {
    ops: Vec<DiffOp>,
}

impl DiffBuilder {
    fn copy(&mut self, base: usize, target: usize, len: usize) {
        if let Some(prev) = self.ops.last_mut() {
            let prev_end = prev.target_idx + prev.len;
            let base_end = prev.base_idx + prev.len;
            assert!(prev_end <= target, "{} <= {} prev {:?}", prev_end, target, prev);
            if prev_end == target && base_end == base {
                prev.len += len;
                return;
            }
        }
        self.ops.push(DiffOp { target_idx: target, base_idx: base, len: len })
    }

    fn to_delta(self, base: &Rope, target: &Rope) -> RopeDelta {
        let mut els = Vec::with_capacity(self.ops.len() * 2);
        let mut targ_pos = 0;
        for DiffOp { base_idx, target_idx, len } in self.ops {
            if target_idx > targ_pos {
                let iv = Interval::new_closed_open(targ_pos, target_idx);
                els.push(DeltaElement::Insert(target.subseq(iv)));
            }
            els.push(DeltaElement::Copy(base_idx, base_idx + len));
            targ_pos = target_idx + len;
        }

        if targ_pos < target.len() {
            let iv = Interval::new_closed_open(targ_pos, target.len());
            els.push(DeltaElement::Insert(target.subseq(iv)));
        }

        Delta { els, base_len: base.len() }
    }
}

/// Fast iterator over lines in a string, not removing newline characters.
struct LineScanner<'a> {
    inner: &'a str,
    idx: usize,
}

impl<'a> Iterator for LineScanner<'a> {
    type Item = &'a str;
    fn next(&mut self) -> Option<&'a str> {
        let idx = memchr(b'\n', &self.inner.as_bytes()[self.idx..])?;
        let result = &self.inner[self.idx..self.idx + idx+1];
        self.idx += idx + 1;
        Some(result)
    }
}

fn make_line_hashes<'a>(base: &'a str, min_size: usize) -> HashMap<&'a str, usize> {
    let mut offset = 0;
    let mut line_hashes = HashMap::with_capacity(base.len() / 60);
    let iter = LineScanner { inner: base, idx: 0 };
    for line in iter {
        let non_ws = non_ws_offset(&line);
        if line.len() - non_ws >= min_size {
            line_hashes.insert(&line[non_ws..], offset + non_ws);
        }
        offset += line.len();
    }
    line_hashes
}

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

    static SMALL_ONE: &str = "This adds FixedSizeAdler32, that has a size set at construction, and keeps bytes in a cyclic buffer of that size to be removed when it fills up.

Current logic (and implementing Write) might be too much, since bytes will probably always be fed one by one anyway. Otherwise a faster way of removing a sequence might be needed (one by one is inefficient).";

    static SMALL_TWO: &str = "This adds some function, I guess?, that has a size set at construction, and keeps bytes in a cyclic buffer of that size to be ground up and injested when it fills up.

Currently my sense of smell (and the pain of implementing Write) might be too much, since bytes will probably always be fed one by one anyway. Otherwise crying might be needed (one by one is inefficient).";

    static INTERVAL_STR: &str = include_str!("../src/interval.rs");
    static BREAKS_STR: &str = include_str!("../src/breaks.rs");

    #[test]
    fn diff_smoke_test() {
        let one = SMALL_ONE.into();
        let two = SMALL_TWO.into();

        let delta = LineHashDiff::compute_delta(&one, &two);
        println!("delta: {:?}", &delta);

        let result = delta.apply(&one);
        assert_eq!(result, two);

        let delta = LineHashDiff::compute_delta(&one, &two);
        println!("delta: {:?}", &delta);

        let result = delta.apply(&one);
        assert_eq!(result, two);
    }

    #[test]
    fn test_larger_diff() {
        let one = INTERVAL_STR.into();
        let two = BREAKS_STR.into();

        let delta = LineHashDiff::compute_delta(&one, &two);
        let result = delta.apply(&one);
        assert_eq!(String::from(result), String::from(two));
    }
}
<commit_msg>Review feedback<commit_after>// Copyright 2018 The xi-editor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Computing deltas between two ropes.

use std::collections::HashMap;

use compare::RopeScanner;
use delta::{Delta, DeltaElement};
use interval::Interval;
use rope::{LinesMetric, Rope, RopeInfo, RopeDelta};
use tree::{Node, NodeInfo};

use memchr::memchr;

/// A trait implemented by various diffing strategies.
pub trait Diff<N: NodeInfo> {
    fn compute_delta(base: &Node<N>, target: &Node<N>) -> Delta<N>;
}

/// The minimum length of non-whitespace characters in a line before
/// we consider it for diffing purposes.
const MIN_SIZE: usize = 32;

/// A line-oriented, hash based diff algorithm.
///
/// This works by taking a hash of each line in either document that
/// has a length, ignoring leading whitespace, above some threshold.
///
/// Lines in the target document are matched against lines in the
/// base document. When a match is found, it is extended forwards
/// and backwards as far as possible.
///
/// This runs in O(n+m) in the lengths of the two ropes, and produces
/// results on a variety of workloads that are comparable in quality
/// (measured in terms of serialized diff size) with the results from
/// using a suffix array, while being an order of magnitude faster.
pub struct LineHashDiff;

impl Diff<RopeInfo> for LineHashDiff {
    fn compute_delta(base: &Rope, target: &Rope) -> RopeDelta {
        let mut builder = DiffBuilder::default();

        // before doing anything, scan top down and bottom up for like-ness.
        let mut scanner = RopeScanner::new(base, target);
        let (start_offset, diff_end) = scanner.find_min_diff_range();
        let target_end = target.len() - diff_end;

        if start_offset > 0 {
            builder.copy(0, 0, start_offset);
        }

        // if our preliminary scan finds no differences we're done
        if start_offset == base.len() && target.len() == base.len() {
            return builder.to_delta(base, target);
        }

        // TODO: because of how `lines_raw` returns Cows, we can't easily build
        // the lookup table without allocating. The eventual solution would be
        // to have a custom iter on the rope that returns suitable chunks.
        let base_string = String::from(base);
        let line_hashes = make_line_hashes(&base_string, MIN_SIZE);

        let line_count = target.measure::<LinesMetric>() + 1;
        let mut matches = Vec::with_capacity(line_count);

        let mut targ_line_offset = 0;
        let mut prev_base = 0;

        let mut needs_subseq = false;
        for line in target.lines_raw(start_offset..target_end) {
            let non_ws = non_ws_offset(&line);
            if line.len() - non_ws >= MIN_SIZE {
                if let Some(base_off) = line_hashes.get(&line[non_ws..]) {
                    let targ_off = targ_line_offset + non_ws;
                    matches.push((start_offset + targ_off, *base_off));
                    if *base_off < prev_base {
                        needs_subseq = true;
                    }
                    prev_base = *base_off;
                }
            }
            targ_line_offset += line.len();
        }

        // we now have an ordered list of matches and their positions.
        // to ensure that our delta only copies non-decreasing base regions,
        // we take the longest increasing subsequence.
        // TODO: a possible optimization here would be to expand matches
        // to adjacent lines first? this would be at best a small win though..

        let longest_subseq = if needs_subseq {
            longest_increasing_region_set(&matches)
        } else {
            matches
        };

        // for each matching region, we extend it forwards and backwards.
        // we keep track of how far forward we extend it each time, to avoid
        // having a subsequent scan extend backwards over the same region.
        let mut prev_end = start_offset;

        for (targ_off, base_off) in longest_subseq {
            if targ_off <= prev_end { continue; }
            let (left_dist, mut right_dist) = expand_match(base, target, base_off,
                                                           targ_off, prev_end);

            // don't let last match expand past target_end
            right_dist = right_dist.min(target_end - targ_off);

            let targ_start = targ_off - left_dist;
            let base_start = base_off - left_dist;
            let len = left_dist + right_dist;
            prev_end = targ_start + len;

            builder.copy(base_start, targ_start, len);
        }

        if diff_end > 0 {
            builder.copy(base.len() - diff_end, target.len() - diff_end, diff_end);
        }

        builder.to_delta(base, target)
    }
}

/// Given two ropes and the offsets of two equal bytes, finds the largest
/// identical substring shared between the two ropes which contains the offset.
///
/// The return value is a pair of offsets, each of which represents an absolute
/// distance. That is to say, the position of the start and end boundaries
/// relative to the input offset.
fn expand_match(base: &Rope, target: &Rope, base_off: usize, targ_off: usize,
                prev_match_targ_end: usize) -> (usize, usize) {

    let mut scanner = RopeScanner::new(base, target);
    let max_left = targ_off - prev_match_targ_end;
    let start = scanner.find_ne_char_left(base_off, targ_off, max_left);
    debug_assert!(start <= max_left, "{} <= {}", start, max_left);
    let end = scanner.find_ne_char_right(base_off, targ_off, None);
    (start.min(max_left), end)
}

/// Finds the longest increasing subset of copyable regions. This is essentially
/// the longest increasing subsequence problem. This implementation is adapted
/// from https://codereview.stackexchange.com/questions/187337/longest-increasing-subsequence-algorithm
fn longest_increasing_region_set(items: &[(usize, usize)]) -> Vec<(usize, usize)> {
    let mut result = vec![0];
    let mut prev_chain = vec![0; items.len()];

    for i in 1..items.len() {
        // If the next item is greater than the last item of the current longest
        // subsequence, push its index at the end of the result and continue.
        let last_idx = *result.last().unwrap();
        if items[last_idx].1 < items[i].1 {
            prev_chain[i] = last_idx;
            result.push(i);
            continue;
        }

        let next_idx = match result.binary_search_by(
            |&j| items[j].1.cmp(&items[i].1)) {
                Ok(_) => continue, // we ignore duplicates
                Err(idx) => idx,
        };

        if &items[i].1 < &items[result[next_idx]].1 {
            if next_idx > 0 {
                prev_chain[i] = result[next_idx - 1];
            }
            result[next_idx] = i;
        }
    }

    // walk backwards from the last item in result to build the final sequence
    let mut u = result.len();
    let mut v = *result.last().unwrap();
    while u != 0 {
        u -= 1;
        result[u] = v;
        v = prev_chain[v];
    }
    result.iter().map(|i| items[*i]).collect()
}

#[inline]
fn non_ws_offset(s: &str) -> usize {
    s.as_bytes()
        .iter()
        .take_while(|b| **b == b' ' || **b == b'\t')
        .count()
}

/// Represents copying `len` bytes from base to target.
#[derive(Debug, Clone, Copy)]
struct DiffOp {
    target_idx: usize,
    base_idx: usize,
    len: usize,
}

/// Keeps track of copy ops during diff construction.
#[derive(Debug, Clone, Default)]
pub struct DiffBuilder {
    ops: Vec<DiffOp>,
}

impl DiffBuilder {
    fn copy(&mut self, base: usize, target: usize, len: usize) {
        if let Some(prev) = self.ops.last_mut() {
            let prev_end = prev.target_idx + prev.len;
            let base_end = prev.base_idx + prev.len;
            assert!(prev_end <= target, "{} <= {} prev {:?}", prev_end, target, prev);
            if prev_end == target && base_end == base {
                prev.len += len;
                return;
            }
        }
        self.ops.push(DiffOp { target_idx: target, base_idx: base, len: len })
    }

    fn to_delta(self, base: &Rope, target: &Rope) -> RopeDelta {
        let mut els = Vec::with_capacity(self.ops.len() * 2);
        let mut targ_pos = 0;
        for DiffOp { base_idx, target_idx, len } in self.ops {
            if target_idx > targ_pos {
                let iv = Interval::new(targ_pos, target_idx);
                els.push(DeltaElement::Insert(target.subseq(iv)));
            }
            els.push(DeltaElement::Copy(base_idx, base_idx + len));
            targ_pos = target_idx + len;
        }

        if targ_pos < target.len() {
            let iv = Interval::new(targ_pos, target.len());
            els.push(DeltaElement::Insert(target.subseq(iv)));
        }

        Delta { els, base_len: base.len() }
    }
}

/// Fast iterator over lines in a string, not removing newline characters.
struct LineScanner<'a> {
    inner: &'a str,
    idx: usize,
}

impl<'a> Iterator for LineScanner<'a> {
    type Item = &'a str;
    fn next(&mut self) -> Option<&'a str> {
        if self.idx >= self.inner.len() {
            return None;
        }

        match memchr(b'\n', &self.inner.as_bytes()[self.idx..]) {
            Some(idx) => {
                let next_idx = self.idx + idx + 1;
                let result = &self.inner[self.idx..next_idx];
                self.idx = next_idx;
                Some(result)
            }
            None => {
                let result = &self.inner[self.idx..];
                self.idx = self.inner.len();
                Some(result)
            }
        }
    }
}

fn make_line_hashes<'a>(base: &'a str, min_size: usize) -> HashMap<&'a str, usize> {
    let mut offset = 0;
    let mut line_hashes = HashMap::with_capacity(base.len() / 60);
    let iter = LineScanner { inner: base, idx: 0 };
    for line in iter {
        let non_ws = non_ws_offset(&line);
        if line.len() - non_ws >= min_size {
            line_hashes.insert(&line[non_ws..], offset + non_ws);
        }
        offset += line.len();
    }
    line_hashes
}

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

    static SMALL_ONE: &str = "This adds FixedSizeAdler32, that has a size set at construction, and keeps bytes in a cyclic buffer of that size to be removed when it fills up.

Current logic (and implementing Write) might be too much, since bytes will probably always be fed one by one anyway. Otherwise a faster way of removing a sequence might be needed (one by one is inefficient).";

    static SMALL_TWO: &str = "This adds some function, I guess?, that has a size set at construction, and keeps bytes in a cyclic buffer of that size to be ground up and injested when it fills up.

Currently my sense of smell (and the pain of implementing Write) might be too much, since bytes will probably always be fed one by one anyway. Otherwise crying might be needed (one by one is inefficient).";

    static INTERVAL_STR: &str = include_str!("../src/interval.rs");
    static BREAKS_STR: &str = include_str!("../src/breaks.rs");

    #[test]
    fn diff_smoke_test() {
        let one = SMALL_ONE.into();
        let two = SMALL_TWO.into();

        let delta = LineHashDiff::compute_delta(&one, &two);
        println!("delta: {:?}", &delta);

        let result = delta.apply(&one);
        assert_eq!(result, two);

        let delta = LineHashDiff::compute_delta(&one, &two);
        println!("delta: {:?}", &delta);

        let result = delta.apply(&one);
        assert_eq!(result, two);
    }

    #[test]
    fn test_larger_diff() {
        let one = INTERVAL_STR.into();
        let two = BREAKS_STR.into();

        let delta = LineHashDiff::compute_delta(&one, &two);
        let result = delta.apply(&one);
        assert_eq!(String::from(result), String::from(two));
    }
}
<|endoftext|>


<commit_before>use std::iter::from_fn;

use turtle::{choose, random, Color, Turtle};

fn main() {
    // Parameters to play around with for changing the character of the created drawing.
    let width = 800;
    let height = 600;
    let row_count = 3;
    let col_count = 4;
    let color_count = 7;

    let mut turtle = ArtisticTurtle::new(width, height, row_count, col_count, color_count);
    turtle.draw();
}

/// A special kind of turtle that is specialized in creating drawings of colorful triangles.
struct ArtisticTurtle {
    turtle: Turtle,
    row_count: u32,
    col_count: u32,
    row_height: f64,
    col_width: f64,
    colors: Vec<Color>,
}

impl ArtisticTurtle {
    /// Create a new turtle using the given parameters.
    /// `width`: total width of the drawing
    /// `height`: total height of the drawing
    /// `row_count`: number of rows of triangles to be created
    /// `col_count`: number of columns per row to be created
    /// `color_count`: number of different colors to use
    fn new(width: u32, height: u32, row_count: u32, col_count: u32, color_count: usize) -> Self {
        // Create and prepare the turtle.
        let mut turtle = Turtle::new();
        turtle.set_speed("instant");
        turtle.pen_up();
        turtle.drawing_mut().set_size([width, height]);

        // Move turtle to the upper left corner of the drawing.
        let size = turtle.drawing().size();
        let x_start = size.width / 2;
        let y_start = size.height / 2;
        turtle.go_to([f64::from(x_start) * -1.0, f64::from(y_start)]);
        turtle.set_speed(20);

        // Calculate height and width of the triangles.
        let row_height = f64::from(size.height) / f64::from(row_count);
        let col_width = f64::from(size.width) / f64::from(col_count);

        // Prepare a set of random colors to randomly choose from when drawing.
        let colors: Vec<Color> = from_fn(|| Some(random())).take(color_count).collect();

        Self {
            turtle,
            row_count,
            col_count,
            row_height,
            col_width,
            colors,
        }
    }

    /// Draw a single triangle by drawing two sides and filling them with a random color
    /// which creates the third side of the triangle along the way.
    /// Note: Even though the turtle is always turning right in this method, this movement
    /// can also result in a left turn (depending on the angle chosen).
    fn draw_triangle(&mut self, angle: f64) {
        // This call to unwrap() is always safe because the color slice will always be non-empty.
        let color = choose(&self.colors).cloned().unwrap();
        self.turtle.set_fill_color(color);
        self.turtle.begin_fill();

        self.turtle.right(angle);
        self.turtle.forward(self.col_width);

        self.turtle.right(angle);
        self.turtle.forward(self.row_height);

        self.turtle.end_fill();
    }

    /// Draw a single row of triangles in two runs:
    /// 1. Create triangles from left to right.
    /// 2. Turn around and fill in triangles from right to left.
    fn draw_row(&mut self) {
        // Create an endless loop over the angles of 90 and 270 degrees
        // (corresponds to moving right and left, respectively).
        let angles = vec![90.0, 270.0];
        let mut angle_cycle = angles.iter().cycle();

        // Create triangles from left to right.
        for _ in 0..self.col_count {
            let angle = angle_cycle.next().unwrap();
            self.draw_triangle(*angle);
        }

        // Skip one angle so that we have the correct angle when turning around.
        angle_cycle.next();

        // Fill in triangles from right to left to complete the row.
        for _ in 0..self.col_count {
            let angle = angle_cycle.next().unwrap();
            self.draw_triangle(*angle);
        }

        // Reset position to prepare for the next row.
        self.turtle.right(180.0);
        self.turtle.forward(self.row_height);
        self.turtle.left(180.0);
    }

    /// Create a drawing consisting of rows of triangles in different colors.
    fn draw(&mut self) {
        for _ in 0..self.row_count {
            self.draw_row();
        }
        self.turtle.hide();
    }
}
<commit_msg>Improve cycling over angles<commit_after>use std::iter::from_fn;

use turtle::{choose, random, Color, Turtle};

fn main() {
    // Parameters to play around with for changing the character of the created drawing.
    let width = 800;
    let height = 600;
    let row_count = 3;
    let col_count = 4;
    let color_count = 7;

    let mut turtle = ArtisticTurtle::new(width, height, row_count, col_count, color_count);
    turtle.draw();
}

/// A special kind of turtle that is specialized in creating drawings of colorful triangles.
struct ArtisticTurtle {
    turtle: Turtle,
    row_count: u32,
    col_count: u32,
    row_height: f64,
    col_width: f64,
    colors: Vec<Color>,
}

impl ArtisticTurtle {
    /// Create a new turtle using the given parameters.
    /// `width`: total width of the drawing
    /// `height`: total height of the drawing
    /// `row_count`: number of rows of triangles to be created
    /// `col_count`: number of columns per row to be created
    /// `color_count`: number of different colors to use
    fn new(width: u32, height: u32, row_count: u32, col_count: u32, color_count: usize) -> Self {
        // Create and prepare the turtle.
        let mut turtle = Turtle::new();
        turtle.set_speed("instant");
        turtle.pen_up();
        turtle.drawing_mut().set_size([width, height]);

        // Move turtle to the upper left corner of the drawing.
        let size = turtle.drawing().size();
        let x_start = size.width / 2;
        let y_start = size.height / 2;
        turtle.go_to([f64::from(x_start) * -1.0, f64::from(y_start)]);
        turtle.set_speed(20);

        // Calculate height and width of the triangles.
        let row_height = f64::from(size.height) / f64::from(row_count);
        let col_width = f64::from(size.width) / f64::from(col_count);

        // Prepare a set of random colors to randomly choose from when drawing.
        let colors: Vec<Color> = from_fn(|| Some(random())).take(color_count).collect();

        Self {
            turtle,
            row_count,
            col_count,
            row_height,
            col_width,
            colors,
        }
    }

    /// Draw a single triangle by drawing two sides and filling them with a random color
    /// which creates the third side of the triangle along the way.
    /// Note: Even though the turtle is always turning right in this method, this movement
    /// can also result in a left turn (depending on the angle chosen).
    fn draw_triangle(&mut self, angle: f64) {
        // This call to unwrap() is always safe because the color slice will always be non-empty.
        let color = choose(&self.colors).cloned().unwrap();
        self.turtle.set_fill_color(color);
        self.turtle.begin_fill();

        self.turtle.right(angle);
        self.turtle.forward(self.col_width);

        self.turtle.right(angle);
        self.turtle.forward(self.row_height);

        self.turtle.end_fill();
    }

    /// Draw a single row of triangles in two runs:
    /// 1. Create triangles from left to right.
    /// 2. Turn around and fill in triangles from right to left.
    fn draw_row(&mut self) {
        // Create an endless loop over the angles of 90 and 270 degrees
        // (corresponds to moving right and left, respectively).
        let angles = vec![90.0, 270.0];
        let angle_cycle = &mut angles.iter().cycle();

        // Create triangles from left to right.
        angle_cycle.take(self.col_count as usize).for_each(|angle| self.draw_triangle(*angle));

        // Skip one angle so that we have the correct angle when turning around.
        angle_cycle.next();

        // Fill in triangles from right to left to complete the row.
        angle_cycle.take(self.col_count as usize).for_each(|angle| self.draw_triangle(*angle));

        // Reset position to prepare for the next row.
        self.turtle.right(180.0);
        self.turtle.forward(self.row_height);
        self.turtle.left(180.0);
    }

    /// Create a drawing consisting of rows of triangles in different colors.
    fn draw(&mut self) {
        for _ in 0..self.row_count {
            self.draw_row();
        }
        self.turtle.hide();
    }
}
<|endoftext|>


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

//! Different assorted utilities.

use std::env;
use std::io::{self, Write};
use std::time::{SystemTime, UNIX_EPOCH};

use log::{Level, Record, SetLoggerError};
use env_logger::{Builder, Formatter};
use colored::*;

use blockchain::{GenesisConfig, ValidatorKeys};
use node::NodeConfig;
use crypto::gen_keypair;

pub use self::types::{Height, Milliseconds, Round, ValidatorId};

mod types;

pub mod fabric;
pub mod config;
#[macro_use]
pub mod metrics;

/// Performs the logger initialization.
pub fn init_logger() -> Result<(), SetLoggerError> {
    let mut builder = Builder::new();
    builder.format(format_log_record);

    if env::var("RUST_LOG").is_ok() {
        builder.parse(&env::var("RUST_LOG").unwrap());
    }

    builder.try_init()
}

/// Generates testnet configuration.
pub fn generate_testnet_config(count: u8, start_port: u16) -> Vec<NodeConfig> {
    let (validators, services): (Vec<_>, Vec<_>) = (0..count as usize)
        .map(|_| (gen_keypair(), gen_keypair()))
        .unzip();
    let genesis = GenesisConfig::new(validators.iter().zip(services.iter()).map(|x| {
        ValidatorKeys {
            consensus_key: (x.0).0,
            service_key: (x.1).0,
        }
    }));
    let peers = (0..validators.len())
        .map(|x| {
            format!("127.0.0.1:{}", start_port + x as u16)
                .parse()
                .unwrap()
        })
        .collect::<Vec<_>>();

    validators
        .into_iter()
        .zip(services.into_iter())
        .enumerate()
        .map(|(idx, (validator, service))| {
            NodeConfig {
                listen_address: peers[idx],
                external_address: Some(peers[idx]),
                network: Default::default(),
                peers: peers.clone(),
                consensus_public_key: validator.0,
                consensus_secret_key: validator.1,
                service_public_key: service.0,
                service_secret_key: service.1,
                genesis: genesis.clone(),
                whitelist: Default::default(),
                api: Default::default(),
                mempool: Default::default(),
                services_configs: Default::default(),
            }
        })
        .collect::<Vec<_>>()
}

fn has_colors() -> bool {
    use term::terminfo::TerminfoTerminal;
    use term::Terminal;
    use std::io;
    use atty;

    let out = io::stderr();
    match TerminfoTerminal::new(out) {
        Some(ref term) if atty::is(atty::Stream::Stderr) => term.supports_color(),
        _ => false,
    }
}

fn format_log_record(buf: &mut Formatter, record: &Record) -> io::Result<()> {
    let ts = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
    let secs = ts.as_secs().to_string();
    let millis = (u64::from(ts.subsec_nanos()) / 1_000_000).to_string();

    let verbose_src_path = match env::var("RUST_VERBOSE_PATH") {
        Ok(val) => val.parse::<bool>().unwrap_or(false),
        Err(_) => false,
    };

    let module = record.module_path().unwrap_or("unknown_module");
    let source_path = if verbose_src_path {
        let file = record.file().unwrap_or("unknown_file");
        let line = record.line().unwrap_or(0);
        format!("{}:{}:{}", module, file, line)
    } else {
        module.to_string()
    };

    if has_colors() {
        let level = match record.level() {
            Level::Error => "ERROR".red(),
            Level::Warn => "WARN".yellow(),
            Level::Info => "INFO".green(),
            Level::Debug => "DEBUG".cyan(),
            Level::Trace => "TRACE".white(),
        };
        write!(
            buf,
            "[{} : {:03}] - [ {} ] - {} - {}",
            secs.bold(),
            millis.bold(),
            level,
            &source_path,
            record.args()
        )
    } else {
        let level = match record.level() {
            Level::Error => "ERROR",
            Level::Warn => "WARN",
            Level::Info => "INFO",
            Level::Debug => "DEBUG",
            Level::Trace => "TRACE",
        };
        write!(
            buf,
            "[{} : {:03}] - [ {} ] - {} - {}",
            secs,
            millis,
            level,
            &source_path,
            record.args()
        )
    }
}
<commit_msg>Fix log output<commit_after>// Copyright 2017 The Exonum Team
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Different assorted utilities.

use std::env;
use std::io::{self, Write};
use std::time::{SystemTime, UNIX_EPOCH};

use log::{Level, Record, SetLoggerError};
use env_logger::{Builder, Formatter};
use colored::*;

use blockchain::{GenesisConfig, ValidatorKeys};
use node::NodeConfig;
use crypto::gen_keypair;

pub use self::types::{Height, Milliseconds, Round, ValidatorId};

mod types;

pub mod fabric;
pub mod config;
#[macro_use]
pub mod metrics;

/// Performs the logger initialization.
pub fn init_logger() -> Result<(), SetLoggerError> {
    let mut builder = Builder::new();
    builder.format(format_log_record);

    if env::var("RUST_LOG").is_ok() {
        builder.parse(&env::var("RUST_LOG").unwrap());
    }

    builder.try_init()
}

/// Generates testnet configuration.
pub fn generate_testnet_config(count: u8, start_port: u16) -> Vec<NodeConfig> {
    let (validators, services): (Vec<_>, Vec<_>) = (0..count as usize)
        .map(|_| (gen_keypair(), gen_keypair()))
        .unzip();
    let genesis = GenesisConfig::new(validators.iter().zip(services.iter()).map(|x| {
        ValidatorKeys {
            consensus_key: (x.0).0,
            service_key: (x.1).0,
        }
    }));
    let peers = (0..validators.len())
        .map(|x| {
            format!("127.0.0.1:{}", start_port + x as u16)
                .parse()
                .unwrap()
        })
        .collect::<Vec<_>>();

    validators
        .into_iter()
        .zip(services.into_iter())
        .enumerate()
        .map(|(idx, (validator, service))| {
            NodeConfig {
                listen_address: peers[idx],
                external_address: Some(peers[idx]),
                network: Default::default(),
                peers: peers.clone(),
                consensus_public_key: validator.0,
                consensus_secret_key: validator.1,
                service_public_key: service.0,
                service_secret_key: service.1,
                genesis: genesis.clone(),
                whitelist: Default::default(),
                api: Default::default(),
                mempool: Default::default(),
                services_configs: Default::default(),
            }
        })
        .collect::<Vec<_>>()
}

fn has_colors() -> bool {
    use term::terminfo::TerminfoTerminal;
    use term::Terminal;
    use std::io;
    use atty;

    let out = io::stderr();
    match TerminfoTerminal::new(out) {
        Some(ref term) if atty::is(atty::Stream::Stderr) => term.supports_color(),
        _ => false,
    }
}

fn format_log_record(buf: &mut Formatter, record: &Record) -> io::Result<()> {
    let ts = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
    let secs = ts.as_secs().to_string();
    let millis = (u64::from(ts.subsec_nanos()) / 1_000_000).to_string();

    let verbose_src_path = match env::var("RUST_VERBOSE_PATH") {
        Ok(val) => val.parse::<bool>().unwrap_or(false),
        Err(_) => false,
    };

    let module = record.module_path().unwrap_or("unknown_module");
    let source_path = if verbose_src_path {
        let file = record.file().unwrap_or("unknown_file");
        let line = record.line().unwrap_or(0);
        format!("{}:{}:{}", module, file, line)
    } else {
        module.to_string()
    };

    if has_colors() {
        let level = match record.level() {
            Level::Error => "ERROR".red(),
            Level::Warn => "WARN".yellow(),
            Level::Info => "INFO".green(),
            Level::Debug => "DEBUG".cyan(),
            Level::Trace => "TRACE".white(),
        };
        writeln!(
            buf,
            "[{} : {:03}] - [ {} ] - {} - {}",
            secs.bold(),
            millis.bold(),
            level,
            &source_path,
            record.args()
        )
    } else {
        let level = match record.level() {
            Level::Error => "ERROR",
            Level::Warn => "WARN",
            Level::Info => "INFO",
            Level::Debug => "DEBUG",
            Level::Trace => "TRACE",
        };
        writeln!(
            buf,
            "[{} : {:03}] - [ {} ] - {} - {}",
            secs,
            millis,
            level,
            &source_path,
            record.args()
        )
    }
}
<|endoftext|>