diff --git a/.gitignore b/.gitignore index a9d37c5..5cdcdba 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ target Cargo.lock +scripts/tmp diff --git a/.travis.yml b/.travis.yml index b980a62..bc1a9b2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,6 +6,7 @@ script: - cargo clean - cargo build --verbose --features default - cargo test --verbose --features default + - cargo bench --verbose --features default - rustdoc --test README.md -L target/debug -L target/debug/deps - cargo doc after_success: | diff --git a/Cargo.toml b/Cargo.toml index 2b63170..67b93bf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "unicode-width" -version = "0.1.0" +version = "0.1.1" authors = ["kwantam "] homepage = "https://github.com/unicode-rs/unicode-width" diff --git a/README.md b/README.md index 53cae1e..cf6b918 100644 --- a/README.md +++ b/README.md @@ -33,5 +33,5 @@ to your `Cargo.toml`: ```toml [dependencies] -unicode-width = "0.1.0" +unicode-width = "0.1.1" ``` diff --git a/scripts/unicode.py b/scripts/unicode.py index aa59006..36788bd 100755 --- a/scripts/unicode.py +++ b/scripts/unicode.py @@ -206,6 +206,7 @@ def emit_charwidth_module(f, width_table): #[cfg(feature = "no_std")] use core::result::Result::{Ok, Err}; + #[inline] fn bsearch_range_value_table(c: char, is_cjk: bool, r: &'static [(char, char, u8, u8)]) -> u8 { #[cfg(feature = "no_std")] use core::cmp::Ordering::{Equal, Less, Greater}; @@ -226,6 +227,7 @@ def emit_charwidth_module(f, width_table): """) f.write(""" + #[inline] pub fn width(c: char, is_cjk: bool) -> Option { match c as usize { _c @ 0 => Some(0), // null is zero width diff --git a/src/lib.rs b/src/lib.rs index 6a81a6a..54ce0d1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -39,7 +39,7 @@ //! //! ```toml //! [dependencies] -//! unicode-width = "0.1.0" +//! unicode-width = "0.1.1" //! ``` #![deny(missing_docs, unsafe_code)] @@ -49,6 +49,8 @@ #![cfg_attr(feature = "no_std", no_std)] #![cfg_attr(feature = "no_std", feature(no_std, core))] +#![cfg_attr(test, feature(test, unicode))] + #[cfg(feature = "no_std")] #[macro_use] extern crate core; @@ -57,6 +59,9 @@ extern crate core; #[macro_use] extern crate std; +#[cfg(test)] +extern crate test; + #[cfg(feature = "no_std")] use core::prelude::*; @@ -70,6 +75,9 @@ use std::ops::Add; mod tables; +#[cfg(test)] +mod tests; + /// Methods for determining displayed width of Unicode characters. pub trait UnicodeWidthChar { /// Returns the character's displayed width in columns, or `None` if the @@ -92,8 +100,10 @@ pub trait UnicodeWidthChar { } impl UnicodeWidthChar for char { + #[inline] fn width(self) -> Option { cw::width(self, false) } + #[inline] fn width_cjk(self) -> Option { cw::width(self, true) } } @@ -121,75 +131,13 @@ pub trait UnicodeWidthStr { } impl UnicodeWidthStr for str { + #[inline] fn width(&self) -> usize { self.chars().map(|c| cw::width(c, false).unwrap_or(0)).fold(0, Add::add) } + #[inline] fn width_cjk(&self) -> usize { self.chars().map(|c| cw::width(c, true).unwrap_or(0)).fold(0, Add::add) } } - -#[cfg(test)] -mod tests { - #[test] - fn test_str() { - use super::UnicodeWidthStr; - - assert_eq!(UnicodeWidthStr::width("hello"), 10); - assert_eq!("hello".width_cjk(), 10); - assert_eq!(UnicodeWidthStr::width("\0\0\0\x01\x01"), 0); - assert_eq!("\0\0\0\x01\x01".width_cjk(), 0); - assert_eq!(UnicodeWidthStr::width(""), 0); - assert_eq!("".width_cjk(), 0); - assert_eq!(UnicodeWidthStr::width("\u{2081}\u{2082}\u{2083}\u{2084}"), 4); - assert_eq!("\u{2081}\u{2082}\u{2083}\u{2084}".width_cjk(), 8); - } - - #[test] - fn test_char() { - use super::UnicodeWidthChar; - #[cfg(feature = "no_std")] - use core::option::Option::{Some, None}; - - assert_eq!(UnicodeWidthChar::width('h'), Some(2)); - assert_eq!('h'.width_cjk(), Some(2)); - assert_eq!(UnicodeWidthChar::width('\x00'), Some(0)); - assert_eq!('\x00'.width_cjk(), Some(0)); - assert_eq!(UnicodeWidthChar::width('\x01'), None); - assert_eq!('\x01'.width_cjk(), None); - assert_eq!(UnicodeWidthChar::width('\u{2081}'), Some(1)); - assert_eq!('\u{2081}'.width_cjk(), Some(2)); - } - - #[test] - fn test_char2() { - use super::UnicodeWidthChar; - #[cfg(feature = "no_std")] - use core::option::Option::{Some, None}; - - assert_eq!(UnicodeWidthChar::width('\x00'),Some(0)); - assert_eq!('\x00'.width_cjk(),Some(0)); - - assert_eq!(UnicodeWidthChar::width('\x0A'),None); - assert_eq!('\x0A'.width_cjk(),None); - - assert_eq!(UnicodeWidthChar::width('w'),Some(1)); - assert_eq!('w'.width_cjk(),Some(1)); - - assert_eq!(UnicodeWidthChar::width('h'),Some(2)); - assert_eq!('h'.width_cjk(),Some(2)); - - assert_eq!(UnicodeWidthChar::width('\u{AD}'),Some(1)); - assert_eq!('\u{AD}'.width_cjk(),Some(1)); - - assert_eq!(UnicodeWidthChar::width('\u{1160}'),Some(0)); - assert_eq!('\u{1160}'.width_cjk(),Some(0)); - - assert_eq!(UnicodeWidthChar::width('\u{a1}'),Some(1)); - assert_eq!('\u{a1}'.width_cjk(),Some(2)); - - assert_eq!(UnicodeWidthChar::width('\u{300}'),Some(0)); - assert_eq!('\u{300}'.width_cjk(),Some(0)); - } -} diff --git a/src/tables.rs b/src/tables.rs index d87f31d..2047f39 100644 --- a/src/tables.rs +++ b/src/tables.rs @@ -24,6 +24,7 @@ pub mod charwidth { #[cfg(feature = "no_std")] use core::result::Result::{Ok, Err}; + #[inline] fn bsearch_range_value_table(c: char, is_cjk: bool, r: &'static [(char, char, u8, u8)]) -> u8 { #[cfg(feature = "no_std")] use core::cmp::Ordering::{Equal, Less, Greater}; @@ -42,6 +43,7 @@ pub mod charwidth { } } + #[inline] pub fn width(c: char, is_cjk: bool) -> Option { match c as usize { _c @ 0 => Some(0), // null is zero width diff --git a/src/tests.rs b/src/tests.rs new file mode 100644 index 0000000..7731878 --- /dev/null +++ b/src/tests.rs @@ -0,0 +1,148 @@ +// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::iter; +use test::{self, Bencher}; + +use super::UnicodeWidthChar; + +#[cfg(feature = "no_std")] +use std::prelude::v1::*; + +#[bench] +fn cargo(b: &mut Bencher) { + let string = iter::repeat('a').take(4096).collect::(); + + b.iter(|| { + for c in string.chars() { + test::black_box(UnicodeWidthChar::width(c)); + } + }); +} + +#[bench] +fn stdlib(b: &mut Bencher) { + let string = iter::repeat('a').take(4096).collect::(); + + b.iter(|| { + for c in string.chars() { + test::black_box(c.width(false)); + } + }); +} + +#[bench] +fn simple_if(b: &mut Bencher) { + let string = iter::repeat('a').take(4096).collect::(); + + b.iter(|| { + for c in string.chars() { + test::black_box(simple_width_if(c)); + } + }); +} + +#[bench] +fn simple_match(b: &mut Bencher) { + let string = iter::repeat('a').take(4096).collect::(); + + b.iter(|| { + for c in string.chars() { + test::black_box(simple_width_match(c)); + } + }); +} + +#[inline] +fn simple_width_if(c: char) -> Option { + let cu = c as u32; + if cu < 127 { + if cu > 31 { + Some(1) + } else if cu == 0 { + Some(0) + } else { + None + } + } else { + UnicodeWidthChar::width(c) + } +} + +#[inline] +fn simple_width_match(c: char) -> Option { + match c as u32 { + cu if cu == 0 => Some(0), + cu if cu < 0x20 => None, + cu if cu < 0x7f => Some(1), + _ => UnicodeWidthChar::width(c) + } +} + +#[test] +fn test_str() { + use super::UnicodeWidthStr; + + assert_eq!(UnicodeWidthStr::width("hello"), 10); + assert_eq!("hello".width_cjk(), 10); + assert_eq!(UnicodeWidthStr::width("\0\0\0\x01\x01"), 0); + assert_eq!("\0\0\0\x01\x01".width_cjk(), 0); + assert_eq!(UnicodeWidthStr::width(""), 0); + assert_eq!("".width_cjk(), 0); + assert_eq!(UnicodeWidthStr::width("\u{2081}\u{2082}\u{2083}\u{2084}"), 4); + assert_eq!("\u{2081}\u{2082}\u{2083}\u{2084}".width_cjk(), 8); +} + +#[test] +fn test_char() { + use super::UnicodeWidthChar; + #[cfg(feature = "no_std")] + use core::option::Option::{Some, None}; + + assert_eq!(UnicodeWidthChar::width('h'), Some(2)); + assert_eq!('h'.width_cjk(), Some(2)); + assert_eq!(UnicodeWidthChar::width('\x00'), Some(0)); + assert_eq!('\x00'.width_cjk(), Some(0)); + assert_eq!(UnicodeWidthChar::width('\x01'), None); + assert_eq!('\x01'.width_cjk(), None); + assert_eq!(UnicodeWidthChar::width('\u{2081}'), Some(1)); + assert_eq!('\u{2081}'.width_cjk(), Some(2)); +} + +#[test] +fn test_char2() { + use super::UnicodeWidthChar; + #[cfg(feature = "no_std")] + use core::option::Option::{Some, None}; + + assert_eq!(UnicodeWidthChar::width('\x00'),Some(0)); + assert_eq!('\x00'.width_cjk(),Some(0)); + + assert_eq!(UnicodeWidthChar::width('\x0A'),None); + assert_eq!('\x0A'.width_cjk(),None); + + assert_eq!(UnicodeWidthChar::width('w'),Some(1)); + assert_eq!('w'.width_cjk(),Some(1)); + + assert_eq!(UnicodeWidthChar::width('h'),Some(2)); + assert_eq!('h'.width_cjk(),Some(2)); + + assert_eq!(UnicodeWidthChar::width('\u{AD}'),Some(1)); + assert_eq!('\u{AD}'.width_cjk(),Some(1)); + + assert_eq!(UnicodeWidthChar::width('\u{1160}'),Some(0)); + assert_eq!('\u{1160}'.width_cjk(),Some(0)); + + assert_eq!(UnicodeWidthChar::width('\u{a1}'),Some(1)); + assert_eq!('\u{a1}'.width_cjk(),Some(2)); + + assert_eq!(UnicodeWidthChar::width('\u{300}'),Some(0)); + assert_eq!('\u{300}'.width_cjk(),Some(0)); +}