pub(crate) fn heading_slug_into(slug: &mut String, raw: &str) {
let bytes = raw.as_bytes();
let len = bytes.len();
slug.clear();
if len <= 64 {
let mut is_safe = true;
let mut has_content = false;
for (idx, &b) in bytes.iter().enumerate() {
if b.is_ascii_alphanumeric() {
has_content = true;
} else if b == b'-' {
if idx == 0 || idx == len - 1 || !has_content {
is_safe = false;
break;
}
} else {
is_safe = false;
break;
}
}
if is_safe && has_content {
let mut all_lower = true;
for &b in bytes {
if b.is_ascii_uppercase() {
all_lower = false;
break;
}
}
if all_lower {
slug.push_str(raw);
return;
}
slug.push_str(raw);
slug.make_ascii_lowercase();
return;
}
}
slug.reserve(len.saturating_sub(slug.capacity()));
let mut i = 0;
let mut prev_dash = true;
while i < len {
let b = bytes[i];
match b {
b'*' | b'_' | b'~' | b'=' | b'+' | b'`' => {
i += 1;
}
b'<' => {
if let Some(close) = memchr::memchr(b'>', &bytes[i..]) {
i += close + 1;
} else {
i += 1;
}
}
b'\\' => {
i += 1;
}
b'[' | b']' | b'!' | b'(' | b')' => {
i += 1;
}
b' ' | b'\t' | b'-' | b'.' => {
if !prev_dash && !slug.is_empty() {
slug.push('-');
prev_dash = true;
}
i += 1;
}
b if b.is_ascii_alphanumeric() => {
slug.push(b.to_ascii_lowercase() as char);
prev_dash = false;
i += 1;
}
b if b >= 0x80 => {
let char_len = crate::utf8_char_len(b);
let c = raw[i..].chars().next().unwrap_or(' ');
if c.is_alphanumeric() {
for lc in c.to_lowercase() {
slug.push(lc);
}
prev_dash = false;
} else {
if !prev_dash && !slug.is_empty() {
slug.push('-');
prev_dash = true;
}
}
i += char_len;
}
_ => {
i += 1;
}
}
}
while slug.ends_with('-') {
slug.pop();
}
}
pub fn benchmark_heading_slug(raw: &str) -> String {
let mut slug = String::with_capacity(raw.len());
heading_slug_into(&mut slug, raw);
slug
}