<commit_before>use unicode_segmentation::UnicodeSegmentation;
use unicode_width::UnicodeWidthStr;

use widgets::{Block, Widget};
use buffer::Buffer;
use layout::Rect;
use style::{Color, Modifier, Style};

/// A widget to display some text. You can specify colors using commands embedded in
/// the text such as "{[color] [text]}".
///
/// # Examples
///
/// ```
/// # extern crate tui;
/// # use tui::widgets::{Block, Borders, Paragraph};
/// # use tui::style::{Style, Color};
/// # fn main() {
/// Paragraph::default()
///     .block(Block::default().title("Paragraph").borders(Borders::ALL))
///     .style(Style::default().fg(Color::White).bg(Color::Black))
///     .wrap(true)
///     .text("First line\nSecond line\n{red Colored text}.");
/// # }
/// ```
pub struct Paragraph<'a> {
    /// A block to wrap the widget in
    block: Option<Block<'a>>,
    /// Widget style
    style: Style,
    /// Wrap the text or not
    wrapping: bool,
    /// The text to display
    text: &'a str,
    /// Should we parse the text for embedded commands
    raw: bool,
    /// Scroll
    scroll: u16,
}

impl<'a> Default for Paragraph<'a> {
    fn default() -> Paragraph<'a> {
        Paragraph {
            block: None,
            style: Default::default(),
            wrapping: false,
            raw: false,
            text: "",
            scroll: 0,
        }
    }
}

impl<'a> Paragraph<'a> {
    pub fn block(&'a mut self, block: Block<'a>) -> &mut Paragraph<'a> {
        self.block = Some(block);
        self
    }

    pub fn text(&mut self, text: &'a str) -> &mut Paragraph<'a> {
        self.text = text;
        self
    }

    pub fn style(&mut self, style: Style) -> &mut Paragraph<'a> {
        self.style = style;
        self
    }

    pub fn wrap(&mut self, flag: bool) -> &mut Paragraph<'a> {
        self.wrapping = flag;
        self
    }

    pub fn raw(&mut self, flag: bool) -> &mut Paragraph<'a> {
        self.raw = flag;
        self
    }

    pub fn scroll(&mut self, offset: u16) -> &mut Paragraph<'a> {
        self.scroll = offset;
        self
    }
}

struct Parser<'a, T>
where
    T: Iterator<Item = &'a str>,
{
    text: T,
    mark: bool,
    cmd_string: String,
    style: Style,
    base_style: Style,
    escaping: bool,
    styling: bool,
}

impl<'a, T> Parser<'a, T>
where
    T: Iterator<Item = &'a str>,
{
    fn new(text: T, base_style: Style) -> Parser<'a, T> {
        Parser {
            text: text,
            mark: false,
            cmd_string: String::from(""),
            style: base_style,
            base_style: base_style,
            escaping: false,
            styling: false,
        }
    }

    fn update_style(&mut self) {
        for cmd in self.cmd_string.split(';') {
            let args = cmd.split('=').collect::<Vec<&str>>();
            if let Some(first) = args.get(0) {
                match *first {
                    "fg" => if let Some(snd) = args.get(1) {
                        self.style.fg = Parser::<T>::str_to_color(snd);
                    },
                    "bg" => if let Some(snd) = args.get(1) {
                        self.style.bg = Parser::<T>::str_to_color(snd);
                    },
                    "mod" => if let Some(snd) = args.get(1) {
                        self.style.modifier = Parser::<T>::str_to_modifier(snd);
                    },
                    _ => {}
                }
            }
        }
    }

    fn str_to_color(string: &str) -> Color {
        match string {
            "black" => Color::Black,
            "red" => Color::Red,
            "green" => Color::Green,
            "yellow" => Color::Yellow,
            "magenta" => Color::Magenta,
            "cyan" => Color::Cyan,
            "gray" => Color::Gray,
            "dark_gray" => Color::DarkGray,
            "light_red" => Color::LightRed,
            "light_green" => Color::LightGreen,
            "light_yellow" => Color::LightYellow,
            "light_magenta" => Color::LightMagenta,
            "light_cyan" => Color::LightCyan,
            "white" => Color::White,
            _ => Color::Reset,
        }
    }

    fn str_to_modifier(string: &str) -> Modifier {
        match string {
            "bold" => Modifier::Bold,
            "italic" => Modifier::Italic,
            "underline" => Modifier::Underline,
            "invert" => Modifier::Invert,
            "crossed_out" => Modifier::CrossedOut,
            _ => Modifier::Reset,
        }
    }

    fn reset(&mut self) {
        self.styling = false;
        self.mark = false;
        self.style = self.base_style;
        self.cmd_string.clear();
    }
}

impl<'a, T> Iterator for Parser<'a, T>
where
    T: Iterator<Item = &'a str>,
{
    type Item = (&'a str, Style);
    fn next(&mut self) -> Option<Self::Item> {
        match self.text.next() {
            Some(s) => if s == "\\" {
                if self.escaping {
                    Some((s, self.style))
                } else {
                    self.escaping = true;
                    self.next()
                }
            } else if s == "{" {
                if self.escaping {
                    self.escaping = false;
                    Some((s, self.style))
                } else if self.mark {
                    Some((s, self.style))
                } else {
                    self.style = self.base_style;
                    self.mark = true;
                    self.next()
                }
            } else if s == "}" && self.mark {
                self.reset();
                self.next()
            } else if s == " " && self.mark {
                if self.styling {
                    Some((s, self.style))
                } else {
                    self.styling = true;
                    self.update_style();
                    self.next()
                }
            } else if self.mark && !self.styling {
                self.cmd_string.push_str(s);
                self.next()
            } else {
                Some((s, self.style))
            },
            None => None,
        }
    }
}

impl<'a> Widget for Paragraph<'a> {
    fn draw(&mut self, area: &Rect, buf: &mut Buffer) {
        let text_area = match self.block {
            Some(ref mut b) => {
                b.draw(area, buf);
                b.inner(area)
            }
            None => *area,
        };

        if text_area.height < 1 {
            return;
        }

        self.background(&text_area, buf, self.style.bg);

        let mut x = 0;
        let mut y = 0;
        let graphemes = UnicodeSegmentation::graphemes(self.text, true);
        let styled: Box<Iterator<Item = (&str, Style)>> = if self.raw {
            Box::new(graphemes.map(|g| (g, self.style)))
        } else {
            Box::new(Parser::new(graphemes, self.style))
        };

        for (string, style) in styled {
            if string == "\n" {
                x = 0;
                y += 1;
                continue;
            }
            if x >= text_area.width {
                if self.wrapping {
                    x = 0;
                    y += 1;
                }
                continue;
            }

            if y > text_area.height + self.scroll - 1 {
                break;
            }

            if y < self.scroll {
                continue;
            }

            buf.get_mut(text_area.left() + x, text_area.top() + y - self.scroll)
                .set_symbol(string)
                .set_style(style);
            x += string.width() as u16;
        }
    }
}
<commit_msg>[widgets][paragraph] Fix text wrapping<commit_after>use unicode_segmentation::UnicodeSegmentation;
use unicode_width::UnicodeWidthStr;

use widgets::{Block, Widget};
use buffer::Buffer;
use layout::Rect;
use style::{Color, Modifier, Style};

/// A widget to display some text. You can specify colors using commands embedded in
/// the text such as "{[color] [text]}".
///
/// # Examples
///
/// ```
/// # extern crate tui;
/// # use tui::widgets::{Block, Borders, Paragraph};
/// # use tui::style::{Style, Color};
/// # fn main() {
/// Paragraph::default()
///     .block(Block::default().title("Paragraph").borders(Borders::ALL))
///     .style(Style::default().fg(Color::White).bg(Color::Black))
///     .wrap(true)
///     .text("First line\nSecond line\n{red Colored text}.");
/// # }
/// ```
pub struct Paragraph<'a> {
    /// A block to wrap the widget in
    block: Option<Block<'a>>,
    /// Widget style
    style: Style,
    /// Wrap the text or not
    wrapping: bool,
    /// The text to display
    text: &'a str,
    /// Should we parse the text for embedded commands
    raw: bool,
    /// Scroll
    scroll: u16,
}

impl<'a> Default for Paragraph<'a> {
    fn default() -> Paragraph<'a> {
        Paragraph {
            block: None,
            style: Default::default(),
            wrapping: false,
            raw: false,
            text: "",
            scroll: 0,
        }
    }
}

impl<'a> Paragraph<'a> {
    pub fn block(&'a mut self, block: Block<'a>) -> &mut Paragraph<'a> {
        self.block = Some(block);
        self
    }

    pub fn text(&mut self, text: &'a str) -> &mut Paragraph<'a> {
        self.text = text;
        self
    }

    pub fn style(&mut self, style: Style) -> &mut Paragraph<'a> {
        self.style = style;
        self
    }

    pub fn wrap(&mut self, flag: bool) -> &mut Paragraph<'a> {
        self.wrapping = flag;
        self
    }

    pub fn raw(&mut self, flag: bool) -> &mut Paragraph<'a> {
        self.raw = flag;
        self
    }

    pub fn scroll(&mut self, offset: u16) -> &mut Paragraph<'a> {
        self.scroll = offset;
        self
    }
}

struct Parser<'a, T>
where
    T: Iterator<Item = &'a str>,
{
    text: T,
    mark: bool,
    cmd_string: String,
    style: Style,
    base_style: Style,
    escaping: bool,
    styling: bool,
}

impl<'a, T> Parser<'a, T>
where
    T: Iterator<Item = &'a str>,
{
    fn new(text: T, base_style: Style) -> Parser<'a, T> {
        Parser {
            text: text,
            mark: false,
            cmd_string: String::from(""),
            style: base_style,
            base_style: base_style,
            escaping: false,
            styling: false,
        }
    }

    fn update_style(&mut self) {
        for cmd in self.cmd_string.split(';') {
            let args = cmd.split('=').collect::<Vec<&str>>();
            if let Some(first) = args.get(0) {
                match *first {
                    "fg" => if let Some(snd) = args.get(1) {
                        self.style.fg = Parser::<T>::str_to_color(snd);
                    },
                    "bg" => if let Some(snd) = args.get(1) {
                        self.style.bg = Parser::<T>::str_to_color(snd);
                    },
                    "mod" => if let Some(snd) = args.get(1) {
                        self.style.modifier = Parser::<T>::str_to_modifier(snd);
                    },
                    _ => {}
                }
            }
        }
    }

    fn str_to_color(string: &str) -> Color {
        match string {
            "black" => Color::Black,
            "red" => Color::Red,
            "green" => Color::Green,
            "yellow" => Color::Yellow,
            "magenta" => Color::Magenta,
            "cyan" => Color::Cyan,
            "gray" => Color::Gray,
            "dark_gray" => Color::DarkGray,
            "light_red" => Color::LightRed,
            "light_green" => Color::LightGreen,
            "light_yellow" => Color::LightYellow,
            "light_magenta" => Color::LightMagenta,
            "light_cyan" => Color::LightCyan,
            "white" => Color::White,
            _ => Color::Reset,
        }
    }

    fn str_to_modifier(string: &str) -> Modifier {
        match string {
            "bold" => Modifier::Bold,
            "italic" => Modifier::Italic,
            "underline" => Modifier::Underline,
            "invert" => Modifier::Invert,
            "crossed_out" => Modifier::CrossedOut,
            _ => Modifier::Reset,
        }
    }

    fn reset(&mut self) {
        self.styling = false;
        self.mark = false;
        self.style = self.base_style;
        self.cmd_string.clear();
    }
}

impl<'a, T> Iterator for Parser<'a, T>
where
    T: Iterator<Item = &'a str>,
{
    type Item = (&'a str, Style);
    fn next(&mut self) -> Option<Self::Item> {
        match self.text.next() {
            Some(s) => if s == "\\" {
                if self.escaping {
                    Some((s, self.style))
                } else {
                    self.escaping = true;
                    self.next()
                }
            } else if s == "{" {
                if self.escaping {
                    self.escaping = false;
                    Some((s, self.style))
                } else if self.mark {
                    Some((s, self.style))
                } else {
                    self.style = self.base_style;
                    self.mark = true;
                    self.next()
                }
            } else if s == "}" && self.mark {
                self.reset();
                self.next()
            } else if s == " " && self.mark {
                if self.styling {
                    Some((s, self.style))
                } else {
                    self.styling = true;
                    self.update_style();
                    self.next()
                }
            } else if self.mark && !self.styling {
                self.cmd_string.push_str(s);
                self.next()
            } else {
                Some((s, self.style))
            },
            None => None,
        }
    }
}

impl<'a> Widget for Paragraph<'a> {
    fn draw(&mut self, area: &Rect, buf: &mut Buffer) {
        let text_area = match self.block {
            Some(ref mut b) => {
                b.draw(area, buf);
                b.inner(area)
            }
            None => *area,
        };

        if text_area.height < 1 {
            return;
        }

        self.background(&text_area, buf, self.style.bg);

        let mut x = 0;
        let mut y = 0;
        let graphemes = UnicodeSegmentation::graphemes(self.text, true);
        let styled: Box<Iterator<Item = (&str, Style)>> = if self.raw {
            Box::new(graphemes.map(|g| (g, self.style)))
        } else {
            Box::new(Parser::new(graphemes, self.style))
        };

        let mut remove_leading_whitespaces = false;
        for (string, style) in styled {
            if string == "\n" {
                x = 0;
                y += 1;
                continue;
            }
            if x >= text_area.width {
                if self.wrapping {
                    x = 0;
                    y += 1;
                    remove_leading_whitespaces = true
                }
            }

            if remove_leading_whitespaces && string == " " {
                continue;
            }
            remove_leading_whitespaces = false;

            if y > text_area.height + self.scroll - 1 {
                break;
            }

            if y < self.scroll {
                continue;
            }

            buf.get_mut(text_area.left() + x, text_area.top() + y - self.scroll)
                .set_symbol(string)
                .set_style(style);
            x += string.width() as u16;
        }
    }
}
<|endoftext|>


<commit_before>use std::rand::random;
use std::bool;
use graphics::Graphics;
/* CPU, Graphics and Memory core */

mod graphics;

//enum mode = {SCHIP, CHIP}; /* differentiate between CHIP and SCHIP modes */

static MAX_RAM : u16 = 4096;
static FLAG : uint = 15;

struct CPU {
    registers : [u8, ..16], /* 16 8 bit general purpose registers */
    mem : [u8, .. MAX_RAM], /* 4096bytes of memory */
    I : u16, /* 16 bit index register */
    pc: u16, /* Program counter */
    sp: uint, /* Stack Pointer */
    stack : [u16, ..16], /* 16 stack frames */
    sound_timer : u8,
    delay_timer : u8,
    hp_48_flags: [u16, ..8], /*SCHIP */
    opcode : u16,
    graphics :Graphics

}

impl CPU {

    fn new(mut mem: Vec<u8>) -> CPU {
        let mut cpu = CPU { registers: [0u8, ..16],
              mem: [0u8, ..MAX_RAM],
              I: 0,
              pc: 0x200,
              sp: 1,
              stack : [0u16, ..16],
              sound_timer: 0,
              delay_timer: 0,
              hp_48_flags: [0u16, ..8],
              opcode : 0,
              graphics : Graphics::new()
       };

       /* Move values out of supplied vector and into
        * CPU mem */
       mem.reverse(); /* reverse vector first as poping off the end (O(1))
                         is much cheaper than shifting from the front (O(n)) */
       for val in cpu.mem.mut_iter() {
            *val = match mem.pop() {
                    Some(item) => item,
                    None => 0x0
            };
       }

       return cpu;
    }

    fn to_addr(dig1 :u8, dig2 :u8, dig3 :u8) -> u16 {
        ((dig1 << 8) | (dig2 << 4) | dig3) as u16
    }

    fn to_val(dig1 :u8, dig2 :u8) -> u8 {
        (dig1 << 4) | dig2
    }

    fn u16_to_hex_vec(hex :u16) -> (u8, u8, u8, u8) {
         (((hex & 0xF000) >> 12) as u8,
          ((hex & 0x0F00) >> 8)  as u8,
          ((hex & 0x00F0) >> 4)  as u8,
           (hex & 0x000F)        as u8)
    }

    fn get_opcode(&self) -> u16 {
        (self.registers[self.pc as uint] << 4) as u16 |
        (self.registers[(self.pc + 1) as uint]) as u16
    }

    fn perform_cycle(&mut self) {

        let opcode = self.get_opcode();
        let opcode_v =  CPU::u16_to_hex_vec(opcode);
        self.inc_pc();

        match opcode_v {
            (0x0, 0x0 ,0xE, 0x0) => self.clear_screen(),
            (0x0, 0x0, 0xE, 0xE) => self.ret(),
            (0x0, N1, N2, N3) => {},
            (0x1, N1, N2, N3) => self.jump(CPU::to_addr(N1, N2, N3)),
            (0x2, N1, N2, N3) => self.call(CPU::to_addr(N1, N2, N3)),
            (0x3, X, N1, N2) => self.skip_equals_reg_val(X, CPU::to_val(N1, N2)),
            (0x4, X, N1, N2) => self.skip_not_equals_reg_val(X, CPU::to_val(N1, N2)),
            (0x5, X, Y, 0x0) => self.skip_equals_regs(X, Y),
            (0x6, X, N1, N2) => self.mov_reg_val(X, CPU::to_val(N1, N2)),
            (0x7, X, N1, N2) => self.add_reg_val(X, CPU::to_val(N1, N2)),
            (0x8, X, Y, 0x0) => self.mov_regs(X, Y),
            (0x8, X, Y, 0x1) => self.or_regs(X, Y),
            (0x8, X, Y, 0x2) => self.and_regs(X, Y),
            (0x8, X, Y, 0x3) => self.xor_regs(X, Y),
            (0x8, X, Y, 0x4) => self.add_regs(X, Y),
            (0x8, X, Y, 0x5) => self.sub_regs(X, Y),
            (0x8, X, _, 0x6) => self.shift_right(X),
            (0x8, X, Y, 0x7) => self.sub_regs(Y, X),
            (0x8, X, _, 0xE) => self.shift_left(X),
            (0x9, X, Y, 0x0) => self.skip_not_equals_regs(X, Y),
            (0xA, N1, N2, N3) => self.set_i(CPU::to_addr(N1, N2, N3)),
            (0xB, N1, N2, N3) => self.jump_val_reg0(CPU::to_addr(N1, N2, N3)),
            (0xC, X, N1, N2) => self.rand(X, CPU::to_val(N1, N2)),
            (0xD, X, Y, N) => {},
            (0xE, X, 0x9, 0xE) => {},
            (0xE, X, 0xA, 0x1) => {},
            (0xF, X, 0x0, 0x7) => {},
            (0xF, X, 0x0, 0xA) => {},
            (0xF, X, 0x1, 0x5) => self.set_reg_delay(X),
            (0xF, X, 0x1, 0x8) => self.set_reg_sound(X),
            (0xF, X, 0x1, 0xE) => self.add_reg_index(X),
            (0xF, X, 0x2, 0x9) =>  {},
            (0xF, X, 0x3, 0x3) => self.binary_decimal(X),
            (0xF, X, 0x5, 0x5) => self.store_regs(X),
            (0xF, X, 0x6, 0x5) => self.load_regs(X),
            _ => fail!("Unknown opcode {:x}",opcode)
        }
    }

    fn reset(&mut self) {
        self.pc = 0x200;
        self.sp = 0;
    }

    fn pop(&mut self) -> u16 {
        let item = self.stack[self.sp];
        self.sp -= 1;
        return item;

    }

    fn push(&mut self, val:u16) {
        self.sp += 1;
        self.stack[self.sp] = val;
    }

    fn inc_pc(&mut self) {
        self.pc += 2;
        self.pc %= MAX_RAM;
    }

    /* Jump to address at 0NNN */
    fn call(&mut self, addr:u16) {
        self.pc = addr & 0xFFF;
    }

    /* Clear the screen */
    fn clear_screen(&mut self) {

    }

    /* Return from a subroutine */
    fn ret(&mut self) {
        self.pc = self.pop();
    }

    fn jump(&mut self, addr: u16) {
        self.pc = addr;
    }

    /* Skip next instruction if register X is equal to NN */
    fn skip_equals_reg_val(&mut self, reg:u8, val:u8) {
        if self.registers[reg as uint] == val {
            self.inc_pc();
        }

    }

    fn skip_not_equals_reg_val(&mut self, reg:u8, val:u8) {
        if self.registers[reg as uint] != val {
            self.inc_pc();
        }
    }

    fn skip_equals_regs(&mut self, reg1:u8, reg2:u8) {

        if self.registers[reg1 as uint] == self.registers[reg2 as uint] {
               self.inc_pc();
        }
    }

    fn skip_not_equals_regs(&mut self, reg1:u8, reg2:u8) {
        if self.registers[reg1 as uint] != self.registers[reg2 as uint] {
               self.inc_pc();
           }
    }

    fn mov_reg_val(&mut self, reg:u8, val:u8) {
        self.registers[reg as uint] = val;
    }

    fn add_reg_val(&mut self, reg:u8, val:u8) {
        self.registers[reg as uint] += val;
    }

    fn mov_regs(&mut self, reg1:u8, reg2:u8) {
        self.registers[reg1 as uint] = self.registers[reg2 as uint];
    }

    fn or_regs(&mut self, reg1:u8, reg2:u8) {
       self.registers[reg1 as uint] |= self.registers[reg2 as uint];
    }

    fn and_regs(&mut self, reg1:u8, reg2:u8) {
        self.registers[reg1 as uint] |= self.registers[reg2 as uint];
    }

    fn xor_regs(&mut self, reg1:u8, reg2:u8) {
        self.registers[reg1 as uint] ^= self.registers[reg2 as uint];
    }

    /* Add two regs, if overflow set flag register otherwise
     * unset flag register */
    fn add_regs(&mut self, reg1:u8, reg2:u8) {
        self.registers[FLAG] =  bool::to_bit::<u8>(0xFF - reg1 > reg2);
        self.registers[reg1 as uint] += self.registers[reg2 as uint];
    }

    /* Sub reg2 from reg1, unset flag register if underflow,
     * set otherwise */
    fn sub_regs(&mut self, reg1:u8, reg2:u8) {
        self.registers[FLAG] = bool::to_bit::<u8>(reg2 < reg1);
        self.registers[reg1 as uint] -= self.registers[reg2 as uint];
    }

    /* Sub reg1 from reg2, unset flag register if underflow,
     * set otherwise */
    fn sub_regs_inv(&mut self, reg1:u8, reg2:u8) {
        self.registers[FLAG] = bool::to_bit::<u8>(reg1 < reg2);
        self.registers[reg2 as uint] -= self.registers[reg1 as uint];
    }

    /* Shift register left by 1, set flag register to most significant bit
     * before shifting */
    fn shift_left(&mut self, reg:u8) {
        self.registers[FLAG] = (self.registers[reg as uint] & 0x80) >> 7;
        self.registers[reg as uint] <<= 1;
    }

    /* Shift register right by 1, set flag register to least significant bit
     * before shifting */
    fn shift_right(&mut self, reg:u8) {
        self.registers[FLAG] = self.registers[reg as uint] & 0x1;
        self.registers[reg as uint] >>= 1;
    }

    /* Set index register to address in opcode */
    fn set_i(&mut self, addr:u16) {
        self.I = addr;
    }

    fn jump_val_reg0(&mut self, addr:u16) {
        self.pc = self.registers[0] as u16 + addr;
    }

    fn rand(&mut self, reg:u8, val:u8) {
        self.registers[reg as uint] = val & random::<u8>();
    }

    fn set_reg_delay(&mut self, reg:u8) {
        self.registers[reg as uint] = self.delay_timer;
    }

    fn set_reg_sound(&mut self, reg:u8) {
        self.registers[reg as uint] = self.sound_timer;
    }

    fn add_reg_index(&mut self, reg:u8) {
        self.registers[FLAG] =
            bool::to_bit::<u8>(0xFFF - (self.registers[reg as uint] as u16) < self.I);
        self.I += self.registers[reg as uint] as u16
    }

    fn store_regs(&mut self, max_reg:u8) {
        let mut addr = self.I;
        for reg in self.registers.slice_to(max_reg as uint + 1).iter() {
            self.mem[addr as uint] = *reg;
            addr += 1;
            addr %= MAX_RAM;
        }
    }

    fn load_regs(&mut self, max_reg:u8) {
        let mut addr = self.I;
        for reg in self.registers.mut_slice_to(max_reg as uint +1).mut_iter() {
            *reg = self.mem[addr as uint];
            addr += 1;
            addr %= MAX_RAM;
        }
    }

    fn store_regs2(&mut self, max_reg:u8) {
        let regs = self.registers.slice_to(max_reg as uint + 1).iter();
        let store = self.mem.mut_slice_from(self.I as uint).mut_iter();
        for (mem, reg) in store.zip(regs) {
            *mem = *reg;
        }

    }

    fn load_regs2(&mut self, max_reg:u8) {
        let regs = self.registers.mut_slice_to(max_reg as uint + 1).mut_iter();
        let store = self.mem.slice_from(self.I as uint).iter();
        for (mem, reg) in store.zip(regs) {
            *reg = *mem;
        }
   }

    /*stores the Binary-coded decimal representation of VX, with the
     * most significant of three digits at the address in I, the middle digit
     * at I + 1, and the lsg at I + 2.*/
    fn binary_decimal(&mut self, reg:u8) {
        let val = self.registers[reg as uint];
        self.mem[self.I as uint] = val/100;
        self.mem[(self.I + 1) as uint] = (val % 100)/10;
        self.mem[(self.I + 2) as uint] = (val % 100)%10;

    }

}

fn main() {

    print!("yay");
}

<commit_msg>Got rid of old load/store operations<commit_after>use std::rand::random;
use std::bool;
use graphics::Graphics;
/* CPU, Graphics and Memory core */

mod graphics;

//enum mode = {SCHIP, CHIP}; /* differentiate between CHIP and SCHIP modes */

static MAX_RAM : u16 = 4096;
static FLAG : uint = 15;

struct CPU {
    registers : [u8, ..16], /* 16 8 bit general purpose registers */
    mem : [u8, .. MAX_RAM], /* 4096bytes of memory */
    I : u16, /* 16 bit index register */
    pc: u16, /* Program counter */
    sp: uint, /* Stack Pointer */
    stack : [u16, ..16], /* 16 stack frames */
    sound_timer : u8,
    delay_timer : u8,
    hp_48_flags: [u16, ..8], /*SCHIP */
    opcode : u16,
    graphics :Graphics

}

impl CPU {

    fn new(mut mem: Vec<u8>) -> CPU {
        let mut cpu = CPU { registers: [0u8, ..16],
              mem: [0u8, ..MAX_RAM],
              I: 0,
              pc: 0x200,
              sp: 1,
              stack : [0u16, ..16],
              sound_timer: 0,
              delay_timer: 0,
              hp_48_flags: [0u16, ..8],
              opcode : 0,
              graphics : Graphics::new()
       };

       /* Move values out of supplied vector and into
        * CPU mem */
       mem.reverse(); /* reverse vector first as poping off the end (O(1))
                         is much cheaper than shifting from the front (O(n)) */
       for val in cpu.mem.mut_iter() {
            *val = match mem.pop() {
                    Some(item) => item,
                    None => 0x0
            };
       }

       return cpu;
    }

    fn to_addr(dig1 :u8, dig2 :u8, dig3 :u8) -> u16 {
        ((dig1 << 8) | (dig2 << 4) | dig3) as u16
    }

    fn to_val(dig1 :u8, dig2 :u8) -> u8 {
        (dig1 << 4) | dig2
    }

    fn u16_to_hex_vec(hex :u16) -> (u8, u8, u8, u8) {
         (((hex & 0xF000) >> 12) as u8,
          ((hex & 0x0F00) >> 8)  as u8,
          ((hex & 0x00F0) >> 4)  as u8,
           (hex & 0x000F)        as u8)
    }

    fn get_opcode(&self) -> u16 {
        (self.registers[self.pc as uint] << 4) as u16 |
        (self.registers[(self.pc + 1) as uint]) as u16
    }

    fn perform_cycle(&mut self) {

        let opcode = self.get_opcode();
        let opcode_v =  CPU::u16_to_hex_vec(opcode);
        self.inc_pc();

        match opcode_v {
            (0x0, 0x0 ,0xE, 0x0) => self.clear_screen(),
            (0x0, 0x0, 0xE, 0xE) => self.ret(),
            (0x0, N1, N2, N3) => {},
            (0x1, N1, N2, N3) => self.jump(CPU::to_addr(N1, N2, N3)),
            (0x2, N1, N2, N3) => self.call(CPU::to_addr(N1, N2, N3)),
            (0x3, X, N1, N2) => self.skip_equals_reg_val(X, CPU::to_val(N1, N2)),
            (0x4, X, N1, N2) => self.skip_not_equals_reg_val(X, CPU::to_val(N1, N2)),
            (0x5, X, Y, 0x0) => self.skip_equals_regs(X, Y),
            (0x6, X, N1, N2) => self.mov_reg_val(X, CPU::to_val(N1, N2)),
            (0x7, X, N1, N2) => self.add_reg_val(X, CPU::to_val(N1, N2)),
            (0x8, X, Y, 0x0) => self.mov_regs(X, Y),
            (0x8, X, Y, 0x1) => self.or_regs(X, Y),
            (0x8, X, Y, 0x2) => self.and_regs(X, Y),
            (0x8, X, Y, 0x3) => self.xor_regs(X, Y),
            (0x8, X, Y, 0x4) => self.add_regs(X, Y),
            (0x8, X, Y, 0x5) => self.sub_regs(X, Y),
            (0x8, X, _, 0x6) => self.shift_right(X),
            (0x8, X, Y, 0x7) => self.sub_regs(Y, X),
            (0x8, X, _, 0xE) => self.shift_left(X),
            (0x9, X, Y, 0x0) => self.skip_not_equals_regs(X, Y),
            (0xA, N1, N2, N3) => self.set_i(CPU::to_addr(N1, N2, N3)),
            (0xB, N1, N2, N3) => self.jump_val_reg0(CPU::to_addr(N1, N2, N3)),
            (0xC, X, N1, N2) => self.rand(X, CPU::to_val(N1, N2)),
            (0xD, X, Y, N) => {},
            (0xE, X, 0x9, 0xE) => {},
            (0xE, X, 0xA, 0x1) => {},
            (0xF, X, 0x0, 0x7) => {},
            (0xF, X, 0x0, 0xA) => {},
            (0xF, X, 0x1, 0x5) => self.set_reg_delay(X),
            (0xF, X, 0x1, 0x8) => self.set_reg_sound(X),
            (0xF, X, 0x1, 0xE) => self.add_reg_index(X),
            (0xF, X, 0x2, 0x9) =>  {},
            (0xF, X, 0x3, 0x3) => self.binary_decimal(X),
            (0xF, X, 0x5, 0x5) => self.store_regs(X),
            (0xF, X, 0x6, 0x5) => self.load_regs(X),
            _ => fail!("Unknown opcode {:x}",opcode)
        }
    }

    fn reset(&mut self) {
        self.pc = 0x200;
        self.sp = 0;
    }

    fn pop(&mut self) -> u16 {
        let item = self.stack[self.sp];
        self.sp -= 1;
        return item;

    }

    fn push(&mut self, val:u16) {
        self.sp += 1;
        self.stack[self.sp] = val;
    }

    fn inc_pc(&mut self) {
        self.pc += 2;
        self.pc %= MAX_RAM;
    }

    /* Jump to address at 0NNN */
    fn call(&mut self, addr:u16) {
        self.pc = addr & 0xFFF;
    }

    /* Clear the screen */
    fn clear_screen(&mut self) {

    }

    /* Return from a subroutine */
    fn ret(&mut self) {
        self.pc = self.pop();
    }

    fn jump(&mut self, addr: u16) {
        self.pc = addr;
    }

    /* Skip next instruction if register X is equal to NN */
    fn skip_equals_reg_val(&mut self, reg:u8, val:u8) {
        if self.registers[reg as uint] == val {
            self.inc_pc();
        }

    }

    fn skip_not_equals_reg_val(&mut self, reg:u8, val:u8) {
        if self.registers[reg as uint] != val {
            self.inc_pc();
        }
    }

    fn skip_equals_regs(&mut self, reg1:u8, reg2:u8) {

        if self.registers[reg1 as uint] == self.registers[reg2 as uint] {
               self.inc_pc();
        }
    }

    fn skip_not_equals_regs(&mut self, reg1:u8, reg2:u8) {
        if self.registers[reg1 as uint] != self.registers[reg2 as uint] {
               self.inc_pc();
           }
    }

    fn mov_reg_val(&mut self, reg:u8, val:u8) {
        self.registers[reg as uint] = val;
    }

    fn add_reg_val(&mut self, reg:u8, val:u8) {
        self.registers[reg as uint] += val;
    }

    fn mov_regs(&mut self, reg1:u8, reg2:u8) {
        self.registers[reg1 as uint] = self.registers[reg2 as uint];
    }

    fn or_regs(&mut self, reg1:u8, reg2:u8) {
       self.registers[reg1 as uint] |= self.registers[reg2 as uint];
    }

    fn and_regs(&mut self, reg1:u8, reg2:u8) {
        self.registers[reg1 as uint] |= self.registers[reg2 as uint];
    }

    fn xor_regs(&mut self, reg1:u8, reg2:u8) {
        self.registers[reg1 as uint] ^= self.registers[reg2 as uint];
    }

    /* Add two regs, if overflow set flag register otherwise
     * unset flag register */
    fn add_regs(&mut self, reg1:u8, reg2:u8) {
        self.registers[FLAG] =  bool::to_bit::<u8>(0xFF - reg1 > reg2);
        self.registers[reg1 as uint] += self.registers[reg2 as uint];
    }

    /* Sub reg2 from reg1, unset flag register if underflow,
     * set otherwise */
    fn sub_regs(&mut self, reg1:u8, reg2:u8) {
        self.registers[FLAG] = bool::to_bit::<u8>(reg2 < reg1);
        self.registers[reg1 as uint] -= self.registers[reg2 as uint];
    }

    /* Sub reg1 from reg2, unset flag register if underflow,
     * set otherwise */
    fn sub_regs_inv(&mut self, reg1:u8, reg2:u8) {
        self.registers[FLAG] = bool::to_bit::<u8>(reg1 < reg2);
        self.registers[reg2 as uint] -= self.registers[reg1 as uint];
    }

    /* Shift register left by 1, set flag register to most significant bit
     * before shifting */
    fn shift_left(&mut self, reg:u8) {
        self.registers[FLAG] = (self.registers[reg as uint] & 0x80) >> 7;
        self.registers[reg as uint] <<= 1;
    }

    /* Shift register right by 1, set flag register to least significant bit
     * before shifting */
    fn shift_right(&mut self, reg:u8) {
        self.registers[FLAG] = self.registers[reg as uint] & 0x1;
        self.registers[reg as uint] >>= 1;
    }

    /* Set index register to address in opcode */
    fn set_i(&mut self, addr:u16) {
        self.I = addr;
    }

    fn jump_val_reg0(&mut self, addr:u16) {
        self.pc = self.registers[0] as u16 + addr;
    }

    fn rand(&mut self, reg:u8, val:u8) {
        self.registers[reg as uint] = val & random::<u8>();
    }

    fn set_reg_delay(&mut self, reg:u8) {
        self.registers[reg as uint] = self.delay_timer;
    }

    fn set_reg_sound(&mut self, reg:u8) {
        self.registers[reg as uint] = self.sound_timer;
    }

    fn add_reg_index(&mut self, reg:u8) {
        self.registers[FLAG] =
            bool::to_bit::<u8>(0xFFF - (self.registers[reg as uint] as u16) < self.I);
        self.I += self.registers[reg as uint] as u16
    }

    fn store_regs(&mut self, max_reg:u8) {
        let regs = self.registers.slice_to(max_reg as uint + 1).iter();
        let store = self.mem.mut_slice_from(self.I as uint).mut_iter();
        for (mem, reg) in store.zip(regs) {
            *mem = *reg;
        }

    }

    fn load_regs(&mut self, max_reg:u8) {
        let regs = self.registers.mut_slice_to(max_reg as uint + 1).mut_iter();
        let store = self.mem.slice_from(self.I as uint).iter();
        for (mem, reg) in store.zip(regs) {
            *reg = *mem;
        }
   }

    /*stores the Binary-coded decimal representation of VX, with the
     * most significant of three digits at the address in I, the middle digit
     * at I + 1, and the lsg at I + 2.*/
    fn binary_decimal(&mut self, reg:u8) {
        let val = self.registers[reg as uint];
        self.mem[self.I as uint] = val/100;
        self.mem[(self.I + 1) as uint] = (val % 100)/10;
        self.mem[(self.I + 2) as uint] = (val % 100)%10;

    }

}

fn main() {

    print!("yay");
}

<|endoftext|>


<commit_before>use std::time::Duration;

use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion};

// The benchmarks here verify that the complexity grows as O(*n*)
// where *n* is the number of characters in the text to be wrapped.

use lipsum::lipsum_words_from_seed;

const LINE_LENGTH: usize = 60;

/// Generate a lorem ipsum text with the given number of characters.
fn lorem_ipsum(length: usize) -> String {
    // The average word length in the lorem ipsum text is somewhere
    // between 6 and 7. So we conservatively divide by 5 to have a
    // long enough text that we can truncate below.
    let mut text = lipsum_words_from_seed(length / 5, 42);
    text.truncate(length);
    text
}

pub fn benchmark(c: &mut Criterion) {
    let mut group = c.benchmark_group("String lengths");
    for length in [200, 300, 400, 600, 800, 1200, 1600, 2400, 3200, 4800, 6400] {
        let text = lorem_ipsum(length);

        let options = textwrap::Options::new(LINE_LENGTH)
            .wrap_algorithm(textwrap::WrapAlgorithm::new_optimal_fit())
            .word_separator(textwrap::WordSeparator::UnicodeBreakProperties);
        group.bench_with_input(
            BenchmarkId::new("fill_optimal_fit_unicode", length),
            &text,
            |b, text| {
                b.iter(|| textwrap::fill(text, &options));
            },
        );

        let options = textwrap::Options::new(LINE_LENGTH)
            .wrap_algorithm(textwrap::WrapAlgorithm::new_optimal_fit())
            .word_separator(textwrap::WordSeparator::AsciiSpace);
        group.bench_with_input(
            BenchmarkId::new("fill_optimal_fit_ascii", length),
            &text,
            |b, text| {
                b.iter(|| textwrap::fill(text, &options));
            },
        );

        let options = textwrap::Options::new(LINE_LENGTH)
            .wrap_algorithm(textwrap::WrapAlgorithm::FirstFit)
            .word_separator(textwrap::WordSeparator::AsciiSpace);
        group.bench_with_input(
            BenchmarkId::new("fill_first_fit", length),
            &text,
            |b, text| {
                b.iter(|| textwrap::fill(text, &options));
            },
        );

        group.bench_function(BenchmarkId::new("fill_inplace", length), |b| {
            b.iter_batched(
                || text.clone(),
                |mut text| textwrap::fill_inplace(&mut text, LINE_LENGTH),
                criterion::BatchSize::SmallInput,
            );
        });

        use hyphenation::{Language, Load, Standard};
        let path = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("la.standard.bincode");
        let dictionary = Standard::from_path(Language::Latin, &path).unwrap();
        let options = textwrap::Options::new(LINE_LENGTH)
            .wrap_algorithm(textwrap::WrapAlgorithm::new_optimal_fit())
            .word_separator(textwrap::WordSeparator::AsciiSpace)
            .word_splitter(textwrap::WordSplitter::Hyphenation(dictionary));
        group.bench_with_input(
            BenchmarkId::new("fill_optimal_fit_ascii_hyphenation", length),
            &text,
            |b, text| {
                b.iter(|| textwrap::fill(text, &options));
            },
        );
    }
    group.finish();
}

criterion_group!(
    name = benches;
    config = Criterion::default().warm_up_time(Duration::from_millis(500));
    targets = benchmark
);
criterion_main!(benches);
<commit_msg>Increase benchmark precision to measure fast paths<commit_after>use std::time::Duration;

use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion};

// The benchmarks here verify that the complexity grows as O(*n*)
// where *n* is the number of characters in the text to be wrapped.

use lipsum::lipsum_words_from_seed;

const LINE_LENGTH: usize = 60;

/// Generate a lorem ipsum text with the given number of characters.
fn lorem_ipsum(length: usize) -> String {
    // The average word length in the lorem ipsum text is somewhere
    // between 6 and 7. So we conservatively divide by 5 to have a
    // long enough text that we can truncate below.
    let mut text = lipsum_words_from_seed(length / 5, 42);
    text.truncate(length);
    text
}

pub fn benchmark(c: &mut Criterion) {
    let mut group = c.benchmark_group("String lengths");
    let lengths = [
        0, 5, 10, 20, 30, 40, 50, 60, 80, 100, 200, 300, 400, 600, 800, 1200, 1600, 2400, 3200,
        4800, 6400,
    ];
    for length in lengths {
        let text = lorem_ipsum(length);

        let options = textwrap::Options::new(LINE_LENGTH)
            .wrap_algorithm(textwrap::WrapAlgorithm::new_optimal_fit())
            .word_separator(textwrap::WordSeparator::UnicodeBreakProperties);
        group.bench_with_input(
            BenchmarkId::new("fill_optimal_fit_unicode", length),
            &text,
            |b, text| {
                b.iter(|| textwrap::fill(text, &options));
            },
        );

        let options = textwrap::Options::new(LINE_LENGTH)
            .wrap_algorithm(textwrap::WrapAlgorithm::new_optimal_fit())
            .word_separator(textwrap::WordSeparator::AsciiSpace);
        group.bench_with_input(
            BenchmarkId::new("fill_optimal_fit_ascii", length),
            &text,
            |b, text| {
                b.iter(|| textwrap::fill(text, &options));
            },
        );

        let options = textwrap::Options::new(LINE_LENGTH)
            .wrap_algorithm(textwrap::WrapAlgorithm::FirstFit)
            .word_separator(textwrap::WordSeparator::AsciiSpace);
        group.bench_with_input(
            BenchmarkId::new("fill_first_fit", length),
            &text,
            |b, text| {
                b.iter(|| textwrap::fill(text, &options));
            },
        );

        group.bench_function(BenchmarkId::new("fill_inplace", length), |b| {
            b.iter_batched(
                || text.clone(),
                |mut text| textwrap::fill_inplace(&mut text, LINE_LENGTH),
                criterion::BatchSize::SmallInput,
            );
        });

        use hyphenation::{Language, Load, Standard};
        let path = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("la.standard.bincode");
        let dictionary = Standard::from_path(Language::Latin, &path).unwrap();
        let options = textwrap::Options::new(LINE_LENGTH)
            .wrap_algorithm(textwrap::WrapAlgorithm::new_optimal_fit())
            .word_separator(textwrap::WordSeparator::AsciiSpace)
            .word_splitter(textwrap::WordSplitter::Hyphenation(dictionary));
        group.bench_with_input(
            BenchmarkId::new("fill_optimal_fit_ascii_hyphenation", length),
            &text,
            |b, text| {
                b.iter(|| textwrap::fill(text, &options));
            },
        );
    }
    group.finish();
}

criterion_group!(
    name = benches;
    config = Criterion::default().warm_up_time(Duration::from_millis(500));
    targets = benchmark
);
criterion_main!(benches);
<|endoftext|>


<commit_before>use std::fs;
use std::path;
use std::process;
use std::sync::mpsc::channel;
use std::thread;
use std::time;

use actix_files;
use actix_web::middleware::errhandlers::{ErrorHandlerResponse, ErrorHandlers};
use actix_web::{dev, http, App, HttpResponse, HttpServer};
use clap;
use cobalt::cobalt_model;
use failure::ResultExt;
use notify;
use notify::Watcher;

use crate::args;
use crate::build;
use crate::error::*;

pub fn serve_command_args() -> clap::App<'static, 'static> {
    clap::SubCommand::with_name("serve")
        .about("build, serve, and watch the project at the source dir")
        .args(&args::get_config_args())
        .arg(
            clap::Arg::with_name("port")
                .short("P")
                .long("port")
                .value_name("INT")
                .help("Port to serve from")
                .default_value("3000")
                .takes_value(true),
        )
        .arg(
            clap::Arg::with_name("host")
                .long("host")
                .value_name("host-name/IP")
                .help("Host to serve from")
                .default_value("localhost")
                .takes_value(true),
        )
        .arg(
            clap::Arg::with_name("no-watch")
                .long("no-watch")
                .help("Disable rebuilding on change")
                .conflicts_with("drafts")
                .takes_value(false),
        )
}

pub fn serve_command(matches: &clap::ArgMatches) -> Result<()> {
    let host = matches.value_of("host").unwrap().to_string();
    let port = matches.value_of("port").unwrap().to_string();
    let ip = format!("{}:{}", host, port);

    let mut config = args::get_config(matches)?;
    debug!("Overriding config `site.base_url` with `{}`", ip);
    config.site.base_url = Some(format!("http://{}", ip));
    let config = cobalt::cobalt_model::Config::from_config(config)?;
    let dest = path::Path::new(&config.destination).to_owned();

    build::build(config.clone())?;

    if matches.is_present("no-watch") {
        serve(dest, ip)?;
    } else {
        info!("Watching {:?} for changes", &config.source);
        thread::spawn(move || {
            if serve(dest, ip).is_err() {
                process::exit(1)
            }
        });

        watch(&config)?;
    }
    Ok(())
}

fn watch(config: &cobalt_model::Config) -> Result<()> {
    // canonicalize is to ensure there is no question that `watcher`s paths come back safe for
    // Files::includes_file
    let source = path::Path::new(&config.source)
        .canonicalize()
        .with_context(|_| failure::err_msg("Failed in processing source"))?;

    let (tx, rx) = channel();
    let mut watcher = notify::watcher(tx, time::Duration::from_secs(1))
        .with_context(|_| failure::err_msg("Notify error"))?;
    watcher
        .watch(&source, notify::RecursiveMode::Recursive)
        .with_context(|_| failure::err_msg("Notify error"))?;

    loop {
        let event = rx
            .recv()
            .with_context(|_| failure::err_msg("Notify error"))?;
        let event_path = match event {
            notify::DebouncedEvent::Create(ref path)
            | notify::DebouncedEvent::NoticeWrite(ref path)
            | notify::DebouncedEvent::Write(ref path)
            | notify::DebouncedEvent::NoticeRemove(ref path)
            | notify::DebouncedEvent::Remove(ref path) => Some(path),
            notify::DebouncedEvent::Rename(_, ref to) => Some(to),
            _ => None,
        };
        let rebuild = if let Some(event_path) = event_path {
            // Be as broad as possible in what can cause a rebuild to
            // ensure we don't miss anything (normal file walks will miss
            // `_layouts`, etc).
            if event_path.starts_with(&config.destination) {
                trace!("Ignored file changed {:?}", event);
                false
            } else {
                debug!("Page changed {:?}", event);
                true
            }
        } else {
            trace!("Assuming change {:?} is relevant", event);
            true
        };
        if rebuild {
            let result = build::build(config.clone());
            if let Err(fail) = result {
                let fail: exitfailure::ExitFailure = fail.into();
                error!("build failed\n{:?}", fail);
            }
        }
    }
}

#[actix_rt::main]
async fn serve(dest: path::PathBuf, ip: String) -> Result<()> {
    info!("Serving {:?} through static file server", dest);

    info!("Server Listening on http://{}", &ip);
    info!("Ctrl-c to stop the server");

    let s = HttpServer::new(move || {
        let not_found_path = dest.join("404.html");
        let error_handlers = if not_found_path.is_file() {
            ErrorHandlers::new().handler(http::StatusCode::NOT_FOUND, not_found)
        } else {
            ErrorHandlers::new()
        };

        App::new()
            .data(ErrorFilePaths {
                not_found: not_found_path,
            })
            .wrap(error_handlers)
            // Start a webserver that serves the `output_dir` directory
            .service(actix_files::Files::new("/", &dest).index_file("index.html"))
    })
    .bind(&ip)
    .expect("Can't start the webserver")
    .shutdown_timeout(20);
    s.run().await?;

    Ok(())
}

struct ErrorFilePaths {
    not_found: path::PathBuf,
}

fn not_found<B>(
    res: dev::ServiceResponse<B>,
) -> std::result::Result<ErrorHandlerResponse<B>, actix_web::Error> {
    let buf: Vec<u8> = {
        let error_files: &ErrorFilePaths = res.request().app_data().unwrap();

        fs::read(&error_files.not_found)?
    };

    let new_resp = HttpResponse::build(http::StatusCode::NOT_FOUND)
        .header(
            http::header::CONTENT_TYPE,
            http::header::HeaderValue::from_static("text/html"),
        )
        .body(buf);

    Ok(ErrorHandlerResponse::Response(
        res.into_response(new_resp.into_body()),
    ))
}
<commit_msg>fix(serve): Ctrl-c works<commit_after>use std::fs;
use std::path;
use std::process;
use std::sync::mpsc::channel;
use std::thread;
use std::time;

use actix_files;
use actix_web::middleware::errhandlers::{ErrorHandlerResponse, ErrorHandlers};
use actix_web::{dev, http, App, HttpResponse, HttpServer};
use clap;
use cobalt::cobalt_model;
use failure::ResultExt;
use notify;
use notify::Watcher;

use crate::args;
use crate::build;
use crate::error::*;

pub fn serve_command_args() -> clap::App<'static, 'static> {
    clap::SubCommand::with_name("serve")
        .about("build, serve, and watch the project at the source dir")
        .args(&args::get_config_args())
        .arg(
            clap::Arg::with_name("port")
                .short("P")
                .long("port")
                .value_name("INT")
                .help("Port to serve from")
                .default_value("3000")
                .takes_value(true),
        )
        .arg(
            clap::Arg::with_name("host")
                .long("host")
                .value_name("host-name/IP")
                .help("Host to serve from")
                .default_value("localhost")
                .takes_value(true),
        )
        .arg(
            clap::Arg::with_name("no-watch")
                .long("no-watch")
                .help("Disable rebuilding on change")
                .conflicts_with("drafts")
                .takes_value(false),
        )
}

pub fn serve_command(matches: &clap::ArgMatches) -> Result<()> {
    let host = matches.value_of("host").unwrap().to_string();
    let port = matches.value_of("port").unwrap().to_string();
    let ip = format!("{}:{}", host, port);

    let mut config = args::get_config(matches)?;
    debug!("Overriding config `site.base_url` with `{}`", ip);
    config.site.base_url = Some(format!("http://{}", ip));
    let config = cobalt::cobalt_model::Config::from_config(config)?;
    let dest = path::Path::new(&config.destination).to_owned();

    build::build(config.clone())?;

    if matches.is_present("no-watch") {
        serve(dest, ip)?;
    } else {
        info!("Watching {:?} for changes", &config.source);
        thread::spawn(move || {
            let e = serve(dest, ip);
            if let Some(e) = e.err() {
                error!("{}", e);
            }
            process::exit(1)
        });

        watch(&config)?;
    }
    Ok(())
}

fn watch(config: &cobalt_model::Config) -> Result<()> {
    // canonicalize is to ensure there is no question that `watcher`s paths come back safe for
    // Files::includes_file
    let source = path::Path::new(&config.source)
        .canonicalize()
        .with_context(|_| failure::err_msg("Failed in processing source"))?;

    let (tx, rx) = channel();
    let mut watcher = notify::watcher(tx, time::Duration::from_secs(1))
        .with_context(|_| failure::err_msg("Notify error"))?;
    watcher
        .watch(&source, notify::RecursiveMode::Recursive)
        .with_context(|_| failure::err_msg("Notify error"))?;

    loop {
        let event = rx
            .recv()
            .with_context(|_| failure::err_msg("Notify error"))?;
        let event_path = match event {
            notify::DebouncedEvent::Create(ref path)
            | notify::DebouncedEvent::NoticeWrite(ref path)
            | notify::DebouncedEvent::Write(ref path)
            | notify::DebouncedEvent::NoticeRemove(ref path)
            | notify::DebouncedEvent::Remove(ref path) => Some(path),
            notify::DebouncedEvent::Rename(_, ref to) => Some(to),
            _ => None,
        };
        let rebuild = if let Some(event_path) = event_path {
            // Be as broad as possible in what can cause a rebuild to
            // ensure we don't miss anything (normal file walks will miss
            // `_layouts`, etc).
            if event_path.starts_with(&config.destination) {
                trace!("Ignored file changed {:?}", event);
                false
            } else {
                debug!("Page changed {:?}", event);
                true
            }
        } else {
            trace!("Assuming change {:?} is relevant", event);
            true
        };
        if rebuild {
            let result = build::build(config.clone());
            if let Err(fail) = result {
                let fail: exitfailure::ExitFailure = fail.into();
                error!("build failed\n{:?}", fail);
            }
        }
    }
}

#[actix_rt::main]
async fn serve(dest: path::PathBuf, ip: String) -> Result<()> {
    info!("Serving {:?} through static file server", dest);

    info!("Server Listening on http://{}", &ip);
    info!("Ctrl-c to stop the server");

    let s = HttpServer::new(move || {
        let not_found_path = dest.join("404.html");
        let error_handlers = if not_found_path.is_file() {
            ErrorHandlers::new().handler(http::StatusCode::NOT_FOUND, not_found)
        } else {
            ErrorHandlers::new()
        };

        App::new()
            .data(ErrorFilePaths {
                not_found: not_found_path,
            })
            .wrap(error_handlers)
            // Start a webserver that serves the `output_dir` directory
            .service(actix_files::Files::new("/", &dest).index_file("index.html"))
    })
    .bind(&ip)
    .expect("Can't start the webserver")
    .shutdown_timeout(20);
    s.run().await?;

    Ok(())
}

struct ErrorFilePaths {
    not_found: path::PathBuf,
}

fn not_found<B>(
    res: dev::ServiceResponse<B>,
) -> std::result::Result<ErrorHandlerResponse<B>, actix_web::Error> {
    let buf: Vec<u8> = {
        let error_files: &ErrorFilePaths = res.request().app_data().unwrap();

        fs::read(&error_files.not_found)?
    };

    let new_resp = HttpResponse::build(http::StatusCode::NOT_FOUND)
        .header(
            http::header::CONTENT_TYPE,
            http::header::HeaderValue::from_static("text/html"),
        )
        .body(buf);

    Ok(ErrorHandlerResponse::Response(
        res.into_response(new_resp.into_body()),
    ))
}
<|endoftext|>


<commit_before>extern crate num_traits;
extern crate godcoin;
extern crate tokio;
extern crate dirs;
extern crate clap;

use clap::{Arg, App, AppSettings, SubCommand};
use tokio::prelude::*;

struct StartNode<'a> {
    bind_address: Option<&'a str>,
}

fn generate_keypair() {
    use godcoin::crypto::*;

    let pair = KeyPair::gen_keypair();
    println!("~~ Keys have been generated ~~");
    println!("Private key WIF: {}", pair.1.to_wif());
    println!("Public key WIF: {}", pair.0.to_wif());
    println!("- Make sure the keys are securely stored");
    println!("- Coins cannot be recovered if you lose your private key");
    println!("- Never give private keys to anyone");
}

fn start_node(node_opts: StartNode) {
    use std::{env, path::Path, path::PathBuf};
    use godcoin::net::*;

    let home: PathBuf = {
        use dirs;
        let home = env::var("GODCOIN_HOME").map(|s| {
            PathBuf::from(s)
        }).unwrap_or_else(|_| {
            Path::join(&dirs::data_local_dir().unwrap(), "godcoin")
        });
        if !Path::is_dir(&home) {
            let res = std::fs::create_dir(&home);
            res.expect(&format!("Failed to create dir at {:?}", &home));
        }
        home
    }.canonicalize().unwrap();
    println!("Found GODcoin home at {:?}", &home);

    if let Some(bind) = node_opts.bind_address {
        let addr = bind.parse();
        match addr {
            Ok(ref addr) => start_server(addr),
            Err(e) => panic!("Failed to parse address: {:?}", e)
        }
    }
}

fn main() {
    godcoin::init().unwrap();

    let app = App::new("godcoin")
                .about("GODcoin core CLI")
                .version(env!("CARGO_PKG_VERSION"))
                .setting(AppSettings::VersionlessSubcommands)
                .setting(AppSettings::SubcommandRequiredElseHelp)
                .subcommand(SubCommand::with_name("keygen")
                            .about("Generates a keypair"))
                .subcommand(SubCommand::with_name("node")
                            .about("Starts the blockchain node service")
                            .arg(Arg::with_name("bind_address")
                                .help("Bind address endpoint (i.e 0.0.0.0:7777)")
                                .long("bind")
                                .value_name("address")));
    let matches = app.get_matches();

    ::tokio::run(future::lazy(move || {
        use ::std::io::{Error, ErrorKind};

        if let Some(_) = matches.subcommand_matches("keygen") {
            generate_keypair();
        } else if let Some(matches) = matches.subcommand_matches("node") {
            start_node(StartNode {
                bind_address: matches.value_of("bind_address")
            });
        } else {
            return Err(Error::new(ErrorKind::Other, "Failed to match subcommand"))
        }

        Ok(())
    }).map_err(|err| {
        println!("Startup failure: {:?}", err);
    }));
}
<commit_msg>Better displaying of errors and initialization<commit_after>extern crate num_traits;
extern crate godcoin;
extern crate tokio;
extern crate dirs;
extern crate clap;

use clap::{Arg, App, AppSettings, SubCommand};
use tokio::prelude::*;

struct StartNode<'a> {
    bind_address: Option<&'a str>,
}

fn generate_keypair() {
    use godcoin::crypto::*;

    let pair = KeyPair::gen_keypair();
    println!("~~ Keys have been generated ~~");
    println!("Private key WIF: {}", pair.1.to_wif());
    println!("Public key WIF: {}", pair.0.to_wif());
    println!("- Make sure the keys are securely stored");
    println!("- Coins cannot be recovered if you lose your private key");
    println!("- Never give private keys to anyone");
}

fn start_node(node_opts: StartNode) {
    use std::{env, path::Path, path::PathBuf};
    use godcoin::net::*;

    let home: PathBuf = {
        use dirs;
        let home = env::var("GODCOIN_HOME").map(|s| {
            PathBuf::from(s)
        }).unwrap_or_else(|_| {
            Path::join(&dirs::data_local_dir().unwrap(), "godcoin")
        });
        if !Path::is_dir(&home) {
            let res = std::fs::create_dir(&home);
            res.expect(&format!("Failed to create dir at {:?}", &home));
            println!("Created GODcoin home at {:?}", &home);
        } else {
            println!("Found GODcoin home at {:?}", &home);
        }
        home
    }.canonicalize().unwrap();

    if let Some(bind) = node_opts.bind_address {
        let addr = bind.parse()
                        .expect(&format!("Failed to parse address: {:?}", bind));
        net::start_server(&addr);
    }
}

fn main() {
    godcoin::init().unwrap();

    let app = App::new("godcoin")
                .about("GODcoin core CLI")
                .version(env!("CARGO_PKG_VERSION"))
                .setting(AppSettings::VersionlessSubcommands)
                .setting(AppSettings::SubcommandRequiredElseHelp)
                .subcommand(SubCommand::with_name("keygen")
                            .about("Generates a keypair"))
                .subcommand(SubCommand::with_name("node")
                            .about("Starts the blockchain node service")
                            .arg(Arg::with_name("bind_address")
                                .help("Bind address endpoint (i.e 0.0.0.0:7777)")
                                .long("bind")
                                .value_name("address")));
    let matches = app.get_matches();

    ::tokio::run(future::lazy(move || {
        use ::std::io::{Error, ErrorKind};

        if let Some(_) = matches.subcommand_matches("keygen") {
            generate_keypair();
        } else if let Some(matches) = matches.subcommand_matches("node") {
            start_node(StartNode {
                bind_address: matches.value_of("bind_address")
            });
        } else {
            return Err(Error::new(ErrorKind::Other, "Failed to match subcommand"))
        }

        Ok(())
    }).map_err(|err| {
        println!("Startup failure: {:?}", err);
    }));
}
<|endoftext|>


<commit_before>use super::paths;
use pulldown_cmark::{html, Options, Parser};
use std::fs;

// Removes any previously generated output
pub fn clean() {
    paths::remove_dir(&paths::public());
}

// Generates a static HTML bundle from markdown notes
pub fn build() {
    paths::create_dir(&paths::public());

    match fs::read_dir(&paths::notes()) {
        Ok(markdown_files) => {
            let mut markdown_links_to_routes = Vec::new();

            for markdown_file in markdown_files {
                let markdown_file = match &markdown_file {
                    Ok(markdown_file) => markdown_file.path(),
                    Err(error) => panic!("There was a problem: {:?}", error),
                };

                let markdown_file_contents = match fs::read_to_string(&markdown_file) {
                    Ok(contents) => contents,
                    Err(error) => panic!("There was a problem: {:?}", error),
                };

                let route = &paths::file_stem(markdown_file);
                paths::create_dir(&paths::public().join(route));
                paths::create_file(
                    &paths::public().join(route).join("index.html"),
                    &markdown_to_html(&markdown_file_contents),
                );
                markdown_links_to_routes.push(format!("- [{}](/{})", route, route).to_string());
            }

            let markdown_home = markdown_links_to_routes.join("\n");

            paths::create_file(
                &paths::public().join("index.html"),
                &markdown_to_html(&markdown_home),
            );
        }
        Err(error) => panic!("There was a problem: {:?}", error),
    };
}

// Adds a CNAME file for the host (GitHub Pages) and registrar (Hover) to use my custom domain name (trevordmiller.com)
pub fn configure() {
    paths::create_file(&paths::cname(), "trevordmiller.com");
}

// Pushes output to my GitHub Pages repo (https://github.com/trevordmiller/trevordmiller.github.io)
pub fn deploy() {}

fn markdown_to_html(markdown: &str) -> std::string::String {
    let parser = Parser::new_ext(markdown, Options::empty());
    let mut html = String::new();
    html::push_html(&mut html, parser);

    format!(
        "
        <!DOCTYPE html>

                <meta charset=\"utf-8\">
                <meta name=\"author\" content=\"Trevor D. Miller\">
                <meta name=\"description\" content=\"Personal website.\">
                <title>trevordmiller.com</title>

Header

                        {}

Footer

    ",
        &html
    )
}
<commit_msg>Use note titles for website home page link text<commit_after>use super::paths;
use pulldown_cmark::{html, Options, Parser};
use std::fs;

// Removes any previously generated output
pub fn clean() {
    paths::remove_dir(&paths::public());
}

// Generates a static HTML bundle from markdown notes
pub fn build() {
    paths::create_dir(&paths::public());

    match fs::read_dir(&paths::notes()) {
        Ok(markdown_files) => {
            let mut markdown_links_to_routes = Vec::new();

            for markdown_file in markdown_files {
                let markdown_file = match &markdown_file {
                    Ok(markdown_file) => markdown_file.path(),
                    Err(error) => panic!("There was a problem: {:?}", error),
                };

                let markdown_file_contents = match fs::read_to_string(&markdown_file) {
                    Ok(contents) => contents,
                    Err(error) => panic!("There was a problem: {:?}", error),
                };

                let route = &paths::file_stem(markdown_file);
                paths::create_dir(&paths::public().join(route));
                paths::create_file(
                    &paths::public().join(route).join("index.html"),
                    &markdown_to_html(&markdown_file_contents),
                );

                let title = match markdown_file_contents.lines().next() {
                    Some(title) => &title[2..],
                    None => panic!("Cannot find a title in {}.", &route),
                };
                markdown_links_to_routes.push(format!("- [{}](/{})", title, route).to_string());
            }

            let markdown_home = markdown_links_to_routes.join("\n");

            paths::create_file(
                &paths::public().join("index.html"),
                &markdown_to_html(&markdown_home),
            );
        }
        Err(error) => panic!("There was a problem: {:?}", error),
    };
}

// Adds a CNAME file for the host (GitHub Pages) and registrar (Hover) to use my custom domain name (trevordmiller.com)
pub fn configure() {
    paths::create_file(&paths::cname(), "trevordmiller.com");
}

// Pushes output to my GitHub Pages repo (https://github.com/trevordmiller/trevordmiller.github.io)
pub fn deploy() {}

fn markdown_to_html(markdown: &str) -> std::string::String {
    let parser = Parser::new_ext(markdown, Options::empty());
    let mut html = String::new();
    html::push_html(&mut html, parser);

    format!(
        "
        <!DOCTYPE html>

                <meta charset=\"utf-8\">
                <meta name=\"author\" content=\"Trevor D. Miller\">
                <meta name=\"description\" content=\"Personal website.\">
                <title>trevordmiller.com</title>

Header

                        {}

Footer

    ",
        &html
    )
}
<|endoftext|>


<commit_before>use libzakopane::config::Config;
use libzakopane::snapshot::Snapshot;
use libzakopane::structs::CliOptions;
use libzakopane::structs::ZakopaneError;

use clap::{App, Arg, ArgMatches, SubCommand};

const DEFAULT_POLICY_ARG_NAME: &'static str = "default-policy";
const CONFIG_FILE_ARG_NAME: &'static str = "config";
const OLD_SNAPSHOT_PATH_ARG_NAME: &'static str = "old-snapshot-path";
const NEW_SNAPSHOT_PATH_ARG_NAME: &'static str = "new-snapshot-path";

// Holds one instance of each struct necessary to operate.
struct OperationalData {
    config: Config,
    old_snapshot: Snapshot,
    new_snapshot: Snapshot,
}

// Reads parsed command-line arguments and returns the appropriate
// operational data. Can abort the program on error.
fn complete_initialization(matches: &ArgMatches) -> Result<OperationalData, ZakopaneError> {
    // The two snapshot paths are required, so these are safe to unwrap.
    let old_snapshot_path = matches.value_of(OLD_SNAPSHOT_PATH_ARG_NAME).unwrap();
    let new_snapshot_path = matches.value_of(NEW_SNAPSHOT_PATH_ARG_NAME).unwrap();
    let old_contents = libzakopane::helpers::ingest_file(old_snapshot_path)?;
    let new_contents = libzakopane::helpers::ingest_file(new_snapshot_path)?;

    let options = CliOptions {
        config_path: matches.value_of(CONFIG_FILE_ARG_NAME),
        default_policy: matches.value_of(DEFAULT_POLICY_ARG_NAME),
    };

    Ok(OperationalData {
        config: Config::new(&options)?,
        old_snapshot: Snapshot::new(&old_contents)?,
        new_snapshot: Snapshot::new(&new_contents)?,
    })
}

// Begins parsing command-line arguments. Can abort the program on
// error.
fn initialize() -> Result<OperationalData, ZakopaneError> {
    let matches = App::new("zakopane")
        .version("0.2.0")
        .author("j39m")
        .about("checksums directories")
        .subcommand(
            SubCommand::with_name("compare")
                .about("compares two zakopane snapshots")
                .arg(
                    Arg::with_name(CONFIG_FILE_ARG_NAME)
                        .short("c")
                        .long("config")
                        .value_name("FILE")
                        .help("specifies a zakopane config")
                        .takes_value(true),
                )
                .arg(
                    Arg::with_name(DEFAULT_POLICY_ARG_NAME)
                        .short("d")
                        .long("default-policy")
                        .value_name("POLICY_TOKENS")
                        .help("specifies an explicit default policy")
                        .takes_value(true),
                )
                .arg(
                    Arg::with_name(OLD_SNAPSHOT_PATH_ARG_NAME)
                        .help("path to older snapshot")
                        .index(1)
                        .required(true),
                )
                .arg(
                    Arg::with_name(NEW_SNAPSHOT_PATH_ARG_NAME)
                        .help("path to newer snapshot")
                        .index(2)
                        .required(true),
                ),
        )
        .get_matches();
    return complete_initialization(&matches);
}

fn main() {
    let operational_data = match initialize() {
        Ok(data) => data,
        Err(error) => {
            eprintln!("{}", error.to_string());
            std::process::exit(1);
        }
    };

    let OperationalData {
        config,
        new_snapshot,
        old_snapshot,
    } = operational_data;
    assert!(config.rules() > 0);
    let violations = libzakopane::enter(&config, &old_snapshot, &new_snapshot);
    println!("{}", violations);
}
<commit_msg>Fix subcommand parsing<commit_after>use libzakopane::config::Config;
use libzakopane::snapshot::Snapshot;
use libzakopane::structs::CliOptions;
use libzakopane::structs::ZakopaneError;

use clap::{App, Arg, ArgMatches};

const DEFAULT_POLICY_ARG_NAME: &'static str = "default-policy";
const CONFIG_FILE_ARG_NAME: &'static str = "config";
const OLD_SNAPSHOT_PATH_ARG_NAME: &'static str = "old-snapshot-path";
const NEW_SNAPSHOT_PATH_ARG_NAME: &'static str = "new-snapshot-path";

// Holds one instance of each struct necessary to operate.
struct OperationalData {
    config: Config,
    old_snapshot: Snapshot,
    new_snapshot: Snapshot,
}

// Reads parsed command-line arguments and returns the appropriate
// operational data. Can abort the program on error.
fn complete_initialization(matches: &ArgMatches) -> Result<OperationalData, ZakopaneError> {
    // The two snapshot paths are required, so these are safe to unwrap.
    let old_snapshot_path = matches.value_of(OLD_SNAPSHOT_PATH_ARG_NAME).unwrap();
    let new_snapshot_path = matches.value_of(NEW_SNAPSHOT_PATH_ARG_NAME).unwrap();
    let old_contents = libzakopane::helpers::ingest_file(old_snapshot_path)?;
    let new_contents = libzakopane::helpers::ingest_file(new_snapshot_path)?;

    let options = CliOptions {
        config_path: matches.value_of(CONFIG_FILE_ARG_NAME),
        default_policy: matches.value_of(DEFAULT_POLICY_ARG_NAME),
    };

    Ok(OperationalData {
        config: Config::new(&options)?,
        old_snapshot: Snapshot::new(&old_contents)?,
        new_snapshot: Snapshot::new(&new_contents)?,
    })
}

// Begins parsing command-line arguments. Can abort the program on
// error.
fn initialize() -> Result<OperationalData, ZakopaneError> {
    let matches = App::new("zakopane")
        .version("0.2.0")
        .author("j39m")
        .about("checksums directories")
        .subcommand(
            App::new("compare")
                .about("compares two zakopane snapshots")
                .arg(
                    Arg::with_name(CONFIG_FILE_ARG_NAME)
                        .short("c")
                        .long("config")
                        .value_name("FILE")
                        .help("specifies a zakopane config")
                        .takes_value(true),
                )
                .arg(
                    Arg::with_name(DEFAULT_POLICY_ARG_NAME)
                        .short("d")
                        .long("default-policy")
                        .value_name("POLICY_TOKENS")
                        .help("specifies an explicit default policy")
                        .takes_value(true),
                )
                .arg(
                    Arg::with_name(OLD_SNAPSHOT_PATH_ARG_NAME)
                        .help("path to older snapshot")
                        .index(1)
                        .required(true),
                )
                .arg(
                    Arg::with_name(NEW_SNAPSHOT_PATH_ARG_NAME)
                        .help("path to newer snapshot")
                        .index(2)
                        .required(true),
                ),
        )
        .get_matches();

    if let Some(ref matches) = matches.subcommand_matches("compare") {
        return complete_initialization(&matches);
    }
    Err(ZakopaneError::Unknown("not implemented".to_string()))
}

fn main() {
    let operational_data = match initialize() {
        Ok(data) => data,
        Err(error) => {
            eprintln!("{}", error.to_string());
            std::process::exit(1);
        }
    };

    let OperationalData {
        config,
        new_snapshot,
        old_snapshot,
    } = operational_data;
    assert!(config.rules() > 0);
    let violations = libzakopane::enter(&config, &old_snapshot, &new_snapshot);
    println!("{}", violations);
}
<|endoftext|>


<commit_before>//! Song decoding / analysis module.
//!
//! Use decoding, and features-extraction functions from other modules
//! e.g. tempo features, spectral features, etc to build a Song and its
//! corresponding Analysis.
#[cfg(feature = "aubio-lib")]
extern crate aubio_lib;

extern crate crossbeam;
extern crate ffmpeg_next as ffmpeg;
extern crate ndarray;
extern crate ndarray_npy;

use super::CHANNELS;
use crate::chroma::ChromaDesc;
use crate::misc::LoudnessDesc;
use crate::temporal::BPMDesc;
use crate::timbral::{SpectralDesc, ZeroCrossingRateDesc};
use crate::SAMPLE_RATE;
use crate::{Analysis, Song};
use crossbeam::thread;
use ffmpeg::codec::threading::{Config, Type as ThreadingType};
use ffmpeg::util;
use ffmpeg::util::format::sample::{Sample, Type};
use std::sync::mpsc;
use std::thread as std_thread;

pub fn push_to_sample_array(frame: &ffmpeg::frame::Audio, sample_array: &mut Vec<f32>) {
    if frame.samples() == 0 {
        return;
    }
    // Account for the padding
    let actual_size = util::format::sample::Buffer::size(
        Sample::F32(Type::Packed),
        CHANNELS,
        frame.samples(),
        false,
    );
    let f32_frame: Vec<f32> = frame.data(0)[..actual_size]
        .chunks_exact(4)
        .map(|x| {
            let mut a: [u8; 4] = [0; 4];
            a.copy_from_slice(x);
            f32::from_le_bytes(a)
        })
        .collect();
    sample_array.extend_from_slice(&f32_frame);
}

impl Song {
    pub fn new(path: &str) -> Result<Self, String> {
        // TODO error handling here
        let mut song = Song::decode(&path)?;

        song.analysis = (&song).analyze();
        Ok(song)
    }

    pub fn analyze(&self) -> Analysis {
        thread::scope(|s| {
            let child_chroma = s.spawn(|_| {
                let mut chroma_desc = ChromaDesc::new(self.sample_rate, 12);
                chroma_desc.do_(&self.sample_array);
                chroma_desc.get_values()
            });

            // These descriptors can be streamed
            let child_timbral = s.spawn(|_| {
                let mut spectral_desc = SpectralDesc::new(self.sample_rate);
                let mut zcr_desc = ZeroCrossingRateDesc::default();
                let windows = self
                    .sample_array
                    .windows(SpectralDesc::WINDOW_SIZE)
                    .step_by(SpectralDesc::HOP_SIZE);
                for window in windows {
                    spectral_desc.do_(&window);
                    zcr_desc.do_(&window);
                }
                let centroid = spectral_desc.get_centroid();
                let rolloff = spectral_desc.get_rolloff();
                let flatness = spectral_desc.get_flatness();
                let zcr = zcr_desc.get_value();
                (centroid, rolloff, flatness, zcr)
            });

            let child_tempo = s.spawn(|_| {
                let mut tempo_desc = BPMDesc::new(self.sample_rate);
                let windows = self
                    .sample_array
                    .windows(BPMDesc::WINDOW_SIZE)
                    .step_by(BPMDesc::HOP_SIZE);

                for window in windows {
                    tempo_desc.do_(&window);
                }
                tempo_desc.get_value()
            });

            let child_loudness = s.spawn(|_| {
                let mut loudness_desc = LoudnessDesc::default();
                let windows = self.sample_array.chunks(LoudnessDesc::WINDOW_SIZE);

                for window in windows {
                    loudness_desc.do_(&window);
                }
                loudness_desc.get_value()
            });

            // Non-streaming approach for that one
            let (is_major, fifth) = child_chroma.join().unwrap();
            let (centroid, rolloff, flatness, zcr) = child_timbral.join().unwrap();
            let tempo = child_tempo.join().unwrap();
            let loudness = child_loudness.join().unwrap();

            Analysis {
                tempo,
                spectral_centroid: centroid,
                zero_crossing_rate: zcr,
                spectral_rolloff: rolloff,
                spectral_flatness: flatness,
                loudness,
                is_major,
                fifth,
            }
        })
        .unwrap()
    }

    // TODO set av_logs to quiet
    pub fn decode(path: &str) -> Result<Song, String> {
        ffmpeg::init().map_err(|e| format!("FFmpeg init error: {:?}.", e))?;

        let mut song = Song::default();
        song.path = path.to_string();
        let mut format = ffmpeg::format::input(&path)
            .map_err(|e| format!("FFmpeg error while opening format: {:?}.", e))?;
        let (mut codec, stream, expected_sample_number) = {
            let stream = format
                .streams()
                .find(|s| s.codec().medium() == ffmpeg::media::Type::Audio)
                .ok_or("No audio stream found.")?;
            stream.codec().set_threading(Config {
                kind: ThreadingType::Frame,
                count: 0,
                safe: true,
            });
            let codec = stream
                .codec()
                .decoder()
                .audio()
                .map_err(|e| format!("FFmpeg error when finding codec: {:?}.", e))?;
            let expected_sample_number = (SAMPLE_RATE as f32 * stream.duration() as f32
                / stream.time_base().denominator() as f32)
                .ceil();
            (codec, stream.index(), expected_sample_number)
        };
        let mut sample_array: Vec<f32> = Vec::with_capacity(expected_sample_number as usize);

        if let Some(title) = format.metadata().get("title") {
            song.title = title.to_string();
        };
        if let Some(artist) = format.metadata().get("artist") {
            song.artist = artist.to_string();
        };
        if let Some(album) = format.metadata().get("album") {
            song.album = album.to_string();
        };
        if let Some(genre) = format.metadata().get("genre") {
            song.genre = genre.to_string();
        };
        if let Some(track_number) = format.metadata().get("track") {
            song.track_number = track_number.to_string();
        };

        // TODO handle WAV without a channel layout set (cf bruiblan.wav)
        let mut resample_context = ffmpeg::software::resampling::context::Context::get(
            codec.format(),
            codec.channel_layout(),
            codec.rate(),
            Sample::F32(Type::Packed),
            ffmpeg::util::channel_layout::ChannelLayout::MONO,
            SAMPLE_RATE,
        )
        .map_err(|e| {
            format!(
                "FFmpeg error trying to allocate resampling context: {:?}",
                e
            )
        })?;

        let (tx, rx) = mpsc::channel();
        let child = std_thread::spawn(move || -> Result<Vec<f32>, String> {
            let mut resampled = ffmpeg::frame::Audio::empty();
            for decoded in rx.iter() {
                resample_context
                    .run(&decoded, &mut resampled)
                    .map_err(|e| format!("FFmpeg error while trying to resample song: {:?}", e))?;
                push_to_sample_array(&resampled, &mut sample_array);
            }

            loop {
                match resample_context
                    .flush(&mut resampled)
                    .map_err(|e| format!("FFmpeg error while trying to resample song: {:?}", e))?
                {
                    Some(_) => {
                        push_to_sample_array(&resampled, &mut sample_array);
                    }
                    None => {
                        push_to_sample_array(&resampled, &mut sample_array);
                        break;
                    }
                };
            }
            Ok(sample_array)
        });
        let mut decoded = ffmpeg::frame::Audio::empty();
        for (s, packet) in format.packets() {
            if s.index() != stream {
                continue;
            }
            codec.send_packet(&packet).unwrap();
            while codec.receive_frame(&mut decoded).is_ok() {
                tx.send(decoded.clone()).map_err(|e| {
                    format!(
                        "Error while sending decoded frame to the resampling thread: {:?}",
                        e
                    )
                })?;
            }
        }

        // Flush the stream
        // TODO check that it's still how to do this
        let packet = ffmpeg::codec::packet::Packet::empty();
        loop {
            codec.send_packet(&packet).unwrap();
            while codec.receive_frame(&mut decoded).is_ok() {
                tx.send(decoded.clone()).map_err(|e| {
                    format!(
                        "Error while sending decoded frame to the resampling thread: {:?}",
                        e
                    )
                })?;
            }
            break;
        }

        drop(tx);
        song.sample_array = child.join().unwrap()?;
        song.sample_rate = SAMPLE_RATE;
        Ok(song)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use ripemd160::{Digest, Ripemd160};
    use std::f32::consts::PI;

    #[test]
    fn test_analyze() {
        let song = Song::decode("data/s16_mono_22_5kHz.flac").unwrap();
        let expected_analysis = Analysis {
            tempo: 0.37860596,
            spectral_centroid: -0.75483,
            zero_crossing_rate: -0.85036564,
            spectral_rolloff: -0.6326486,
            spectral_flatness: -0.77610075,
            loudness: 0.27126348,
            is_major: -1.,
            fifth: (f32::cos(5. * PI / 3.), f32::sin(5. * PI / 3.)),
        };
        assert!(expected_analysis.approx_eq(&song.analyze()));
    }

    fn _test_decode(path: &str, expected_hash: &[u8]) {
        let song = Song::decode(path).unwrap();
        let mut hasher = Ripemd160::new();
        for sample in song.sample_array.iter() {
            hasher.update(sample.to_le_bytes().to_vec());
        }

        assert_eq!(expected_hash, hasher.finalize().as_slice());
    }

    #[test]
    fn tags() {
        let song = Song::decode("data/s16_mono_22_5kHz.flac").unwrap();
        assert_eq!(song.artist, "David TMX");
        assert_eq!(song.title, "Renaissance");
        assert_eq!(song.album, "Renaissance");
        assert_eq!(song.track_number, "02");
        assert_eq!(song.genre, "Pop");
    }

    #[test]
    fn resample_multi() {
        let path = String::from("data/s32_stereo_44_1_kHz.flac");
        let expected_hash = [
            0xc5, 0xf8, 0x23, 0xce, 0x63, 0x2c, 0xf4, 0xa0, 0x72, 0x66, 0xbb, 0x49, 0xad, 0x84,
            0xb6, 0xea, 0x48, 0x48, 0x9c, 0x50,
        ];
        _test_decode(&path, &expected_hash);
    }

    #[test]
    fn resample_stereo() {
        let path = String::from("data/s16_stereo_22_5kHz.flac");
        let expected_hash = [
            0x24, 0xed, 0x45, 0x58, 0x06, 0xbf, 0xfb, 0x05, 0x57, 0x5f, 0xdc, 0x4d, 0xb4, 0x9b,
            0xa5, 0x2b, 0x05, 0x56, 0x10, 0x4f,
        ];
        _test_decode(&path, &expected_hash);
    }

    #[test]
    fn decode_mono() {
        let path = String::from("data/s16_mono_22_5kHz.flac");
        // Obtained through
        // ffmpeg -i data/s16_mono_22_5kHz.flac -ar 22050 -ac 1 -c:a pcm_f32le
        // -f hash -hash ripemd160 -
        let expected_hash = [
            0x9d, 0x95, 0xa5, 0xf2, 0xd2, 0x9c, 0x68, 0xe8, 0x8a, 0x70, 0xcd, 0xf3, 0x54, 0x2c,
            0x5b, 0x45, 0x98, 0xb4, 0xf3, 0xb4,
        ];
        _test_decode(&path, &expected_hash);
    }

    #[test]
    fn test_decode_right_capacity_vec() {
        let path = String::from("data/s16_mono_22_5kHz.flac");
        let song = Song::decode(&path).unwrap();
        assert_eq!(song.sample_array.len(), song.sample_array.capacity());

        let path = String::from("data/s32_stereo_44_1_kHz.flac");
        let song = Song::decode(&path).unwrap();
        assert_eq!(song.sample_array.len(), song.sample_array.capacity());

        // Not 100% sure that the number of samples for ogg is known
        // precisely in advance
        let path = String::from("data/capacity_fix.ogg");
        let song = Song::decode(&path).unwrap();
        assert!(song.sample_array.len() as f32 / song.sample_array.capacity() as f32 > 0.95);
        assert!(song.sample_array.len() as f32 / (song.sample_array.capacity() as f32) < 1.);
    }
}
<commit_msg>Stop copying packets over resampling thread<commit_after>//! Song decoding / analysis module.
//!
//! Use decoding, and features-extraction functions from other modules
//! e.g. tempo features, spectral features, etc to build a Song and its
//! corresponding Analysis.
#[cfg(feature = "aubio-lib")]
extern crate aubio_lib;

extern crate crossbeam;
extern crate ffmpeg_next as ffmpeg;
extern crate ndarray;
extern crate ndarray_npy;

use super::CHANNELS;
use crate::chroma::ChromaDesc;
use crate::misc::LoudnessDesc;
use crate::temporal::BPMDesc;
use crate::timbral::{SpectralDesc, ZeroCrossingRateDesc};
use crate::SAMPLE_RATE;
use crate::{Analysis, Song};
use crossbeam::thread;
use ffmpeg::codec::threading::{Config, Type as ThreadingType};
use ffmpeg::util;
use ffmpeg::util::format::sample::{Sample, Type};
use std::sync::mpsc;
use std::thread as std_thread;

pub fn push_to_sample_array(frame: &ffmpeg::frame::Audio, sample_array: &mut Vec<f32>) {
    if frame.samples() == 0 {
        return;
    }
    // Account for the padding
    let actual_size = util::format::sample::Buffer::size(
        Sample::F32(Type::Packed),
        CHANNELS,
        frame.samples(),
        false,
    );
    let f32_frame: Vec<f32> = frame.data(0)[..actual_size]
        .chunks_exact(4)
        .map(|x| {
            let mut a: [u8; 4] = [0; 4];
            a.copy_from_slice(x);
            f32::from_le_bytes(a)
        })
        .collect();
    sample_array.extend_from_slice(&f32_frame);
}

impl Song {
    pub fn new(path: &str) -> Result<Self, String> {
        // TODO error handling here
        let mut song = Song::decode(&path)?;

        song.analysis = (&song).analyze();
        Ok(song)
    }

    pub fn analyze(&self) -> Analysis {
        thread::scope(|s| {
            let child_chroma = s.spawn(|_| {
                let mut chroma_desc = ChromaDesc::new(self.sample_rate, 12);
                chroma_desc.do_(&self.sample_array);
                chroma_desc.get_values()
            });

            // These descriptors can be streamed
            let child_timbral = s.spawn(|_| {
                let mut spectral_desc = SpectralDesc::new(self.sample_rate);
                let mut zcr_desc = ZeroCrossingRateDesc::default();
                let windows = self
                    .sample_array
                    .windows(SpectralDesc::WINDOW_SIZE)
                    .step_by(SpectralDesc::HOP_SIZE);
                for window in windows {
                    spectral_desc.do_(&window);
                    zcr_desc.do_(&window);
                }
                let centroid = spectral_desc.get_centroid();
                let rolloff = spectral_desc.get_rolloff();
                let flatness = spectral_desc.get_flatness();
                let zcr = zcr_desc.get_value();
                (centroid, rolloff, flatness, zcr)
            });

            let child_tempo = s.spawn(|_| {
                let mut tempo_desc = BPMDesc::new(self.sample_rate);
                let windows = self
                    .sample_array
                    .windows(BPMDesc::WINDOW_SIZE)
                    .step_by(BPMDesc::HOP_SIZE);

                for window in windows {
                    tempo_desc.do_(&window);
                }
                tempo_desc.get_value()
            });

            let child_loudness = s.spawn(|_| {
                let mut loudness_desc = LoudnessDesc::default();
                let windows = self.sample_array.chunks(LoudnessDesc::WINDOW_SIZE);

                for window in windows {
                    loudness_desc.do_(&window);
                }
                loudness_desc.get_value()
            });

            // Non-streaming approach for that one
            let (is_major, fifth) = child_chroma.join().unwrap();
            let (centroid, rolloff, flatness, zcr) = child_timbral.join().unwrap();
            let tempo = child_tempo.join().unwrap();
            let loudness = child_loudness.join().unwrap();

            Analysis {
                tempo,
                spectral_centroid: centroid,
                zero_crossing_rate: zcr,
                spectral_rolloff: rolloff,
                spectral_flatness: flatness,
                loudness,
                is_major,
                fifth,
            }
        })
        .unwrap()
    }

    // TODO set av_logs to quiet
    pub fn decode(path: &str) -> Result<Song, String> {
        ffmpeg::init().map_err(|e| format!("FFmpeg init error: {:?}.", e))?;

        let mut song = Song::default();
        song.path = path.to_string();
        let mut format = ffmpeg::format::input(&path)
            .map_err(|e| format!("FFmpeg error while opening format: {:?}.", e))?;
        let (mut codec, stream, expected_sample_number) = {
            let stream = format
                .streams()
                .find(|s| s.codec().medium() == ffmpeg::media::Type::Audio)
                .ok_or("No audio stream found.")?;
            stream.codec().set_threading(Config {
                kind: ThreadingType::Frame,
                count: 0,
                safe: true,
            });
            let codec = stream
                .codec()
                .decoder()
                .audio()
                .map_err(|e| format!("FFmpeg error when finding codec: {:?}.", e))?;
            let expected_sample_number = (SAMPLE_RATE as f32 * stream.duration() as f32
                / stream.time_base().denominator() as f32)
                .ceil();
            (codec, stream.index(), expected_sample_number)
        };
        let mut sample_array: Vec<f32> = Vec::with_capacity(expected_sample_number as usize);

        if let Some(title) = format.metadata().get("title") {
            song.title = title.to_string();
        };
        if let Some(artist) = format.metadata().get("artist") {
            song.artist = artist.to_string();
        };
        if let Some(album) = format.metadata().get("album") {
            song.album = album.to_string();
        };
        if let Some(genre) = format.metadata().get("genre") {
            song.genre = genre.to_string();
        };
        if let Some(track_number) = format.metadata().get("track") {
            song.track_number = track_number.to_string();
        };

        // TODO handle WAV without a channel layout set (cf bruiblan.wav)
        let mut resample_context = ffmpeg::software::resampling::context::Context::get(
            codec.format(),
            codec.channel_layout(),
            codec.rate(),
            Sample::F32(Type::Packed),
            ffmpeg::util::channel_layout::ChannelLayout::MONO,
            SAMPLE_RATE,
        )
        .map_err(|e| {
            format!(
                "FFmpeg error trying to allocate resampling context: {:?}",
                e
            )
        })?;

        let (tx, rx) = mpsc::channel();
        let child = std_thread::spawn(move || -> Result<Vec<f32>, String> {
            let mut resampled = ffmpeg::frame::Audio::empty();
            for decoded in rx.iter() {
                resample_context
                    .run(&decoded, &mut resampled)
                    .map_err(|e| format!("FFmpeg error while trying to resample song: {:?}", e))?;
                push_to_sample_array(&resampled, &mut sample_array);
            }

            loop {
                match resample_context
                    .flush(&mut resampled)
                    .map_err(|e| format!("FFmpeg error while trying to resample song: {:?}", e))?
                {
                    Some(_) => {
                        push_to_sample_array(&resampled, &mut sample_array);
                    }
                    None => {
                        push_to_sample_array(&resampled, &mut sample_array);
                        break;
                    }
                };
            }
            Ok(sample_array)
        });
        for (s, packet) in format.packets() {
            if s.index() != stream {
                continue;
            }
            codec.send_packet(&packet).unwrap();
            loop {
                let mut decoded = ffmpeg::frame::Audio::empty();
                match codec.receive_frame(&mut decoded) {
                    Ok(_) => {
                        tx.send(decoded).map_err(|e| {
                            format!(
                                "Error while sending decoded frame to the resampling thread: {:?}",
                                e
                            )
                        })?;
                    },
                    Err(_) => break,
                }
            }
        }

        // Flush the stream
        // TODO check that it's still how to do this
        let packet = ffmpeg::codec::packet::Packet::empty();
        loop {
            codec.send_packet(&packet).unwrap();
            loop {
                let mut decoded = ffmpeg::frame::Audio::empty();
                match codec.receive_frame(&mut decoded) {
                    Ok(_) => {
                        tx.send(decoded).map_err(|e| {
                            format!(
                                "Error while sending decoded frame to the resampling thread: {:?}",
                                e
                            )
                        })?;
                    },
                    Err(_) => break,
                }
            }
            break;
        }

        drop(tx);
        song.sample_array = child.join().unwrap()?;
        song.sample_rate = SAMPLE_RATE;
        Ok(song)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use ripemd160::{Digest, Ripemd160};
    use std::f32::consts::PI;

    #[test]
    fn test_analyze() {
        let song = Song::decode("data/s16_mono_22_5kHz.flac").unwrap();
        let expected_analysis = Analysis {
            tempo: 0.37860596,
            spectral_centroid: -0.75483,
            zero_crossing_rate: -0.85036564,
            spectral_rolloff: -0.6326486,
            spectral_flatness: -0.77610075,
            loudness: 0.27126348,
            is_major: -1.,
            fifth: (f32::cos(5. * PI / 3.), f32::sin(5. * PI / 3.)),
        };
        assert!(expected_analysis.approx_eq(&song.analyze()));
    }

    fn _test_decode(path: &str, expected_hash: &[u8]) {
        let song = Song::decode(path).unwrap();
        let mut hasher = Ripemd160::new();
        for sample in song.sample_array.iter() {
            hasher.update(sample.to_le_bytes().to_vec());
        }

        assert_eq!(expected_hash, hasher.finalize().as_slice());
    }

    #[test]
    fn tags() {
        let song = Song::decode("data/s16_mono_22_5kHz.flac").unwrap();
        assert_eq!(song.artist, "David TMX");
        assert_eq!(song.title, "Renaissance");
        assert_eq!(song.album, "Renaissance");
        assert_eq!(song.track_number, "02");
        assert_eq!(song.genre, "Pop");
    }

    #[test]
    fn resample_multi() {
        let path = String::from("data/s32_stereo_44_1_kHz.flac");
        let expected_hash = [
            0xc5, 0xf8, 0x23, 0xce, 0x63, 0x2c, 0xf4, 0xa0, 0x72, 0x66, 0xbb, 0x49, 0xad, 0x84,
            0xb6, 0xea, 0x48, 0x48, 0x9c, 0x50,
        ];
        _test_decode(&path, &expected_hash);
    }

    #[test]
    fn resample_stereo() {
        let path = String::from("data/s16_stereo_22_5kHz.flac");
        let expected_hash = [
            0x24, 0xed, 0x45, 0x58, 0x06, 0xbf, 0xfb, 0x05, 0x57, 0x5f, 0xdc, 0x4d, 0xb4, 0x9b,
            0xa5, 0x2b, 0x05, 0x56, 0x10, 0x4f,
        ];
        _test_decode(&path, &expected_hash);
    }

    #[test]
    fn decode_mono() {
        let path = String::from("data/s16_mono_22_5kHz.flac");
        // Obtained through
        // ffmpeg -i data/s16_mono_22_5kHz.flac -ar 22050 -ac 1 -c:a pcm_f32le
        // -f hash -hash ripemd160 -
        let expected_hash = [
            0x9d, 0x95, 0xa5, 0xf2, 0xd2, 0x9c, 0x68, 0xe8, 0x8a, 0x70, 0xcd, 0xf3, 0x54, 0x2c,
            0x5b, 0x45, 0x98, 0xb4, 0xf3, 0xb4,
        ];
        _test_decode(&path, &expected_hash);
    }

    #[test]
    fn test_decode_right_capacity_vec() {
        let path = String::from("data/s16_mono_22_5kHz.flac");
        let song = Song::decode(&path).unwrap();
        assert_eq!(song.sample_array.len(), song.sample_array.capacity());

        let path = String::from("data/s32_stereo_44_1_kHz.flac");
        let song = Song::decode(&path).unwrap();
        assert_eq!(song.sample_array.len(), song.sample_array.capacity());

        // Not 100% sure that the number of samples for ogg is known
        // precisely in advance
        let path = String::from("data/capacity_fix.ogg");
        let song = Song::decode(&path).unwrap();
        assert!(song.sample_array.len() as f32 / song.sample_array.capacity() as f32 > 0.95);
        assert!(song.sample_array.len() as f32 / (song.sample_array.capacity() as f32) < 1.);
    }
}
<|endoftext|>