Thanks for your comment!
I confirmed you are correct about the char decoding skipped entirely.
So I withdraw this pre-proposal.
Thank you all for your comments!
And I learned how to use cargo-asm
to view the assembler output with rust code interleaved. Nice!
Here is what I confirmed.
I wrote the following two functions.
pub fn calc_with_char_indices_print(s: &str) -> usize {
s.char_indices().fold(0, |acc, (len, x)| {
println!("x={}", x);
acc + len
})
}
pub fn calc_with_char_indices(s: &str) -> usize {
s.char_indices().fold(0, |acc, (len, _)| acc + len)
}
Then I followed https://stackoverflow.com/a/54287770/1391518 to see assembler output.
cargo install cargo-asm
cargo build --release
cargo asm --no-color --rust len_utf8_at_benchmark::calc_with_char_indices_print > calc_with_char_indices_print.asm
cargo asm --no-color --rust len_utf8_at_benchmark::calc_with_char_indices > calc_with_char_indices.asm
In the standard library,
/// Reads the next code point out of a byte iterator (assuming a
/// UTF-8-like encoding).
#[unstable(feature = "str_internals", issue = "none")]
#[inline]
pub fn next_code_point<'a, I: Iterator<Item = &'a u8>>(bytes: &mut I) -> Option<u32> {
// Decode UTF-8
let x = *bytes.next()?;
if x < 128 {
return Some(x as u32);
}
// Multibyte case follows
// Decode from a byte combination out of: [[[x y] z] w]
// NOTE: Performance is sensitive to the exact formulation here
let init = utf8_first_byte(x, 2);
let y = unwrap_or_0(bytes.next());
let mut ch = utf8_acc_cont_byte(init, y);
if x >= 0xE0 {
// [[x y z] w] case
// 5th bit in 0xE0 .. 0xEF is always clear, so `init` is still valid
let z = unwrap_or_0(bytes.next());
let y_z = utf8_acc_cont_byte((y & CONT_MASK) as u32, z);
ch = init << 12 | y_z;
if x >= 0xF0 {
// [x y z w] case
// use only the lower 3 bits of `init`
let w = unwrap_or_0(bytes.next());
ch = (init & 7) << 18 | utf8_acc_cont_byte(y_z, w);
}
}
Some(ch)
}
/// Returns the value of `ch` updated with continuation byte `byte`.
#[inline]
fn utf8_acc_cont_byte(ch: u32, byte: u8) -> u32 {
(ch << 6) | (byte & CONT_MASK) as u32
}
The part after if x >= 0xF0
in calc_with_char_indices_print()
has assembler for ch = (init & 7) << 18 | utf8_acc_cont_byte(y_z, w);
if x >= 0xF0 { (/home/hnakamur/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/str/mod.rs:539)
cmp dl, -16
if x >= 0xF0 { (/home/hnakamur/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/str/mod.rs:539)
jae .LBB1_15
.LBB1_14:
shl esi, 12
.LBB1_9:
or ecx, esi
mov edx, ecx
jmp .LBB1_20
.LBB1_16:
xor edx, edx
.LBB1_18:
ch = (init & 7) << 18 | utf8_acc_cont_byte(y_z, w); (/home/hnakamur/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/str/mod.rs:543)
and esi, 7
shl esi, 18
(ch << 6) | (byte & CONT_MASK) as u32 (/home/hnakamur/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/str/mod.rs:498)
shl ecx, 6
or ecx, esi
ch = (init & 7) << 18 | utf8_acc_cont_byte(y_z, w); (/home/hnakamur/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/str/mod.rs:543)
or ecx, edx
mov edx, ecx
None => None, (/home/hnakamur/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/str/mod.rs:694)
cmp ecx, 1114112
jne .LBB1_20
jmp .LBB1_21
.LBB1_1:
xor r12d, r12d
.LBB1_21:
}
On the other hand, the part after if x >= 0xF0
in calc_with_char_indices()
does NOT have assembler for ch = (init & 7) << 18 | utf8_acc_cont_byte(y_z, w);
if x >= 0xF0 { (/home/hnakamur/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/str/mod.rs:539)
cmp r9b, -16
if x >= 0xF0 { (/home/hnakamur/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/str/mod.rs:539)
jb .LBB0_16
.LBB0_12:
if is_empty!(self) { (/home/hnakamur/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/slice/mod.rs:3750)
cmp rdi, rsi
if is_empty!(self) { (/home/hnakamur/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/slice/mod.rs:3750)
jne .LBB0_14
xor ebx, ebx
jmp .LBB0_15
.LBB0_17:
}