Someone said C was better than Rust at in-place string modifications. So I made this.
use in_place_string_map::MapInPlace;
fn decode_percent(s: &mut str) -> &mut str {
let mut m = MapInPlace::new(s);
while let Some(c) = m.pop() {
match c {
'%' => {
let num = m.pop_chars(2).expect("not enough chars");
let n = u8::from_str_radix(num, 16).expect("invalid hex");
m.push(n as char).expect("no more capacity");
}
_ => {
m.push(c).expect("no more capacity");
}
}
}
m.into_mapped()
}
The only thing you need to be careful of is to not push more bytes than you have popped so far. Here it's fine since %ff is 2 bytes long (the longest it can be) but took 3 bytes of source text. It's not unsafe to do this of course, you'll just fail to push.
More details about how it works are both in the code (it's somewhat commented) and the blog post.
I have a mirror of the repo over on https://github.com/5225225/in-place-string-map (This is not the canonical URL, that is hosted on my gitea at https://git.5snb.club/5225225/in-place-string-map). Feel free to make issues/pull requests against the github repo. Alternatively, contact me (Email in the authors field, or read the commit emails).