Vawious optimizations UwU

pull/2/head
Isaac Mills 3 years ago
parent c14a18ab95
commit 1111bda1ca
No known key found for this signature in database
GPG Key ID: B67D7410F33A0F61

@ -28,6 +28,7 @@ indicatif = "0.16.2"
linkify = "0.8.0" linkify = "0.8.0"
ahash = "0.7.6" ahash = "0.7.6"
rand_xoshiro = "0.6.0" rand_xoshiro = "0.6.0"
memmap = "0.7.0"
[profile.release] [profile.release]
lto = "fat" lto = "fat"

@ -1,26 +1,26 @@
pub const FACES_SIZE: usize = 15; pub const FACES_SIZE: usize = 14;
pub const FACES: [&str; FACES_SIZE] = [ pub const FACES: [&[u8]; FACES_SIZE] = [
"OwO", "UwU", ">w<", "^w^", "ÚwÚ", "^-^", ":3", "x3", "xDD", ";;w;;", ">_<", ">_>", "^.^", b"OwO", b"UwU", b">w<", b"^w^", b"^-^", b":3", b"x3", b"xDD", b";;w;;", b">_<", b">_>", b"^.^",
":33", "uWu", b":33", b"uWu",
]; ];
pub const ACTIONS_SIZE: usize = 17; pub const ACTIONS_SIZE: usize = 17;
pub const ACTIONS: [&str; ACTIONS_SIZE] = [ pub const ACTIONS: [&[u8]; ACTIONS_SIZE] = [
"*notices bulge*", b"*notices bulge*",
"*cries*", b"*cries*",
"*hugs tightly*", b"*hugs tightly*",
"*screams*", b"*screams*",
"*looks away*", b"*looks away*",
"*blushes*", b"*blushes*",
"*sweats*", b"*sweats*",
"*cuddles you*", b"*cuddles you*",
"*moans*", b"*moans*",
"*giggles shyly*", b"*giggles shyly*",
"*looks at you*", b"*looks at you*",
"*twerks*", b"*twerks*",
"*sighs*", b"*sighs*",
"*leans over*", b"*leans over*",
"*pokes you*", b"*pokes you*",
"*teleports behind you*", b"*teleports behind you*",
"*shuffles closer*", b"*shuffles closer*",
]; ];

@ -1,46 +1,10 @@
use std::fs::File; use std::io::{BufWriter, Error, Write};
use std::io::{BufRead, BufReader, BufWriter, Error, Write};
use std::path::Path;
pub struct UwUInFile {
file_bytes: u64,
reader: BufReader<File>,
pub buffer: String,
}
#[repr(transparent)] #[repr(transparent)]
pub struct UwUOutFile<T: Write> { pub struct UwUOutFile<T: Write> {
writer: BufWriter<T>, writer: BufWriter<T>,
} }
impl UwUInFile {
#[inline]
pub fn new(path: &Path) -> Result<UwUInFile, Error> {
let file = File::open(path)?;
Ok(UwUInFile {
file_bytes: file.metadata()?.len(),
reader: BufReader::new(file),
buffer: String::new(),
})
}
#[inline]
pub fn read_until_newline(&mut self) -> Result<usize, Error> {
self.reader.read_line(&mut self.buffer)
}
#[inline]
pub fn clear_buffer(&mut self) {
self.buffer.clear();
}
#[inline]
pub fn get_file_bytes(&self) -> u64 {
self.file_bytes
}
}
impl<T: Write> UwUOutFile<T> { impl<T: Write> UwUOutFile<T> {
#[inline] #[inline]
pub fn new(writer: T) -> UwUOutFile<T> { pub fn new(writer: T) -> UwUOutFile<T> {
@ -49,23 +13,8 @@ impl<T: Write> UwUOutFile<T> {
} }
} }
#[inline]
pub fn write_newline(&mut self) -> Result<(), Error> {
self.writer.write_all(b"\n")
}
#[inline]
pub fn write_string(&mut self, write_str: &str) -> Result<(), Error> {
self.writer.write_all(write_str.as_bytes())
}
#[inline] #[inline]
pub fn write_bytes(&mut self, write_bytes: &[u8]) -> Result<(), Error> { pub fn write_bytes(&mut self, write_bytes: &[u8]) -> Result<(), Error> {
self.writer.write_all(write_bytes) self.writer.write_all(write_bytes)
} }
#[inline]
pub fn write_fmt(&mut self, fmt: std::fmt::Arguments) -> Result<(), Error> {
self.writer.write_fmt(fmt)
}
} }

@ -9,7 +9,7 @@ use constants::ACTIONS;
use constants::ACTIONS_SIZE; use constants::ACTIONS_SIZE;
use constants::FACES; use constants::FACES;
use constants::FACES_SIZE; use constants::FACES_SIZE;
use io::{UwUInFile, UwUOutFile}; use io::UwUOutFile;
use progress_bar::UwUProgressBar; use progress_bar::UwUProgressBar;
use seeder::UwUSeeder; use seeder::UwUSeeder;
@ -19,34 +19,43 @@ mod progress_bar;
mod seeder; mod seeder;
#[derive(Debug)] #[derive(Debug)]
struct Modifiers { pub struct UwUify<'a> {
supplied_at_runtime: bool, text: &'a str,
input: &'a str,
output: &'a str,
words: f64, words: f64,
faces: f64, faces: f64,
actions: f64, actions: f64,
stutters: f64, stutters: f64,
}
#[derive(Debug)]
pub struct UwUify<'a> {
text: &'a str,
input: &'a Path,
output: &'a str,
modifiers: Modifiers,
random: bool, random: bool,
linkify: LinkFinder, linkify: LinkFinder,
} }
impl<'a> Default for UwUify<'a> {
fn default() -> Self {
Self {
text: "",
input: "",
output: "",
words: 1.0,
faces: 0.05,
actions: 0.125,
stutters: 0.225,
random: false,
linkify: LinkFinder::new(),
}
}
}
impl<'a> UwUify<'a> { impl<'a> UwUify<'a> {
pub fn new( pub fn new(
text: Option<&'a str>, text: Option<&'a str>,
infile: Option<&'a Path>, infile: Option<&'a str>,
outfile: Option<&'a str>, outfile: Option<&'a str>,
supplied_at_runtime: bool, words: Option<&'a str>,
words: f64, faces: Option<&'a str>,
faces: f64, actions: Option<&'a str>,
actions: f64, stutters: Option<&'a str>,
stutters: f64,
random: bool, random: bool,
) -> UwUify<'a> { ) -> UwUify<'a> {
// I dislike this // I dislike this
@ -54,21 +63,28 @@ impl<'a> UwUify<'a> {
let mut linkify = LinkFinder::new(); let mut linkify = LinkFinder::new();
linkify.kinds(&[LinkKind::Email, LinkKind::Url]); linkify.kinds(&[LinkKind::Email, LinkKind::Url]);
linkify.url_must_have_scheme(false); linkify.url_must_have_scheme(false);
let mut uwuify = UwUify {
UwUify {
text: text.unwrap_or_default(), text: text.unwrap_or_default(),
input: infile.unwrap_or(Path::new("")), input: infile.unwrap_or_default(),
output: outfile.unwrap_or_default(), output: outfile.unwrap_or_default(),
modifiers: Modifiers {
supplied_at_runtime,
words,
faces,
actions,
stutters,
},
random, random,
linkify, linkify,
..Default::default()
};
if let Some(words) = words {
uwuify.words = words.parse::<f64>().unwrap();
}
if let Some(faces) = faces {
uwuify.faces = faces.parse::<f64>().unwrap();
}
if let Some(actions) = actions {
uwuify.actions = actions.parse::<f64>().unwrap();
} }
if let Some(stutters) = stutters {
uwuify.stutters = stutters.parse::<f64>().unwrap();
}
uwuify
} }
pub fn uwuify(&self) -> Result<(), Error> { pub fn uwuify(&self) -> Result<(), Error> {
@ -84,11 +100,9 @@ impl<'a> UwUify<'a> {
} }
let mut uwu_out_file = UwUOutFile::new(File::create(&self.output)?); let mut uwu_out_file = UwUOutFile::new(File::create(&self.output)?);
let uwu_progress_bar = UwUProgressBar::new();
self.uwuify_sentence(&self.text, &mut uwu_out_file)?; self.uwuify_sentence(&self.text, &mut uwu_out_file)?;
let mut uwu_progress_bar = UwUProgressBar::new(self.text.len() as u64);
uwu_progress_bar.update_progess(self.text.len());
uwu_progress_bar.finish("UwU'ifying Complete!"); uwu_progress_bar.finish("UwU'ifying Complete!");
Ok(()) Ok(())
} else { } else {
@ -100,7 +114,7 @@ impl<'a> UwUify<'a> {
let mut out = UwUOutFile::new(std::io::sink()); let mut out = UwUOutFile::new(std::io::sink());
self.uwuify_sentence(&self.text, &mut out)?; self.uwuify_sentence(&self.text, &mut out)?;
#[cfg(not(test))] #[cfg(not(test))]
out.write_newline()?; out.write_bytes(b"\n")?;
Ok(()) Ok(())
} }
} else { } else {
@ -112,23 +126,17 @@ impl<'a> UwUify<'a> {
)); ));
} }
let mut uwu_in_file = UwUInFile::new(&self.input)?; let uwu_progress_bar = UwUProgressBar::new();
let mut uwu_out_file = UwUOutFile::new(File::create(&self.output)?); self.uwuify_sentence(
let mut uwu_progress_bar = UwUProgressBar::new(uwu_in_file.get_file_bytes()); unsafe {
std::str::from_utf8_unchecked(
loop { memmap::Mmap::map(&File::open(&self.input)?)?.as_ref(),
let bytes_read_in = uwu_in_file.read_until_newline()?; )
if bytes_read_in != 0 { },
self.uwuify_sentence(&uwu_in_file.buffer, &mut uwu_out_file)?; &mut UwUOutFile::new(File::create(&self.output)?),
uwu_out_file.write_newline()?; )?;
uwu_progress_bar.update_progess(bytes_read_in);
uwu_in_file.clear_buffer();
} else {
uwu_progress_bar.finish("UwU'ifying Complete!"); uwu_progress_bar.finish("UwU'ifying Complete!");
return Ok(()); Ok(())
}
}
} }
} }
@ -137,76 +145,47 @@ impl<'a> UwUify<'a> {
text: &str, text: &str,
out: &mut UwUOutFile<T>, out: &mut UwUOutFile<T>,
) -> Result<(), std::io::Error> { ) -> Result<(), std::io::Error> {
text.split_whitespace().try_for_each(|word| { text.as_bytes()
self.uwuify_word(word, out)?; .split(|w| matches!(*w, b'\t' | b'\x0C' | b'\r' | b' '))
out.write_string(" ") .try_for_each(|word| {
})
}
fn uwuify_word<T: Write>(&self, word: &str, out: &mut UwUOutFile<T>) -> Result<(), Error> {
let mut seeder = UwUSeeder::new(word, self.random); let mut seeder = UwUSeeder::new(word, self.random);
let random_value = seeder.random(); let random_value = seeder.random();
if !self.modifiers.supplied_at_runtime { if random_value <= self.faces {
if random_value <= self.modifiers.faces { out.write_bytes(FACES[seeder.random_int(0..FACES_SIZE)])?;
out.write_string(FACES[seeder.random_int(0..FACES_SIZE)])?;
out.write_bytes(b" ")?; out.write_bytes(b" ")?;
} else if random_value <= self.modifiers.actions { } else if random_value <= self.actions {
out.write_string(ACTIONS[seeder.random_int(0..ACTIONS_SIZE)])?; out.write_bytes(ACTIONS[seeder.random_int(0..ACTIONS_SIZE)])?;
out.write_bytes(b" ")?; out.write_bytes(b" ")?;
} else if random_value <= self.modifiers.stutters { } else if random_value <= self.stutters {
(0..seeder.random_int(1..2)).into_iter().try_for_each(|_| { (0..seeder.random_int(1..2)).into_iter().try_for_each(|_| {
out.write_bytes(&word.as_bytes()[0..1])?; out.write_bytes(&word[0..1])?;
out.write_bytes(b"-") out.write_bytes(b"-")
})?; })?;
} }
} else {
if random_value <= self.modifiers.stutters {
(0..seeder.random_int(1..2)).into_iter().try_for_each(|_| {
out.write_bytes(&word.as_bytes()[0..1])?;
out.write_bytes(b"-")
})?;
}
if random_value <= self.modifiers.faces {
out.write_string(FACES[seeder.random_int(0..FACES_SIZE)])?;
out.write_bytes(b" ")?;
}
if random_value <= self.modifiers.actions {
out.write_string(ACTIONS[seeder.random_int(0..ACTIONS_SIZE)])?;
out.write_bytes(b" ")?;
}
}
if self.linkify.links(word).count() > 0 { if self
return out.write_string(word); .linkify
.links(unsafe { std::str::from_utf8_unchecked(word) })
.count()
> 0
|| random_value > self.words
{
out.write_bytes(word)?;
} else {
(0..word.len()).try_for_each(|index| match word[index] {
b'L' | b'R' => out.write_bytes(b"W"),
b'l' | b'r' => out.write_bytes(b"w"),
b'E' | b'e' | b'A' | b'I' | b'O' | b'U' | b'a' | b'i' | b'o' | b'u' => {
match word.get(index - 1).unwrap_or(&word[0]) {
b'N' | b'n' => out.write_bytes(&[b'y', word[index]]),
_ => out.write_bytes(&[word[index]]),
} }
let mut seeder = UwUSeeder::new(word, self.random);
if seeder.random() > self.modifiers.words {
return out.write_string(word);
} }
_ => out.write_bytes(&[word[index]]),
let word_bytes = word.as_bytes(); })?;
let uwu_text_count = word.len();
(0..uwu_text_count).try_for_each(|index| {
let previous_char =
*word_bytes.get(index - 1).unwrap_or_else(|| &word_bytes[0]) as char;
let current_char = word_bytes[index] as char;
match current_char {
'L' | 'R' => out.write_bytes(b"W"),
'l' | 'r' => out.write_bytes(b"w"),
'E' | 'e' => match previous_char {
'N' | 'n' => out.write_fmt(format_args!("y{}", current_char)),
_ => out.write_fmt(format_args!("{}", current_char)),
},
'A' | 'I' | 'O' | 'U' | 'a' | 'i' | 'o' | 'u' => match previous_char {
'N' | 'n' => out.write_fmt(format_args!("y{}", current_char)),
_ => out.write_fmt(format_args!("{}", current_char)),
},
_ => out.write_fmt(format_args!("{}", current_char)),
} }
out.write_bytes(b" ")
}) })
} }
} }
@ -214,20 +193,18 @@ impl<'a> UwUify<'a> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
extern crate test; extern crate test;
use linkify::{LinkFinder, LinkKind};
#[bench] #[bench]
fn uwu_bench(b: &mut test::Bencher) { fn uwu_bench(b: &mut test::Bencher) {
let uwuify = super::UwUify::new( let mut linkify = LinkFinder::new();
Some(include_str!("test.txt")), linkify.kinds(&[LinkKind::Email, LinkKind::Url]);
None, linkify.url_must_have_scheme(false);
None, let uwuify = super::UwUify {
false, text: include_str!("test.txt"),
1.0, linkify,
0.05, ..Default::default()
0.125, };
0.225,
false,
);
b.iter(|| uwuify.uwuify()); b.iter(|| uwuify.uwuify());
} }
} }

@ -2,12 +2,6 @@ use clap::{Arg, ArgGroup, ErrorKind};
use uwuify::UwUify; use uwuify::UwUify;
macro_rules! modifiers_supplied_at_runtime {
($faces_occurrences:expr,$actions_occurrences:expr,$stutters_occurrences:expr) => {
$faces_occurrences > 0 || $actions_occurrences > 0 || $stutters_occurrences > 0
};
}
macro_rules! app { macro_rules! app {
() => { () => {
clap::App::new(env!("CARGO_PKG_NAME")) clap::App::new(env!("CARGO_PKG_NAME"))
@ -103,17 +97,12 @@ fn main() {
match UwUify::new( match UwUify::new(
matches.value_of("text"), matches.value_of("text"),
matches.value_of("infile").map(|f| std::path::Path::new(f)), matches.value_of("infile"),
matches.value_of("outfile"), matches.value_of("outfile"),
modifiers_supplied_at_runtime!( matches.value_of("words"),
matches.occurrences_of("faces"), matches.value_of("faces"),
matches.occurrences_of("actions"), matches.value_of("actions"),
matches.occurrences_of("stutters") matches.value_of("stutters"),
),
matches.value_of_t_or_exit("words"),
matches.value_of_t_or_exit("faces"),
matches.value_of_t_or_exit("actions"),
matches.value_of_t_or_exit("stutters"),
matches.is_present("random"), matches.is_present("random"),
) )
.uwuify() .uwuify()

@ -5,20 +5,16 @@ pub struct UwUProgressBar(ProgressBar);
impl UwUProgressBar { impl UwUProgressBar {
#[inline] #[inline]
pub fn new(file_total_bytes: u64) -> UwUProgressBar { pub fn new() -> UwUProgressBar {
let progress_bar = ProgressBar::new(file_total_bytes); let progress_bar = ProgressBar::new_spinner();
progress_bar.set_style(ProgressStyle::default_bar() progress_bar.set_style(
.template("{spinner:.green} [{elapsed_precise}] [{bar:60.cyan/blue}] {bytes}/{total_bytes} ({eta}) {msg}") ProgressStyle::default_spinner().template("{spinner:.green} [{elapsed_precise}] {msg}"),
.progress_chars("#>-")); );
progress_bar.enable_steady_tick(100);
UwUProgressBar(progress_bar) UwUProgressBar(progress_bar)
} }
#[inline]
pub fn update_progess(&mut self, bytes_read_in: usize) {
self.0.inc(bytes_read_in as u64);
}
#[inline] #[inline]
pub fn finish(&self, message: &'static str) { pub fn finish(&self, message: &'static str) {
self.0.finish_with_message(message); self.0.finish_with_message(message);

@ -13,10 +13,10 @@ pub struct UwUSeeder {
impl UwUSeeder { impl UwUSeeder {
#[inline] #[inline]
pub fn new(word: &str, random: bool) -> UwUSeeder { pub fn new(word: &[u8], random: bool) -> UwUSeeder {
let entropy = if !random { let entropy = if !random {
let mut hasher = ahash::AHasher::default(); let mut hasher = ahash::AHasher::default();
hasher.write(word.as_bytes()); hasher.write(word);
hasher.finish() hasher.finish()
} else { } else {
rand::rngs::OsRng.next_u64() rand::rngs::OsRng.next_u64()
@ -30,7 +30,7 @@ impl UwUSeeder {
#[inline] #[inline]
pub fn random(&mut self) -> f64 { pub fn random(&mut self) -> f64 {
f64::from_ne_bytes(self.floating.next_u64().to_ne_bytes()) self.floating.gen_range(0.0..1.0)
} }
#[inline] #[inline]

Loading…
Cancel
Save