//! Extensions and macros use std::cell::RefCell; use std::ptr; #[macro_export] macro_rules! basic_enum { ($(#[$meta:meta])* $vis:vis $name:ident $(; $tcomment:literal)?: $($var:ident $(=> $comment:literal)?),+ $(,)?) => { $(#[$meta])* #[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Copy)] $(#[doc = $tcomment])? $vis enum $name { $( $(#[doc = $comment])? $var ),+ } } } /// Create a `Yes` or `No` enum. #[macro_export] macro_rules! bool_type { ($vis:vis $name:ident $(; $comment:literal)? => $yes:ident, $no:ident) => { basic_enum!(#[repr(u8)] $vis $name $(; $comment)?: $yes => "# First variant\n\nYes/true", $no => "# Second variant\n\nNo/false"); impl From for $name { #[inline] fn from(from: bool) -> Self { if from { Self::$yes } else { Self::$no } } } impl From<$name> for bool { #[inline] fn from(from: $name) -> Self { match from { $name::$yes => true, $name::$no => false, } } } impl $name { /// Create from a bool value. #[inline] pub const fn new(from: bool) -> Self { if from { Self::$yes } else { Self::$no } } /// Is this false? #[inline] pub const fn is_no(self) -> bool { !self.is_yes() } /// Is this true? #[inline] pub const fn is_yes(self) -> bool { match self { Self::$yes => true, Self::$no => false, } } /// Return Some(T) if self is true. #[inline] pub fn some(self, value: T) -> Option { self.and_then(move || value) } /// Map this value #[inline] pub fn map(self, f: F) -> T where F: FnOnce(bool) -> T { f(self.is_yes()) } /// Run this closure if value is false #[inline] pub fn or_else(self, f: F) -> Option where F: FnOnce() -> T { if let Self::$no = self { Some(f()) } else { None } } /// Run this closure if value is true #[inline] pub fn and_then(self, f: F) -> Option where F: FnOnce() -> T { if let Self::$yes = self { Some(f()) } else { None } } /// Return `yes` if true and `no` if false #[inline] pub fn either(self, yes: T, no: T) -> T { self.and_either(move || yes, move || no) } /// Run closure `yes` if value is true, `no` if value is false. #[inline] pub fn and_either(self, yes: F, no: G) -> T where F: FnOnce() -> T, G: FnOnce() -> T, { match self { Self::$yes => yes(), Self::$no => no(), } } } }; ($vis:vis $name:ident $(; $comment:literal)?) => { $crate::bool_type!($vis $name $(; $comment)? => Yes, No); } } /// Max size of bytes we'll allocate to the stack at runtime before using a heap allocated buffer. pub const STACK_SIZE_LIMIT: usize = 4096; /// Allocate `size` bytes. Allocates on the stack if size is lower than `STACK_SIZE_LIMIT`, otherwise allocates on the heap. pub fn alloca_limit(size: usize, f: F) -> T where F: FnOnce(&mut [u8]) -> T { if size > STACK_SIZE_LIMIT { thread_local! { static BUFFER: RefCell> = RefCell::new(vec![0u8; STACK_SIZE_LIMIT*2]); } BUFFER.with(move |buf| { // If the borrow fails then `f` has recursively called into this function, so for that we allocate a new buffer instead of reusing this static one. if let Ok(mut buf) = buf.try_borrow_mut() { if buf.len() < size { buf.resize(size, 0); } let res = f(&mut buf[..size]); bytes::blank(&mut buf[..size]); res } else { f(&mut vec![0u8; size]) } }) } else { stackalloc::alloca_zeroed(size, f) // I don't think this is okay to do. //stackalloc::alloca(size, move |buf| f(unsafe { stackalloc::helpers::slice_assume_init_mut(buf) })) } } /// Create an accessor method. for a field in a structure. /// /// The supported accessor types are: `ref`, `mut`, and `move`. #[macro_export] macro_rules! accessor { ($vis:vis ref $name:ident -> $ty:ty => $internal:ident $(; $comment:literal)?) => { $(#[doc=$comment])? #[inline] $vis fn $name(&self) -> &$ty { &self.$internal } }; ($vis:vis ref $name:ident -> $ty:ty => $internal:tt $(; $comment:literal)?) => { $(#[doc=$comment])? #[inline] $vis fn $name(&self) -> &$ty { &self.$internal } }; ($vis:vis mut $name:ident -> $ty:ty => $internal:ident $(; $comment:literal)?) => { $(#[doc=$comment])? #[inline] $vis fn $name(&self) -> &mut $ty { &mut self.$internal } }; ($vis:vis mut $name:ident -> $ty:ty => $internal:tt $(; $comment:literal)?) => { $(#[doc=$comment])? #[inline] $vis fn $name(&self) -> &mut $ty { &mut self.$internal } }; ($vis:vis move $name:ident -> $ty:ty => $internal:ident $(; $comment:literal)?) => { $(#[doc=$comment])? #[inline] $vis fn $name(&self) -> $ty { self.$internal } }; ($vis:vis move $name:ident -> $ty:ty => $internal:tt $(; $comment:literal)?) => { $(#[doc=$comment])? #[inline] $vis fn $name(&self) -> $ty { self.$internal } }; } #[macro_export] macro_rules! lazy_format { ($msg:literal $($tt:tt)*) => { { use ::std::fmt::{self, Write, Formatter}; use ::std::sync::Mutex; use ::std::io; let pfn = move |fmt| { write!(fmt, $msg $($tt)*)?; let mut sfmt = String::new(); write!(&mut sfmt, $msg $($tt)*)?; Ok(sfmt) }; enum LazyFormatInner { //todo: redo this entire thing Pending(F), Complete(String), Error(fmt::Error), Panicked, } struct LazyFormat(Mutex>); impl) -> io::Result> fmt::Display for LazyFormat { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { //todo: redo this entire thing /* let mut inner = self.0.lock().unwrap(); let this = std::mem::replace(inner, LazyFormatInner::Panicked); //TODO: impl fmt::Write wrapper that multi-writes to 2 outputs let string = match this { LazyFormatInner::Pending(func) => func(f), LazyFormatInner::Complete(string) => write!(f, "{}", string).map(move |_| string), LazyFormatInner::Error(err) => return Err(err), LazyFormatInner::Panicked => panic!(), }; match string { Err(err) => { *inner = LazyFormatInner::Error(err), }, }*/ } } } } } #[cfg(not(feature="log"))] #[macro_export] macro_rules! trace { ($fmt:literal $($tt:tt)*) => { { ((), $($tt)*); } } } pub mod bytes { use super::*; /// `bzero` this slice pub fn blank(slice: &mut [u8]) { unsafe { ptr::write_bytes(slice.as_mut_ptr(), 0, slice.len()); } } } mod slice; pub use slice::*;