From 20bd7f000fa788b9d38c5d664bb19b1ba38af850 Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Mon, 21 Nov 2016 23:26:31 +0100 Subject: [PATCH 1/3] utf8 validation: Compute block end upfront Simplify the conditional used for ensuring that the whole word loop is only used if there are at least two whole words left to read. This makes the function slightly smaller and simpler, a 0-5% reduction in runtime for various test cases. --- src/libcore/str/mod.rs | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/src/libcore/str/mod.rs b/src/libcore/str/mod.rs index 7f91da5314247..48c0f0b27087f 100644 --- a/src/libcore/str/mod.rs +++ b/src/libcore/str/mod.rs @@ -1217,6 +1217,11 @@ fn contains_nonascii(x: usize) -> bool { fn run_utf8_validation(v: &[u8]) -> Result<(), Utf8Error> { let mut offset = 0; let len = v.len(); + + let usize_bytes = mem::size_of::(); + let ascii_block_size = 2 * usize_bytes; + let blocks_end = if len >= ascii_block_size { len - ascii_block_size + 1 } else { 0 }; + while offset < len { let old_offset = offset; macro_rules! err { () => {{ @@ -1282,26 +1287,22 @@ fn run_utf8_validation(v: &[u8]) -> Result<(), Utf8Error> { // Ascii case, try to skip forward quickly. // When the pointer is aligned, read 2 words of data per iteration // until we find a word containing a non-ascii byte. - let usize_bytes = mem::size_of::(); - let bytes_per_iteration = 2 * usize_bytes; let ptr = v.as_ptr(); let align = (ptr as usize + offset) & (usize_bytes - 1); if align == 0 { - if len >= bytes_per_iteration { - while offset <= len - bytes_per_iteration { - unsafe { - let u = *(ptr.offset(offset as isize) as *const usize); - let v = *(ptr.offset((offset + usize_bytes) as isize) as *const usize); - - // break if there is a nonascii byte - let zu = contains_nonascii(u); - let zv = contains_nonascii(v); - if zu || zv { - break; - } + while offset < blocks_end { + unsafe { + let u = *(ptr.offset(offset as isize) as *const usize); + let v = *(ptr.offset((offset + usize_bytes) as isize) as *const usize); + + // break if there is a nonascii byte + let zu = contains_nonascii(u); + let zv = contains_nonascii(v); + if zu || zv { + break; } - offset += bytes_per_iteration; } + offset += ascii_block_size; } // step from the point where the wordwise loop stopped while offset < len && v[offset] < 128 { From 4a8b04eda0505948bae2f1be367b10bd8ba082e5 Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Tue, 22 Nov 2016 13:47:45 +0100 Subject: [PATCH 2/3] utf8 validation: Cleanup code in the ascii fast path --- src/libcore/str/mod.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/libcore/str/mod.rs b/src/libcore/str/mod.rs index 48c0f0b27087f..7cb70bce7b864 100644 --- a/src/libcore/str/mod.rs +++ b/src/libcore/str/mod.rs @@ -1292,13 +1292,11 @@ fn run_utf8_validation(v: &[u8]) -> Result<(), Utf8Error> { if align == 0 { while offset < blocks_end { unsafe { - let u = *(ptr.offset(offset as isize) as *const usize); - let v = *(ptr.offset((offset + usize_bytes) as isize) as *const usize); - + let block = ptr.offset(offset as isize) as *const usize; // break if there is a nonascii byte - let zu = contains_nonascii(u); - let zv = contains_nonascii(v); - if zu || zv { + let zu = contains_nonascii(*block); + let zv = contains_nonascii(*block.offset(1)); + if zu | zv { break; } } From 0dffc1e193f11392ba49b033a0d1c21ab4863b3d Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Tue, 22 Nov 2016 13:47:45 +0100 Subject: [PATCH 3/3] utf8 validation: Cleanup code by renaming index variable --- src/libcore/str/mod.rs | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/libcore/str/mod.rs b/src/libcore/str/mod.rs index 7cb70bce7b864..c334ac4887279 100644 --- a/src/libcore/str/mod.rs +++ b/src/libcore/str/mod.rs @@ -1215,15 +1215,15 @@ fn contains_nonascii(x: usize) -> bool { /// invalid sequence. #[inline(always)] fn run_utf8_validation(v: &[u8]) -> Result<(), Utf8Error> { - let mut offset = 0; + let mut index = 0; let len = v.len(); let usize_bytes = mem::size_of::(); let ascii_block_size = 2 * usize_bytes; let blocks_end = if len >= ascii_block_size { len - ascii_block_size + 1 } else { 0 }; - while offset < len { - let old_offset = offset; + while index < len { + let old_offset = index; macro_rules! err { () => {{ return Err(Utf8Error { valid_up_to: old_offset @@ -1231,15 +1231,15 @@ fn run_utf8_validation(v: &[u8]) -> Result<(), Utf8Error> { }}} macro_rules! next { () => {{ - offset += 1; + index += 1; // we needed data, but there was none: error! - if offset >= len { + if index >= len { err!() } - v[offset] + v[index] }}} - let first = v[offset]; + let first = v[index]; if first >= 128 { let w = UTF8_CHAR_WIDTH[first as usize]; let second = next!(); @@ -1282,17 +1282,17 @@ fn run_utf8_validation(v: &[u8]) -> Result<(), Utf8Error> { } _ => err!() } - offset += 1; + index += 1; } else { // Ascii case, try to skip forward quickly. // When the pointer is aligned, read 2 words of data per iteration // until we find a word containing a non-ascii byte. let ptr = v.as_ptr(); - let align = (ptr as usize + offset) & (usize_bytes - 1); + let align = (ptr as usize + index) & (usize_bytes - 1); if align == 0 { - while offset < blocks_end { + while index < blocks_end { unsafe { - let block = ptr.offset(offset as isize) as *const usize; + let block = ptr.offset(index as isize) as *const usize; // break if there is a nonascii byte let zu = contains_nonascii(*block); let zv = contains_nonascii(*block.offset(1)); @@ -1300,14 +1300,14 @@ fn run_utf8_validation(v: &[u8]) -> Result<(), Utf8Error> { break; } } - offset += ascii_block_size; + index += ascii_block_size; } // step from the point where the wordwise loop stopped - while offset < len && v[offset] < 128 { - offset += 1; + while index < len && v[index] < 128 { + index += 1; } } else { - offset += 1; + index += 1; } } }