aboutsummaryrefslogtreecommitdiffstats
path: root/askama_escape
diff options
context:
space:
mode:
authorLibravatar René Kijewski <kijewski@library.vetmed.fu-berlin.de>2022-04-13 11:50:10 +0200
committerLibravatar Dirkjan Ochtman <dirkjan@ochtman.nl>2022-04-13 13:18:27 +0200
commitfc779e22c3bfa0e94df208a88a361b99e28a9129 (patch)
tree3a09dfd2352be5d217317f03561170e3baaf4f0c /askama_escape
parentab39ced64e4a56b741ee32487d3340e008c7e4fb (diff)
downloadaskama-fc779e22c3bfa0e94df208a88a361b99e28a9129.tar.gz
askama-fc779e22c3bfa0e94df208a88a361b99e28a9129.tar.bz2
askama-fc779e22c3bfa0e94df208a88a361b99e28a9129.zip
Remove `unsafe { … }` code from askama_escape
Using only safe code is actually same as fast as the previous "unsafe" code according to the crate's benchmark. The code was extracted from [markup]'s escape function in [escape.rs], written by Utkarsh Kukreti <utkarshkukreti@gmail.com>, licensed as `MIT OR Apache-2.0`. [markup]: https://crates.io/crates/markup [escape.rs]: https://github.com/utkarshkukreti/markup.rs/blob/8ec40428483790b2c296e907e7be4147b157fe8f/markup/src/escape.rs#L1-L21
Diffstat (limited to 'askama_escape')
-rw-r--r--askama_escape/src/lib.rs84
1 files changed, 34 insertions, 50 deletions
diff --git a/askama_escape/src/lib.rs b/askama_escape/src/lib.rs
index 1788843..8e57906 100644
--- a/askama_escape/src/lib.rs
+++ b/askama_escape/src/lib.rs
@@ -107,40 +107,31 @@ where
pub struct Html;
-macro_rules! escaping_body {
- ($start:ident, $i:ident, $fmt:ident, $bytes:ident, $quote:expr) => {{
- if $start < $i {
- $fmt.write_str(unsafe { str::from_utf8_unchecked(&$bytes[$start..$i]) })?;
- }
- $fmt.write_str($quote)?;
- $start = $i + 1;
- }};
-}
-
impl Escaper for Html {
fn write_escaped<W>(&self, mut fmt: W, string: &str) -> fmt::Result
where
W: Write,
{
- let bytes = string.as_bytes();
- let mut start = 0;
- for (i, b) in bytes.iter().enumerate() {
- if b.wrapping_sub(b'"') <= FLAG {
- match *b {
- b'<' => escaping_body!(start, i, fmt, bytes, "&lt;"),
- b'>' => escaping_body!(start, i, fmt, bytes, "&gt;"),
- b'&' => escaping_body!(start, i, fmt, bytes, "&amp;"),
- b'"' => escaping_body!(start, i, fmt, bytes, "&quot;"),
- b'\'' => escaping_body!(start, i, fmt, bytes, "&#x27;"),
- _ => (),
- }
+ let mut last = 0;
+ for (index, byte) in string.bytes().enumerate() {
+ macro_rules! go {
+ ($expr:expr) => {{
+ fmt.write_str(&string[last..index])?;
+ fmt.write_str($expr)?;
+ last = index + 1;
+ }};
+ }
+
+ match byte {
+ b'<' => go!("&lt;"),
+ b'>' => go!("&gt;"),
+ b'&' => go!("&amp;"),
+ b'"' => go!("&quot;"),
+ b'\'' => go!("&#x27;"),
+ _ => {}
}
}
- if start < bytes.len() {
- fmt.write_str(unsafe { str::from_utf8_unchecked(&bytes[start..]) })
- } else {
- Ok(())
- }
+ fmt.write_str(&string[last..])
}
}
@@ -170,8 +161,6 @@ pub trait Escaper {
W: Write;
}
-const FLAG: u8 = b'>' - b'"';
-
/// Escape chevrons, ampersand and apostrophes for use in JSON
#[cfg(feature = "json")]
#[derive(Debug, Clone, Default)]
@@ -191,30 +180,25 @@ impl JsonEscapeBuffer {
#[cfg(feature = "json")]
impl std::io::Write for JsonEscapeBuffer {
fn write(&mut self, bytes: &[u8]) -> std::io::Result<usize> {
- macro_rules! push_esc_sequence {
- ($start:ident, $i:ident, $self:ident, $bytes:ident, $quote:expr) => {{
- if $start < $i {
- $self.0.extend_from_slice(&$bytes[$start..$i]);
- }
- $self.0.extend_from_slice($quote);
- $start = $i + 1;
- }};
- }
+ let mut last = 0;
+ for (index, byte) in bytes.iter().enumerate() {
+ macro_rules! go {
+ ($expr:expr) => {{
+ self.0.extend(&bytes[last..index]);
+ self.0.extend($expr);
+ last = index + 1;
+ }};
+ }
- self.0.reserve(bytes.len());
- let mut start = 0;
- for (i, b) in bytes.iter().enumerate() {
- match *b {
- b'&' => push_esc_sequence!(start, i, self, bytes, br#"\u0026"#),
- b'\'' => push_esc_sequence!(start, i, self, bytes, br#"\u0027"#),
- b'<' => push_esc_sequence!(start, i, self, bytes, br#"\u003c"#),
- b'>' => push_esc_sequence!(start, i, self, bytes, br#"\u003e"#),
- _ => (),
+ match byte {
+ b'&' => go!(br#"\u0026"#),
+ b'\'' => go!(br#"\u0027"#),
+ b'<' => go!(br#"\u003c"#),
+ b'>' => go!(br#"\u003e"#),
+ _ => {}
}
}
- if start < bytes.len() {
- self.0.extend_from_slice(&bytes[start..]);
- }
+ self.0.extend(&bytes[last..]);
Ok(bytes.len())
}