#![feature(non_ascii_idents)]
#![allow(unknown_lints, uncommon_codepoints)]
#![cfg_attr(feature = "nightly", feature(asm))]
#![cfg_attr(feature = "nightly", feature(const_fn, const_panic))]
#![cfg_attr(feature = "nightly", feature(test))]
#[allow(unused_imports)] #[macro_use] extern crate lazy_static;
extern crate libc;
#[cfg(feature = "crossterm")] extern crate crossterm;
use std::any::Any;
use std::fs;
use std::fmt;
use std::io::{self, Read};
use std::marker::PhantomData;
#[allow(unused_imports)] use std::os::raw::c_int;
use std::path::Path;
use std::process::{Command, Stdio};
use std::str::{from_utf8_unchecked, FromStr};
#[allow(unused_imports)] use std::sync::Mutex;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::time::{Duration, SystemTime, UNIX_EPOCH};
#[cfg(test)] use std::thread::sleep;
pub fn filename<'a> (path: &'a str) -> &'a str {
let name = match path.rfind (|ch| ch == '/' || ch == '\\') {
Some (ofs) => &path[ofs+1..],
None => path};
if name.ends_with (".rs") {&name[0 .. name.len() - 3]} else {name}}
#[macro_export] macro_rules! try_s {
($e: expr) => {match $e {
Ok (ok) => ok,
Err (err) => {return Err (format! ("{}:{}] {}", $crate::filename (file!()), line!(), err));}}}}
#[macro_export] macro_rules! try_f {
($e: expr) => {match $e {
Ok (ok) => ok,
Err (err) => {return Err (From::from (format! ("{}:{}] {}", $crate::filename (file!()), line!(), err)));}}}}
#[macro_export] macro_rules! try_sp {
($e: expr) => {match $e {&Ok (ref ok) => ok,
&Err (ref err) => {return Err (From::from (format! ("{}:{}] {:?}", $crate::filename (file!()), line!(), err)));}}}}
#[macro_export] macro_rules! try_fu {
($e: expr) => {match $e {
Ok (ok) => ok,
Err (err) => {return Box::new (futures01::future::err (From::from (err)))}}}}
#[macro_export] macro_rules! try_fus {
($e: expr) => {match $e {
Ok (ok) => ok,
Err (err) => {return Box::new (futures01::future::err (ERRL! ("{}", err)))}}}}
#[macro_export] macro_rules! ERRL {
($format: expr, $($args: tt)+) => {format! (concat! ("{}:{}] ", $format), $crate::filename (file!()), line!(), $($args)+)};
($format: expr) => {format! (concat! ("{}:{}] ", $format), $crate::filename (file!()), line!())}}
#[macro_export] macro_rules! ERR {
($format: expr, $($args: tt)+) => {Err (ERRL! ($format, $($args)+))};
($format: expr) => {Err (ERRL! ($format))}}
#[cfg(feature = "base62")] pub mod base62;
#[cfg(all(feature = "base91", feature = "nightly"))] pub mod base91;
#[cfg(feature = "winapi")] pub mod win;
#[cfg(all(feature = "crossterm", not(target_arch = "wasm32")))]
fn isatty (fd: c_int) -> c_int {unsafe {libc::isatty (fd)}}
#[cfg(all(feature = "crossterm", target_arch = "wasm32"))]
fn isatty (_fd: c_int) -> c_int {0}
#[cfg(feature = "crossterm")]
lazy_static! {
static ref STATUS_LINE: Mutex<String> = Mutex::new (String::new());
pub static ref ISATTY: bool = isatty (1) != 0;
pub static ref STATUS_LINE_LM: AtomicUsize = AtomicUsize::new (0);}
#[cfg(feature = "crossterm")]
pub fn status_line_lm() -> u64 {STATUS_LINE_LM.load (Ordering::Relaxed) as u64}
#[cfg(feature = "crossterm")]
pub fn status_line_lm0() {STATUS_LINE_LM.store (0, Ordering::Relaxed)}
#[cfg(feature = "crossterm")]
fn delete_line (stdout: &mut io::Stdout) {
use crossterm::{terminal, QueueableCommand};
let _ = stdout.queue (terminal::Clear (terminal::ClearType::UntilNewLine));}
#[cfg(all(feature = "crossterm"))]
pub fn status_line (file: &str, line: u32, status: String) {
use crossterm::{QueueableCommand, cursor};
use io::{stdout, Write};
use std::collections::hash_map::DefaultHasher;
use std::hash::Hasher;
if let Ok (mut status_line) = STATUS_LINE.lock() {
let mut stdout = stdout();
let old_hash = {let mut hasher = DefaultHasher::new(); hasher.write (status_line.as_bytes()); hasher.finish()};
status_line.clear();
use std::fmt::Write;
let _ = write! (&mut *status_line, "{}:{}] {}", filename (file), line, status);
let new_hash = {let mut hasher = DefaultHasher::new(); hasher.write (status_line.as_bytes()); hasher.finish()};
if old_hash != new_hash {
STATUS_LINE_LM.store (now_ms() as usize, Ordering::Relaxed);
match crossterm::terminal::size() {
Ok ((w, _)) if status_line.chars().count() >= w as usize => {
let mut tmp = String::with_capacity (w as usize - 1);
for ch in status_line.chars().take (w as usize - 1) {tmp.push (ch)}
let _ = stdout.write (tmp.as_bytes());},
_ => {let _ = stdout.write (status_line.as_bytes());}};
delete_line (&mut stdout);
let _ = stdout.queue (cursor::MoveToColumn (0));
let _ = stdout.flush();}}}
#[cfg(feature = "crossterm")]
pub fn status_line_clear() {
use io::{stdout, Write};
if let Ok (mut status_line) = STATUS_LINE.lock() {
if *ISATTY && !status_line.is_empty() {
let mut stdout = stdout();
STATUS_LINE_LM.store (now_ms() as usize, Ordering::Relaxed);
status_line.clear();
delete_line (&mut stdout);
let _ = stdout.flush();}}}
#[cfg(feature = "crossterm")]
pub fn with_status_line (code: &dyn Fn()) {
use crossterm::{QueueableCommand, cursor};
use io::{stdout, Write};
if let Ok (status_line) = STATUS_LINE.lock() {
if !*ISATTY || status_line.is_empty() {
code()
} else {
let mut stdout = stdout();
delete_line (&mut stdout);
let _ = stdout.flush();
code();
let _ = stdout.write (status_line.as_bytes());
let _ = stdout.queue (cursor::MoveToColumn (0));
let _ = stdout.flush();}}}
#[cfg(feature = "crossterm")]
#[test] fn test_status_line() {
with_status_line (&|| println! ("hello world"));}
#[macro_export] macro_rules! gstring {($array: ident, $code: block) => {{
let end = {
let mut $array = ::std::io::Cursor::new (&mut $array[..]);
let $array = &mut $array;
$code;
$array.position() as usize};
unsafe {::std::str::from_utf8_unchecked (&$array[0..end])}}}}
pub fn netstring (at: &[u8]) -> Result<(&[u8], &[u8]), String> {
let length_end = match at.iter().position (|&ch| ch < b'0' || ch > b'9') {Some (l) if l > 0 => l, _ => return ERR! ("No len.")};
match at.get (length_end) {Some (&ch) if ch == b':' => (), _ => return ERR! ("No colon.")};
let length = unsafe {from_utf8_unchecked (&at[0 .. length_end])};
let length: usize = try_s! (length.parse());
let bulk_pos = 0 + length_end + 1;
let bulk_end = bulk_pos + length;
match at.get (bulk_end) {Some (&ch) if ch == b',' => (), _ => return ERR! ("No comma.")}
Ok ((&at[bulk_pos .. bulk_end], &at[bulk_end + 1 ..]))}
#[cfg(unix)]
pub fn with_hostname (visitor: &mut dyn FnMut (&[u8])) -> Result<(), std::io::Error> {
use libc::{size_t, gethostname};
use std::ffi::CStr;
let mut buf = [0; 128];
let rc = unsafe {gethostname (buf.as_mut_ptr(), (buf.len() - 1) as size_t)};
if rc == 0 {
let cs = unsafe {CStr::from_ptr (buf.as_ptr())};
Ok (visitor (cs.to_bytes()))
} else {
Err (io::Error::last_os_error())}}
#[cfg(unix)] #[test] fn test_hostname() {
let mut hostname = String::new();
with_hostname (&mut |bytes| hostname = String::from_utf8_lossy (bytes) .into_owned()) .unwrap();}
pub fn slurp (path: &dyn AsRef<Path>) -> Vec<u8> {
let mut file = match fs::File::open (path) {
Ok (f) => f,
Err (ref err) if err.kind() == io::ErrorKind::NotFound => return Vec::new(),
Err (err) => panic! ("Can't open {:?}: {}", path.as_ref(), err)};
let mut buf = Vec::new();
file.read_to_end (&mut buf) .expect ("!read");
buf}
pub fn slurp_prog (command: &str) -> Result<String, String> {
let output = match Command::new ("dash") .arg ("-c") .arg (command) .output() {
Ok (output) => output,
Err (ref err) if err.kind() == io::ErrorKind::NotFound => {
try_s! (Command::new ("sh") .arg ("-c") .arg (command) .output())},
Err (err) => return ERR! ("{}", err)};
let combined_output: String = if output.stderr.is_empty() {
try_s! (String::from_utf8 (output.stdout))
} else if output.stdout.is_empty() {
try_s! (String::from_utf8 (output.stderr))
} else {
let mut buf = String::with_capacity (output.stderr.len() + output.stdout.len());
buf.push_str (try_s! (std::str::from_utf8 (&output.stderr[..])));
buf.push_str (try_s! (std::str::from_utf8 (&output.stdout[..])));
buf};
if output.status.success() {Ok (combined_output)} else {Err (combined_output)}}
#[test] fn test_slurp_prog() {
let foo = match slurp_prog ("echo foo") {Ok (foo) => foo, Err (err) => panic! ("{}", err)};
assert_eq! (foo.trim(), "foo");}
pub fn cmd (cmd: &str) -> Result<(), String> {
println! ("$ {}", cmd);
let status = try_s! (Command::new ("dash") .arg ("-c") .arg (cmd) .stdout (Stdio::inherit()) .stderr (Stdio::inherit()) .status());
if !status.success() {Err (format! ("Command returned an error status: {}", status))} else {Ok(())}}
pub fn any_to_str<'a> (message: &'a dyn Any) -> Option<&'a str> {
if let Some (message) = message.downcast_ref::<&str>() {return Some (message)}
if let Some (message) = message.downcast_ref::<String>() {return Some (&message[..])}
return None}
pub fn duration_to_float (duration: Duration) -> f64 {
duration.as_secs() as f64 + ((duration.subsec_nanos() as f64) / 1000000000.0)}
pub fn ms2sec (ms: u64) -> f64 {
(ms / 1000) as f64 + ((ms % 1000) as f64 / 1000.0)}
pub fn now_float() -> f64 {
let now = SystemTime::now().duration_since (UNIX_EPOCH) .expect ("!duration_since");
duration_to_float (now)}
#[test] fn test_now_float() {
let now = SystemTime::now().duration_since (UNIX_EPOCH) .expect ("!duration_since") .as_secs();
let t1 = now_float();
assert_eq! (now, t1 as u64);
sleep (Duration::from_millis (100));
let t2 = now_float();
let delta = t2 - t1;
assert! (delta >= 0.098 && delta <= 0.150, "delta: {}", delta);}
pub fn duration_to_ms (duration: Duration) -> u64 {
duration.as_secs() * 1000 + (duration.subsec_nanos() / 1000000) as u64}
pub fn now_ms() -> u64 {
let now = SystemTime::now().duration_since (UNIX_EPOCH) .expect ("!duration_since");
duration_to_ms (now)}
#[test] fn test_now_ms() {
let t1 = now_ms();
sleep (Duration::from_millis (100));
let t2 = now_ms();
let delta = t2 - t1;
assert! (delta >= 98 && delta <= 150, "delta: {}", delta);}
pub fn last_modified_sec (path: &dyn AsRef<Path>) -> Result<f64, String> {
let meta = match path.as_ref().metadata() {
Ok (m) => m,
Err (ref err) if err.kind() == std::io::ErrorKind::NotFound => return Ok (0.),
Err (err) => return ERR! ("{}", err)};
let lm = try_s! (meta.modified());
let lm = duration_to_float (try_s! (lm.duration_since (UNIX_EPOCH)));
Ok (lm)}
#[cfg(all(feature = "nightly", feature = "rdtsc"))]
pub fn rdtsc() -> u64 {
unsafe {
let mut low: u32; let mut high: u32;
asm! ("rdtsc", lateout ("eax") low, lateout ("edx") high, options (nomem, nostack));
((high as u64) << 32) | (low as u64)}}
#[cfg(all(feature = "nightly", feature = "rdtsc"))]
#[test] fn test_rdtsc() {
assert! (rdtsc() != rdtsc())}
pub struct FileLock<'a> {
pub lock_path: &'a dyn AsRef<Path>,
pub ttl_sec: f64,
pub file: std::fs::File}
impl<'a> FileLock<'a> {
pub fn lock (lock_path: &'a dyn AsRef<Path>, ttl_sec: f64) -> Result<Option<FileLock<'a>>, String> {
let mut cycle = 0u8;
loop {
if cycle > 1 {break Ok (None)}
cycle += 1;
match std::fs::OpenOptions::new().write (true) .create_new (true) .open (lock_path.as_ref()) {
Ok (file) => break Ok (Some (FileLock {lock_path, ttl_sec, file})),
Err (ref ie) if ie.kind() == std::io::ErrorKind::AlreadyExists => {
let lm = match last_modified_sec (lock_path) {
Ok (lm) => lm,
Err (ie) => break ERR! ("Error checking {:?}: {}", lock_path.as_ref(), ie)};
if lm == 0. {continue}
if now_float() - lm > ttl_sec {
if let Err (err) = std::fs::remove_file (lock_path.as_ref()) {break ERR! ("Error removing {:?}: {}", lock_path.as_ref(), err)}
continue}
break Ok (None)},
Err (ie) => break ERR! ("Error creating {:?}: {}", lock_path.as_ref(), ie)}}}
#[cfg(target_os = "linux")]
pub fn touch (&self) -> Result<(), String> {
let ts = libc::timespec {tv_sec: 0, tv_nsec: libc::UTIME_NOW};
let times = [ts, ts];
use std::os::unix::io::AsRawFd;
let rc = unsafe {libc::futimens (self.file.as_raw_fd(), ×[0])};
if rc != 0 {
let err = std::io::Error::last_os_error();
return ERR! ("Can't touch {:?}: {}", self.lock_path.as_ref(), err)}
Ok(())}}
impl<'a> Drop for FileLock<'a> {
fn drop (&mut self) {
let _ = std::fs::remove_file (self.lock_path);}}
impl<'a> std::fmt::Debug for FileLock<'a> {
fn fmt (&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write! (f, "FileLock ({:?}, {})", self.lock_path.as_ref(), self.ttl_sec)}}
#[derive(Debug)]
pub struct ProcEn {
pub name: String,
pub path: std::path::PathBuf,
pub cmdline: Vec<String>}
impl ProcEn {
pub fn pid (&self) -> Option<u32> {
if let Some (file_name) = self.path.file_name() {
if let Some (file_name) = file_name.to_str() {
if let Ok (pid) = file_name.parse() {
return Some (pid)}}}
None}}
pub struct ProcIt {read_dir: std::fs::ReadDir}
impl ProcIt {
pub fn new() -> ProcIt {
ProcIt {
read_dir: match Path::new ("/proc") .read_dir() {Ok (it) => it, Err (err) => panic! ("!proc: {}", err)}}}}
impl Iterator for ProcIt {
type Item = ProcEn;
fn next (&mut self) -> Option<ProcEn> {
match self.read_dir.next() {
None => return None,
Some (Err (err)) => panic! ("ProcIt] !read_dir: {}", err),
Some (Ok (proc_en)) => {
let file_type = match proc_en.file_type() {Ok (ft) => ft, Err (err) => panic! ("!file_type: {}", err)};
if !file_type.is_dir() {return self.next()}
let name = proc_en.file_name();
let name = match name.to_str() {Some (name) => name, None => panic! ("ProcIt] !to_str")};
if !name.as_bytes().iter().all (|&b| b >= b'0' && b <= b'9') {
return self.next()}
let path = proc_en.path();
let cmdline = String::from_utf8 (slurp (&path.join ("cmdline"))) .expect ("!from_utf8");
if cmdline.is_empty() {return self.next()}
Some (ProcEn {name: name.into(), path: path, cmdline: cmdline.split ('\0') .map (String::from) .collect()})}}}}
pub mod oneshot {
use std::panic::AssertUnwindSafe;
use std::sync::{Arc, Condvar, Mutex};
pub struct Sender<T> (Arc<Mutex<Option<T>>>, Arc<AssertUnwindSafe<Condvar>>);
impl<T> Sender<T> {
pub fn send (self, v: T) {
{ let arc_mutex = self.0;
{ let lr = arc_mutex.lock();
if let Ok (mut lock) = lr {*lock = Some (v)} } }
self.1.notify_one()}}
pub struct Receiver<T> (Arc<Mutex<Option<T>>>, Arc<AssertUnwindSafe<Condvar>>);
impl<T> Receiver<T> {
pub fn recv (self) -> Result<T, String> {
let mut arc_mutex = self.0;
let arc_condvar = self.1;
loop {
match Arc::try_unwrap (arc_mutex) {
Ok (mutex) => {
if let Some (value) = try_s! (mutex.into_inner()) {return Ok (value)}
else {return ERR! ("recv] Sender gone without providing a value")}},
Err (am) => {
arc_mutex = am;
let locked_value = try_s! (arc_mutex.lock());
if locked_value.is_none() {let _locked_value = try_s! (arc_condvar.wait (locked_value));}}}}}}
pub fn oneshot<T>() -> (Sender<T>, Receiver<T>) {
let arc_mutex = Arc::new (Mutex::new (None));
let arc_condvar = Arc::new (AssertUnwindSafe (Condvar::new()));
(Sender (arc_mutex.clone(), arc_condvar.clone()), Receiver (arc_mutex, arc_condvar))}}
#[test] fn test_oneshot() {
let (tx, rx) = oneshot::oneshot();
std::thread::spawn (|| tx.send (42));
assert_eq! (42, rx.recv().expect("!recv"))}
pub fn binprint (bin: &[u8], blank: u8) -> String {
let mut bin: Vec<u8> = bin.into();
for ch in bin.iter_mut() {if *ch < 0x20 || *ch >= 0x7F {*ch = blank}}
unsafe {String::from_utf8_unchecked (bin)}}
pub struct Constructible<T> {
value: AtomicUsize,
_phantom: std::marker::PhantomData<T>}
impl<T> Default for Constructible<T> {
fn default() -> Constructible<T> {Constructible {
value: AtomicUsize::new (0),
_phantom: PhantomData}}}
impl<T> From<T> for Constructible<T> {
fn from (v: T) -> Constructible<T> {
let v = Box::new (v);
let v = Box::into_raw (v);
Constructible {
value: AtomicUsize::new (v as usize),
_phantom: PhantomData}}}
impl<T> From<Option<T>> for Constructible<T> {
fn from (v: Option<T>) -> Constructible<T> {
match v {
Some (v) => Constructible::from (v),
None => Constructible::default()}}}
impl<T, E> FromStr for Constructible<T> where T: FromStr<Err=E> {
type Err = E;
fn from_str (s: &str) -> Result<Self, Self::Err> {
let v: T = s.parse()?;
Ok (Self::from (v))}}
#[test] fn test_parse() {
use std::num::ParseIntError;
let rc: Result<Constructible<u32>, ParseIntError> = "-1".parse();
assert! (rc.is_err());
let c: Constructible<i32> = "123".parse().unwrap();
assert_eq! (c.copy_or (0), 123);}
impl<T> Constructible<T> {
#[cfg(feature = "nightly")]
pub const fn new() -> Constructible<T> {
Constructible {
value: AtomicUsize::new (0),
_phantom: PhantomData}}
pub fn initialize<'a> (&'a self, v: Box<T>) -> Result<&'a T, String> {
let v = Box::into_raw (v);
if let Err (_) = self.value.compare_exchange (0, v as usize, Ordering::Relaxed, Ordering::Relaxed) {
unsafe {Box::from_raw (v)};
return ERR! ("Cell already initialized")}
Ok (unsafe {&*v})}
pub fn pin<'a> (&'a self, v: T) -> Result<&'a T, String> {self.initialize (Box::new (v))}
pub fn or<'a, F> (&'a self, default: &'a F) -> &'a T where F: Fn() -> &'a T, T: 'a {
let v = self.value.load (Ordering::Relaxed);
if v != 0 {
let v = v as *const T;
unsafe {&*v}
} else {
default()}}
pub fn copy_or (&self, default: T) -> T where T: Copy {
let v = self.value.load (Ordering::Relaxed);
if v != 0 {
let v = v as *const T;
unsafe {*v}
} else {
default}}
pub fn clone_or (&self, default: T) -> T where T: Clone {
let v = self.value.load (Ordering::Relaxed);
if v != 0 {
let v = v as *const T;
unsafe {(*v).clone()}
} else {
default}}
pub fn ok_or<'a, E> (&'a self, error: E) -> Result<&'a T, E> {
let v = self.value.load (Ordering::Relaxed);
if v != 0 {
let v = v as *const T;
Ok (unsafe {&*v})
} else {
Err (error)}}
pub fn as_option<'a> (&'a self) -> Option<&'a T> {
let v = self.value.load (Ordering::Relaxed);
if v != 0 {
let v = v as *const T;
Some (unsafe {&*v})
} else {
None}}
pub fn is_none (&self) -> bool {
self.value.load (Ordering::Relaxed) == 0}
pub fn is_some (&self) -> bool {
self.value.load (Ordering::Relaxed) != 0}
pub fn iter<'a> (&'a self) -> std::option::IntoIter<&'a T> {
self.as_option().into_iter()}}
#[cfg(feature = "nightly")]
#[test] fn test_const() {
static V: Constructible<i32> = Constructible::new();
assert_eq! (V.copy_or (-1), -1);
V.pin (11) .unwrap();
assert_eq! (V.copy_or (-1), 11);
assert! (V.pin (22) .is_err());
assert_eq! (V.copy_or (-1), 11)}
impl<'a, T> IntoIterator for &'a Constructible<T> {
type Item = &'a T;
type IntoIter = std::option::IntoIter<&'a T>;
fn into_iter (self) -> Self::IntoIter {
self.as_option().into_iter()}}
impl<T> fmt::Debug for Constructible<T> where T: fmt::Debug {
fn fmt (&self, ft: &mut fmt::Formatter) -> fmt::Result {
write! (ft, "{:?}", self.as_option())}}
impl<T> fmt::Display for Constructible<T> where T: fmt::Display {
fn fmt (&self, ft: &mut fmt::Formatter) -> fmt::Result {
match self.as_option() {
Some (v) => write! (ft, "{}", v),
None => write! (ft, "-")}}}
impl<T> Drop for Constructible<T> {
fn drop (&mut self) {
let v = self.value.load (Ordering::Relaxed);
if v == 0 {return}
if self.value.compare_exchange (v, 0, Ordering::Relaxed, Ordering::Relaxed) .is_err() {return}
unsafe {Box::from_raw (v as *mut T)};}}