//#[inline] fn reverse(slice: &mut [T]) { match slice { [ref mut a, ref mut rest @ .., ref mut b] => { std::mem::swap(a, b); reverse(rest) }, [] | [_] => (), } } /// This actually works! And is so easily parallelisable with something like rayon, or asyncing by spawning/creating the tail-call into a new task, then either waiting them concurrently or in parallen (spawn or created future from just calling the function without awaiting it) #[allow(dead_code)] fn binsearch<'a, V: ?Sized, T: PartialEq + 'a>(slice: &'a [T], find: &V) -> Option<&'a T> { match slice { [ref a, pivot @ .., ref b] => { match (a==find, b==find) { (true, _) => Some(a), (_, true) => Some(b), _ => binsearch(pivot, find), } }, [ref a] if a == find => Some(a), _ => None, } } trait Input: AsRef<[u8]> + std::fmt::Debug{} impl Input for T where T: AsRef<[u8]> + std::fmt::Debug{} fn collect_input() -> Box + 'static> { use std::{ ffi::{OsStr, OsString}, os::unix::ffi::*, }; #[derive(Debug)] enum MaybeUTF8 { UTF8(String), Raw(OsString), } impl AsRef<[u8]> for MaybeUTF8 { #[inline] fn as_ref(&self) -> &[u8] { match self { Self::UTF8(string) => string.as_bytes(), Self::Raw(raw) => raw.as_bytes(), } } } impl From for MaybeUTF8 { #[inline(always)] fn from(from: String) -> Self { Self::UTF8(from) } } impl From for MaybeUTF8 { #[inline(always)] fn from(from: OsString) -> Self { Self::Raw(from) } } #[allow(dead_code)] impl MaybeUTF8 { #[inline(always)] pub fn from_raw_bytes(bytes: &[u8]) -> Self { Self::Raw(OsStr::from_bytes(bytes).to_os_string()) } #[inline(always)] #[deprecated(note="XXX: TODO: Only use this if the read_until() into vec does not add the '\n' into the vec as well. Otherwise, *always* use this.")] pub fn from_raw_vec(vec: Vec) -> Self { Self::Raw(OsString::from_vec(vec)) } } if std::env::args_os().len() <= 1 { use std::io::{ self, BufRead, }; // No args, collect stdin lines if !cfg!(feature="byte-strings") { // Collect utf8 string lines Box::new(io::stdin() .lock() .lines() .filter_map(Result::ok) .map(MaybeUTF8::from)) } else { // Collect arbitrary byte strings struct OsLineReader<'a>(io::StdinLock<'a>, Vec); impl<'a> Iterator for OsLineReader<'a> { type Item = MaybeUTF8; fn next(&mut self) -> Option { Some(match handle_fmt_err_or(self.0.read_until(b'\n', &mut self.1), || 0) { 0 => return None, read_sz => { let line = MaybeUTF8::from_raw_bytes(&self.1[..]); //TODO: XXX: If self.1 here does not have the '\n' added into it by read_until(); use from_raw_vec(self.1.clone()) instead; it'll be more efficient. self.1.clear(); //TODO: todo!("Do we need read_sz ({read_sz}) at all here? Will the `\n` be inside the read string?"); line }, }) } } Box::new(OsLineReader(io::stdin().lock(), // Acquire the lock until the iterator is consumed (like all other paths in this function) Vec::with_capacity(4096))) // Buffer of size 4k } } else { // Has arguments, return them if cfg!(feature="byte-strings") { Box::new(std::env::args_os().skip(1).map(MaybeUTF8::from)) } else if cfg!(feature="ignore-invalid-args") { Box::new(std::env::args_os().skip(1).filter_map(|os| os.into_string().ok()).map(MaybeUTF8::from)) } else { Box::new(std::env::args_os().skip(1).map(|os| os.to_string_lossy().into_owned().into())) } } } #[cfg_attr(feature="ignore-output-errors", inline)] fn handle_fmt_err_or(res: std::io::Result, or: F) -> T where F: FnOnce() -> T { #[cfg(not(feature="ignore-output-errors"))] if let Err(e) = res { eprintln!("[!] failed to write line: {e}"); or() } #[cfg(feature="ignore-output-errors")] res.unwrap_or_else(|_| or()) } #[cfg_attr(feature="ignore-output-errors", inline(always))] fn handle_fmt_err(res: std::io::Result) { #[cfg(not(feature="ignore-output-errors"))] if let Err(e) = res { eprintln!("[!] failed to write line: {e}"); } let _ = res; } fn main() { let mut args: Vec<_> = collect_input().collect(); reverse(&mut args[..]); //eprintln!("{:?}", binsearch(&args[..], "1")); // It works! #[cfg(feature="output-lines")] { #[allow(unused_imports)] use std::io::{ Write, BufWriter, }; #[cfg(feature="buffer-output")] let mut out = BufWriter::new(std::io::stdout().lock()); #[cfg(not(feature="buffer-output"))] let mut out = std::io::stdout().lock(); for x in args.iter() { handle_fmt_err({ if cfg!(feature="output-quoted") { //XXX: This doesn't flush, right? It shouldn't, but maybe we should test it? writeln!(&mut out, "{:?}", x) } else { //writeln!(&mut out, "{}", x) out.write(x.as_ref()) .and_then(|_| out.write(b"\n")) .map(|_| {}) } }); } //#[cfg(feature="buffer-output")] handle_fmt_err(out.flush()); //XXX: Do we need to flush when not buffering output? Does it matter since buffering output will be enabled by default and should almost always be enabled? } #[cfg(not(feature="output-lines"))] println!("{:?}", args); }