From 7ebda2de44a2355e058c3a11e124bc776387936b Mon Sep 17 00:00:00 2001 From: Bulat Musin Date: Wed, 3 Jan 2018 18:11:42 +0300 Subject: [PATCH 1/3] echo: rewrite two functions as closures --- src/echo/echo.rs | 23 +++-------------------- 1 file changed, 3 insertions(+), 20 deletions(-) diff --git a/src/echo/echo.rs b/src/echo/echo.rs index 15fd51335..9e61a33a0 100644 --- a/src/echo/echo.rs +++ b/src/echo/echo.rs @@ -47,27 +47,10 @@ fn to_char(bytes: &[u8], base: u32) -> char { usize::from_str_radix(from_utf8(bytes.as_ref()).unwrap(), base).unwrap() as u8 as char } -#[inline(always)] -fn isxdigit(c: u8) -> bool { - match c as char { - '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | - '8' | '9' | 'A' | 'B' | 'C' | 'D' | 'E' | 'F' => true, - _ => false - } -} - -#[inline(always)] -fn isodigit(c: u8) -> bool { - match c as char { - '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' => true, - _ => false - } -} - fn convert_str(string: &[u8], index: usize, base: u32) -> (char, usize) { let (max_digits, is_legal_digit) : (usize, fn(u8) -> bool) = match base { - 8 => (3, isodigit), - 16 => (2, isxdigit), + 8 => (3, |c| { (c as char).is_digit(8) }), + 16 => (2, |c| { (c as char).is_digit(16) }), _ => panic!(), }; @@ -175,7 +158,7 @@ pub fn uumain(args: Vec) -> i32 { if options.newline { return_if_err!(1, stdout().flush()) } else { - println!("") + println!() } 0 From 26ad2405723e0d985a0b36d53394ca7d2854bb02 Mon Sep 17 00:00:00 2001 From: Bulat Musin Date: Wed, 3 Jan 2018 18:14:50 +0300 Subject: [PATCH 2/3] echo: reorder of match patterns People write \n \t and \r \v much more often than other escape sequences, so it makes more sense to optimise for common case, as match scans from top to bottom. --- src/echo/echo.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/echo/echo.rs b/src/echo/echo.rs index 9e61a33a0..af195d1a0 100644 --- a/src/echo/echo.rs +++ b/src/echo/echo.rs @@ -105,15 +105,15 @@ pub fn uumain(args: Vec) -> i32 { prev_was_slash = false; match c { '\\' => print!("\\"), + 'n' => print!("\n"), + 'r' => print!("\r"), + 't' => print!("\t"), + 'v' => print!("\x0B"), 'a' => print!("\x07"), 'b' => print!("\x08"), 'c' => break, 'e' => print!("\x1B"), 'f' => print!("\x0C"), - 'n' => print!("\n"), - 'r' => print!("\r"), - 't' => print!("\t"), - 'v' => print!("\x0B"), 'x' => { let (c, num_char_used) = convert_str(string.as_bytes(), index + 1, 16); if num_char_used == 0 { From ec543508bc5c252a590fc89da08aa29f3f5c8ff2 Mon Sep 17 00:00:00 2001 From: Bulat Musin Date: Wed, 3 Jan 2018 18:20:38 +0300 Subject: [PATCH 3/3] echo: refactor slightly --- src/echo/echo.rs | 108 +++++++++++++++++++++-------------------------- 1 file changed, 49 insertions(+), 59 deletions(-) diff --git a/src/echo/echo.rs b/src/echo/echo.rs index af195d1a0..cc39f37ac 100644 --- a/src/echo/echo.rs +++ b/src/echo/echo.rs @@ -16,9 +16,9 @@ use std::io::{Write, stdout}; use std::str::from_utf8; #[allow(dead_code)] -static SYNTAX: &'static str = "[OPTIONS]... [STRING]..."; -static SUMMARY: &'static str = "display a line of text"; -static LONG_HELP: &'static str = r#" +static SYNTAX: &str = "[OPTIONS]... [STRING]..."; +static SUMMARY: &str = "display a line of text"; +static HELP: &str = r#" Echo the STRING(s) to standard output. If -e is in effect, the following sequences are recognized: @@ -36,26 +36,24 @@ static LONG_HELP: &'static str = r#" \\xHH byte with hexadecimal value HH (1 to 2 digits) "#; -#[derive(Clone)] -struct EchoOptions { +enum Base { + B8 = 8, + B16 = 16, +} + +struct Opts { newline: bool, escape: bool } -#[inline(always)] -fn to_char(bytes: &[u8], base: u32) -> char { - usize::from_str_radix(from_utf8(bytes.as_ref()).unwrap(), base).unwrap() as u8 as char -} - -fn convert_str(string: &[u8], index: usize, base: u32) -> (char, usize) { - let (max_digits, is_legal_digit) : (usize, fn(u8) -> bool) = match base { - 8 => (3, |c| { (c as char).is_digit(8) }), - 16 => (2, |c| { (c as char).is_digit(16) }), - _ => panic!(), +fn convert_str(string: &[u8], index: usize, base: Base) -> (char, usize) { + let (max_digits, is_legal_digit): (usize, fn(u8) -> bool) = match base { + Base::B8 => (3, |c| { (c as char).is_digit(8) }), + Base::B16 => (2, |c| { (c as char).is_digit(16) }), }; - let mut bytes = vec!(); - for offset in 0usize .. max_digits { + let mut bytes = vec![]; + for offset in 0..max_digits { if string.len() <= index + offset as usize { break; } @@ -70,39 +68,39 @@ fn convert_str(string: &[u8], index: usize, base: u32) -> (char, usize) { if bytes.is_empty() { (' ', 0) } else { - (to_char(&bytes, base), bytes.len()) + (usize::from_str_radix( + from_utf8(bytes.as_ref()).unwrap(), + base as u32 + ).unwrap() as u8 as char, bytes.len()) } } pub fn uumain(args: Vec) -> i32 { - let mut options = EchoOptions { - newline: false, - escape: false - }; - - let matches = new_coreopts!(SYNTAX, SUMMARY, LONG_HELP) + let matches = new_coreopts!(SYNTAX, SUMMARY, HELP) .optflag("n", "", "do not output the trailing newline") .optflag("e", "", "enable interpretation of backslash escapes") .optflag("E", "", "disable interpretation of backslash escapes (default)") .parse(args); - options.newline = matches.opt_present("n"); - options.escape = matches.opt_present("e"); + let options = Opts { + newline: matches.opt_present("n"), + escape: matches.opt_present("e"), + }; let free = matches.free; if !free.is_empty() { let string = free.join(" "); if options.escape { let mut prev_was_slash = false; let mut iter = string.chars().enumerate(); - while let Some((index, c)) = iter.next() { - if !prev_was_slash { + while let Some((mut idx, c)) = iter.next() { + prev_was_slash = if !prev_was_slash { if c != '\\' { print!("{}", c); + false } else { - prev_was_slash = true; + true } } else { - prev_was_slash = false; match c { '\\' => print!("\\"), 'n' => print!("\n"), @@ -114,40 +112,32 @@ pub fn uumain(args: Vec) -> i32 { 'c' => break, 'e' => print!("\x1B"), 'f' => print!("\x0C"), - 'x' => { - let (c, num_char_used) = convert_str(string.as_bytes(), index + 1, 16); - if num_char_used == 0 { - print!("\\x"); + ch => { // 'x' or '0' or _ + idx = if ch == 'x' || ch == '0' { + idx + 1 } else { - print!("{}", c); - for _ in 0 .. num_char_used { - iter.next(); // consume used characters - } - } - }, - '0' => { - let (c, num_char_used) = convert_str(string.as_bytes(), index + 1, 8); - if num_char_used == 0 { - print!("\0"); - } else { - print!("{}", c); - for _ in 0 .. num_char_used { - iter.next(); // consume used characters - } - } - } - _ => { - let (esc_c, num_char_used) = convert_str(string.as_bytes(), index, 8); - if num_char_used == 0 { - print!("\\{}", c); - } else { - print!("{}", esc_c); - for _ in 1 .. num_char_used { - iter.next(); // consume used characters + idx + }; + let base = if ch == 'x' { Base::B16 } else { Base::B8 }; + match convert_str(string.as_bytes(), idx, base) { + (_, 0) => { + match ch { + 'x' => print!("\\x"), + '0' => print!("\0"), + _ => print!("\\{}", c), + } + }, + (c, num_char_used) => { + print!("{}", c); + let beg = if ch == 'x' || ch == '0' { 0 } else { 1 }; + for _ in beg..num_char_used { + iter.next(); // consume used characters + } } } } } + false } } } else {