diff --git a/sailfish/src/runtime/filter.rs b/sailfish/src/runtime/filter.rs index 6fbc1f7..60e04bf 100644 --- a/sailfish/src/runtime/filter.rs +++ b/sailfish/src/runtime/filter.rs @@ -1,6 +1,9 @@ +//! Build-in filters + // TODO: performance improvement use std::fmt; +use std::ptr; use super::{Buffer, Render, RenderError}; @@ -86,6 +89,60 @@ pub fn lower(expr: &T) -> Lower { Lower(expr) } +pub struct Trim<'a, T>(&'a T); + +impl<'a, T: Render> Render for Trim<'a, T> { + #[inline] + fn render(&self, b: &mut Buffer) -> Result<(), RenderError> { + let old_len = b.len(); + self.0.render(b)?; + trim_impl(b, old_len); + Ok(()) + } + + #[inline] + fn render_escaped(&self, b: &mut Buffer) -> Result<(), RenderError> { + let old_len = b.len(); + self.0.render_escaped(b)?; + trim_impl(b, old_len); + Ok(()) + } +} + +fn trim_impl(b: &mut Buffer, old_len: usize) { + debug_assert!(b.len() >= old_len); + let new_contents = &b.as_str()[old_len..]; + let trimmed = new_contents.trim(); + let trimmed_len = trimmed.len(); + + if new_contents.len() != trimmed_len { + // performs inplace trimming + + if new_contents.as_ptr() != trimmed.as_ptr() { + debug_assert!(new_contents.as_ptr() < trimmed.as_ptr()); + let offset = trimmed.as_ptr() as usize - new_contents.as_ptr() as usize; + unsafe { + ptr::copy( + b.as_mut_ptr().add(old_len + offset), + b.as_mut_ptr().add(old_len), + trimmed_len, + ); + } + } + + debug_assert!(b.capacity() >= old_len + trimmed_len); + unsafe { + b._set_len(old_len + trimmed_len); + } + } +} + +/// convert the rendered contents to lowercase +#[inline] +pub fn trim(expr: &T) -> Trim { + Trim(expr) +} + #[cfg(test)] mod tests { use super::*; @@ -104,4 +161,23 @@ mod tests { lower(&"

TITLE

").render_escaped(&mut buf).unwrap(); assert_eq!(buf.as_str(), "<h1>title</h1>"); } + + #[test] + fn trim_test() { + let mut buf = Buffer::new(); + trim(&" hello ").render(&mut buf).unwrap(); + trim(&"hello ").render(&mut buf).unwrap(); + trim(&" hello").render(&mut buf).unwrap(); + assert_eq!(buf.as_str(), "hellohellohello"); + + let mut buf = Buffer::new(); + trim(&"hello ").render(&mut buf).unwrap(); + trim(&" hello").render(&mut buf).unwrap(); + trim(&"hello").render(&mut buf).unwrap(); + assert_eq!(buf.as_str(), "hellohellohello"); + + let mut buf = Buffer::new(); + trim(&" hello").render(&mut buf).unwrap(); + assert_eq!(buf.as_str(), "hello"); + } }