You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
570 lines
13 KiB
570 lines
13 KiB
use super::*;
|
|
use std::iter::FusedIterator;
|
|
use std::collections::BTreeSet;
|
|
use std::borrow::Borrow;
|
|
use futures::prelude::*;
|
|
|
|
use std::cell::RefCell;
|
|
use std::ptr;
|
|
|
|
/// General utilities
|
|
pub mod util;
|
|
|
|
/// Functions for manipulating byte slices
|
|
pub mod bytes;
|
|
|
|
// Internal modules
|
|
mod iters;
|
|
pub use iters::*;
|
|
|
|
mod streams;
|
|
pub use streams::*;
|
|
|
|
mod hashers;
|
|
pub use hashers::*;
|
|
|
|
mod hex;
|
|
pub use hex::*;
|
|
|
|
mod lag;
|
|
pub use lag::*;
|
|
|
|
mod defer_drop;
|
|
pub use defer_drop::*;
|
|
|
|
pub mod sync;
|
|
|
|
pub mod plex;
|
|
pub use plex::MultiplexStreamExt;
|
|
|
|
// The extension traits are defined in this `mod.rs` file, no need to re-export anything from here.
|
|
pub mod chunking;
|
|
|
|
/// How many elements should `precollect` allocate on the stack before spilling to the heap.
|
|
pub const PRECOLLECT_STACK_SIZE: usize = 64;
|
|
|
|
/// Implement `Default` for a type.
|
|
#[macro_export] macro_rules! default {
|
|
($ty:ty: $ex:expr) => {
|
|
|
|
impl Default for $ty
|
|
{
|
|
#[inline]
|
|
fn default() -> Self
|
|
{
|
|
$ex
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/// Create a duration with time suffix `h`, `m`, `s`, `ms` or `ns`.
|
|
///
|
|
/// # Combination
|
|
/// These can also be combined.
|
|
/// ```
|
|
/// # use flan_utils::duration;
|
|
/// duration!(1 h, 20 m, 30 s);
|
|
/// ```
|
|
#[macro_export] macro_rules! duration
|
|
{
|
|
(0 $($_any:tt)?) => (::core::time::Duration::from_secs(0));
|
|
($dur:literal ms) => (::core::time::Duration::from_millis($dur));
|
|
($dur:literal ns) => (::core::time::Duration::from_nanos($dur));
|
|
($dur:literal s) => (::core::time::Duration::from_secs($dur));
|
|
($dur:literal m) => (::core::time::Duration::from_secs($dur * 60));
|
|
($dur:literal h) => (::core::time::Duration::from_secs($dur * 60 * 60));
|
|
|
|
( $($dur:literal $unit:tt),*)=> {
|
|
duration!(0 s) $(
|
|
+ duration!($dur $unit)
|
|
)*
|
|
};
|
|
}
|
|
|
|
|
|
/// Create a basic, C-like enum
|
|
#[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<bool> 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<T>(self, value: T) -> Option<T>
|
|
{
|
|
self.and_then(move || value)
|
|
}
|
|
|
|
/// Map this value
|
|
#[inline] pub fn map<F, T>(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<F, T>(self, f: F) -> Option<T>
|
|
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<F, T>(self, f: F) -> Option<T>
|
|
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<T>(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<F, G, T>(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);
|
|
}
|
|
}
|
|
|
|
|
|
/// 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
|
|
}
|
|
};
|
|
}
|
|
|
|
|
|
/// Collect an iterator's output and then drop it to detach the iterator from any references or resources it might have.
|
|
#[macro_export] macro_rules! precollect {
|
|
($iter:expr, $num:literal) => {
|
|
{
|
|
{
|
|
let it: ::smallvec::SmallVec<[_; $num]> = $iter.into_iter().collect();
|
|
it.into_iter()
|
|
}
|
|
}
|
|
};
|
|
($iter:expr) => {
|
|
{
|
|
{
|
|
let it: ::smallvec::SmallVec<[_; $crate::ext::PRECOLLECT_STACK_SIZE]> = $iter.into_iter().collect();
|
|
it.into_iter()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[macro_export] macro_rules! no_op {
|
|
($expr:expr) => {
|
|
{
|
|
$expr;
|
|
}
|
|
};
|
|
}
|
|
|
|
/// The ID type used for backing ID types;
|
|
pub type GenericID = uuid::Uuid;
|
|
|
|
/// Create a type that contains a (globally) unique ID.
|
|
#[macro_export] macro_rules! id_type {
|
|
($name:ident $(: $doc:literal)?) => ($crate::id_type!{pub(self) $name $(: $doc)?});
|
|
($vis:vis $name:ident $(: $doc:literal)?) => {
|
|
$(#[doc=$doc])?
|
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
|
|
$vis struct $name($crate::ext::GenericID);
|
|
|
|
impl $name
|
|
{
|
|
/// Create a new unique ID.
|
|
#[inline(always)] fn id_new() -> Self
|
|
{
|
|
Self($crate::ext::GenericID::new_v4())
|
|
}
|
|
|
|
/// The generic ID type backing this one
|
|
#[inline(always)] fn id_generic(&self) -> &$crate::ext::GenericID
|
|
{
|
|
&self.0
|
|
}
|
|
|
|
/// Consume into the generic ID
|
|
#[inline(always)] fn id_into_generic(self) -> $crate::ext::GenericID
|
|
{
|
|
self.0
|
|
}
|
|
|
|
/// Create from a generic ID
|
|
#[inline(always)] fn id_from_generic(gen: $crate::ext::GenericID) -> Self
|
|
{
|
|
Self(gen)
|
|
}
|
|
}
|
|
|
|
impl ::std::fmt::Display for $name
|
|
{
|
|
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result
|
|
{
|
|
//use ::std::fmt::Write;
|
|
f.write_str(concat!(stringify!($name),"<"))?;
|
|
self.0.fmt(f)?;
|
|
f.write_str(">")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Expands to `unreachable_unchecked` in non-debug builds.
|
|
///
|
|
/// # Safety
|
|
/// You must make 100% sure this code path will never be entered, or it will cause undefined behaviour in release builds.
|
|
#[macro_export] macro_rules! debug_unreachable {
|
|
() => {
|
|
if cfg!(debug_assertions) {
|
|
#[cold] unreachable!()
|
|
} else {
|
|
::std::hint::unreachable_unchecked()
|
|
}
|
|
};
|
|
}
|
|
|
|
|
|
/// Dirty debugging macro to get the compiler to print an error message telling you the size of a type.
|
|
/// ```
|
|
/// check_size!((u8, u8)); // Expected ... found one with *2* elements
|
|
/// ```
|
|
/// # Size assertions
|
|
/// Can also be used to statically assert the size of a type
|
|
/// ```
|
|
/// # use datse::ext::check_size;
|
|
/// check_size!(u16 where == 2; "u16 should be 2 bytes");
|
|
/// check_size!(u16 where < 3; "u16 should be lower than 3 bytes");
|
|
/// check_size!(u16 where > 1; "u16 should be larger that 1 byte");
|
|
/// check_size!(u16 where != 0; "u16 should be larger that 0 bytes");
|
|
/// ```
|
|
///
|
|
/// You can also combine multiple
|
|
/// ```
|
|
/// # use datse::ext::check_size;
|
|
/// check_size!([u8; 512] where {
|
|
/// != 10;
|
|
/// > 511;
|
|
/// < 513;
|
|
/// == 512
|
|
/// });
|
|
/// ```
|
|
///
|
|
/// This can be used to give you prompts as to when you might want to consider boxing a type.
|
|
#[macro_export] macro_rules! check_size {
|
|
($t:ty) => {
|
|
const _: [(); 0] = [(); ::std::mem::size_of::<$t>()];
|
|
};
|
|
|
|
($t:ty where == $n:literal $(; $msg:literal)?) => {
|
|
const _: [(); $n] = [(); ::std::mem::size_of::<$t>()];
|
|
};
|
|
|
|
($t:ty where {$($op:tt $n:literal);+} $(; $msg:literal)?) => {
|
|
$(
|
|
$crate::static_assert!(::std::mem::size_of::<$t>() $op $n);
|
|
)+
|
|
};
|
|
|
|
($t:ty where $op:tt $n:literal $(; $msg:literal)?) => {
|
|
$crate::static_assert!(::std::mem::size_of::<$t>() $op $n $(; $msg)?);
|
|
};
|
|
}
|
|
|
|
check_size!(u8 where == 1);
|
|
check_size!(u16 where > 1);
|
|
check_size!([u8; 512] where <= 512);
|
|
check_size!([u8; 512] where {
|
|
!= 10;
|
|
> 511;
|
|
< 513;
|
|
== 512
|
|
});
|
|
|
|
|
|
/// Assert the output of a constant boolean expression is `true` at compile time.
|
|
#[macro_export] macro_rules! static_assert {
|
|
($val:expr $(; $msg:literal)?) => {
|
|
const _: [(); 1] = [(); ($val as bool) as usize];
|
|
}
|
|
}
|
|
|
|
/// Assert a trait is object safe. This will produce a compiler error if the trait is not object safe
|
|
#[macro_export] macro_rules! assert_object_safe {
|
|
($trait:path $(; $msg:literal)?) => {
|
|
const _:() = {
|
|
#[cold] fn __assert_object_safe() -> !
|
|
{
|
|
let _: &dyn $trait;
|
|
unsafe {
|
|
debug_unreachable!()
|
|
}
|
|
}
|
|
};
|
|
}
|
|
}
|
|
|
|
assert_object_safe!(AsRef<str>; "object safety assertion test");
|
|
static_assert!(1+1==2; "static assertion test");
|
|
|
|
/// Execute this expression if running in debug mode.
|
|
#[macro_export] macro_rules! debug_expr {
|
|
(if $expr:expr; else $or:expr) => {
|
|
if cfg!(debug_assertions) { $expr }
|
|
else { $or }
|
|
};
|
|
(else $expr:expr) => {
|
|
if !cfg!(debug_assertions) { $expr }
|
|
};
|
|
($expr:expr) => {
|
|
if cfg!(debug_assertions) { $expr }
|
|
};
|
|
}
|
|
|
|
/// Run each expression in sequence then return the result of the first one.
|
|
///
|
|
/// # Example
|
|
/// ```
|
|
/// # use mtfse::prog1;
|
|
/// assert_eq!(prog1 {
|
|
/// 1 + 2;
|
|
/// println!("Done thing");
|
|
/// 4 + 5;
|
|
/// }, 3);
|
|
/// ```
|
|
#[macro_export] macro_rules! prog1 {
|
|
($first:expr; $($rest:expr);* $(;)?) => {
|
|
($first, $( $rest ),*).0
|
|
}
|
|
}
|
|
|
|
/// Helper to create an `std::io::Error` structure.
|
|
///
|
|
/// # Example
|
|
/// ```
|
|
/// # use mtfse::io_err;
|
|
/// let err = io_err!(Other, "some error");
|
|
/// ```
|
|
#[macro_export] macro_rules! io_err {
|
|
($kind:ident, $msg:expr) => {
|
|
::std::io::Error::new(::std::io::ErrorKind::$kind, $msg)
|
|
};
|
|
($msg:expr) => (io_err!(Other, $msg));
|
|
}
|
|
|
|
|
|
|
|
pub trait UnwrapInfallible<T>
|
|
{
|
|
fn unwrap_infallible(self) -> T;
|
|
}
|
|
|
|
impl<T> UnwrapInfallible<T> for Result<T, std::convert::Infallible>
|
|
{
|
|
/// Unwrap with 0 overhead for values that cannot possibly be `Err`.
|
|
#[inline(always)] fn unwrap_infallible(self) -> T {
|
|
match self {
|
|
Ok(v) => v,
|
|
Err(_) => unsafe { debug_unreachable!() },
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<T> UnwrapInfallible<T> for Result<T, !>
|
|
{
|
|
/// Unwrap with 0 overhead for values that cannot possibly be `Err`.
|
|
#[inline(always)] fn unwrap_infallible(self) -> T {
|
|
match self {
|
|
Ok(v) => v,
|
|
Err(_) => unsafe { debug_unreachable!() },
|
|
}
|
|
}
|
|
}
|
|
|
|
pub trait UnwrapErrInfallible<T>
|
|
{
|
|
fn unwrap_err_infallible(self) -> T;
|
|
}
|
|
|
|
impl<T> UnwrapErrInfallible<T> for Result<std::convert::Infallible, T>
|
|
{
|
|
/// Unwrap with 0 overhead for values that cannot possibly be `Ok`.
|
|
#[inline(always)] fn unwrap_err_infallible(self) -> T {
|
|
match self {
|
|
Err(v) => v,
|
|
Ok(_) => unsafe { debug_unreachable!() },
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<T> UnwrapErrInfallible<T> for Result<!, T>
|
|
{
|
|
/// Unwrap with 0 overhead for values that cannot possibly be `Ok`.
|
|
#[inline(always)] fn unwrap_err_infallible(self) -> T {
|
|
match self {
|
|
Err(v) => v,
|
|
Ok(_) => unsafe { debug_unreachable!() },
|
|
}
|
|
}
|
|
}
|
|
|
|
pub trait ChunkStreamExt<T>: Sized
|
|
{
|
|
fn chunk_into<I: From<Vec<T>>>(self, sz: usize) -> chunking::ChunkingStream<Self,T,I>;
|
|
fn chunk(self, sz: usize) -> chunking::ChunkingStream<Self, T>
|
|
{
|
|
self.chunk_into(sz)
|
|
}
|
|
}
|
|
|
|
impl<S, T> ChunkStreamExt<T> for S
|
|
where S: Stream<Item=T>
|
|
{
|
|
fn chunk_into<I: From<Vec<T>>>(self, sz: usize) -> chunking::ChunkingStream<Self,T,I>
|
|
{
|
|
chunking::ChunkingStream::new(self, sz)
|
|
}
|
|
}
|
|
|
|
|
|
pub trait ChunkIterExt<T>: Sized
|
|
{
|
|
fn chunk_into<I: From<Vec<T>>>(self, sz: usize) -> iters::ChunkingIter<Self,T,I>;
|
|
fn chunk(self, sz: usize) -> iters::ChunkingIter<Self, T>
|
|
{
|
|
self.chunk_into(sz)
|
|
}
|
|
}
|
|
|
|
impl<S, T> ChunkIterExt<T> for S
|
|
where S: Iterator<Item=T>
|
|
{
|
|
fn chunk_into<I: From<Vec<T>>>(self, sz: usize) -> iters::ChunkingIter<Self,T,I>
|
|
{
|
|
iters::ChunkingIter::new(self, sz)
|
|
}
|
|
}
|