//! Code generation for bytewise slice comparison. Target is to provide a compile time optimization //! within a single macro which looks like: //! //! ``` //! macro_rules! slice_compare ( //! ($a:expr, $b:expr, $len:expr) => {{ //! match $len { //! 1 => cmp!($a, $b, u8, 0), //! 2 => cmp!($a, $b, u16, 0), //! 3 => cmp!($a, $b, u16, 0) && cmp!($a, $b, u8, 2), //! 4 => cmp!($a, $b, u32, 0), //! ... //! ``` //! //! The supported slice length can be set by changing the `OPT_LEN` variable. static OPT_LEN: usize = 256; use std::{env, iter, io}; use std::fs::File; use std::io::Write; use std::path::Path; use std::error::Error; pub fn main() { run().expect("Could not generate needed source code."); } fn fill(indent: usize) -> String { iter::repeat(' ').take(indent).collect() } fn run() -> Result<(), Box> { let out_dir = env::var("OUT_DIR")?; let dest_path = Path::new(&out_dir).join("compare.rs"); let mut f = File::create(&dest_path)?; // Generate the slice comparison source code writeln!(f, "macro_rules! slice_compare (")?; writeln!(f, "{}($a:expr, $b:expr, $len:expr) => {{{{", fill(4))?; writeln!(f, "{}match $len {{", fill(8))?; for i in 1..OPT_LEN + 1 { let mut bits = i * 8 as usize; let mut sizes = vec![8, 16, 32, 64]; let mut offset = 0; write!(f, "{}{} => ", fill(12), i)?; while !sizes.is_empty() { let size = sizes.last().ok_or(io::Error::from(io::ErrorKind::Other))?.clone(); if bits >= size { if offset > 0 { write!(f, " && ")?; } write!(f, "cmp!($a, $b, u{}, {})", size, offset)?; bits = bits.checked_sub(size).ok_or(io::Error::from(io::ErrorKind::Other))?; offset += size / 8; } else { sizes.pop(); } if bits == 0 { break; } } writeln!(f, ",")?; } writeln!(f, "{}_ => unsafe {{ memcmp($a, $b, $len) == 0 }},", fill(12))?; writeln!(f, "{}}}", fill(8))?; writeln!(f, "{}}}}}", fill(4))?; writeln!(f, ");")?; Ok(()) }