wyz-0.5.1/.cargo_vcs_info.json0000644000000001360000000000100116700ustar { "git": { "sha1": "83f076970d745dc1706b46126d5700aa072af031" }, "path_in_vcs": "" }wyz-0.5.1/Cargo.toml0000644000000022000000000000100076600ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2018" name = "wyz" version = "0.5.1" authors = ["myrrlyn "] include = [ "Cargo.toml", "LICENSE.txt", "README.md", "src/**/*.rs", ] description = "myrrlyn’s utility collection" homepage = "https://myrrlyn.net/crates/wyz" documentation = "https://docs.rs/wyz" readme = "README.md" keywords = [] categories = ["no-std"] license = "MIT" repository = "https://github.com/myrrlyn/wyz" [dependencies.once_cell] version = "1" optional = true [dependencies.tap] version = "1" [dependencies.typemap] version = "0.3" optional = true [features] alloc = [] default = ["std"] garbage = [ "once_cell", "typemap", ] std = ["alloc"] wyz-0.5.1/Cargo.toml.orig000075500000000000000000000022541046102023000133550ustar 00000000000000################################################################################ # Project Manifest # # # # This file describes the Rust project to the Cargo build tool for operations. # ################################################################################ [package] name = "wyz" version = "0.5.1" authors = [ "myrrlyn ", ] edition = "2018" categories = [ "no-std", ] description = "myrrlyn’s utility collection" documentation = "https://docs.rs/wyz" homepage = "https://myrrlyn.net/crates/wyz" include = [ "Cargo.toml", "LICENSE.txt", "README.md", "src/**/*.rs", ] keywords = [ ] license = "MIT" readme = "README.md" repository = "https://github.com/myrrlyn/wyz" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] [dependencies.once_cell] version = "1" optional = true [dependencies.tap] version = "1" [dependencies.typemap] version = "0.3" optional = true [features] alloc = [] garbage = [ "once_cell", "typemap", ] default = ["std"] std = ["alloc"] wyz-0.5.1/LICENSE.txt000064400000000000000000000020721046102023000123040ustar 00000000000000MIT License Copyright (c) 2018 myrrlyn (Alexander Payne) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. wyz-0.5.1/README.md000064400000000000000000000152051046102023000117420ustar 00000000000000
# `wyz` [![Crate][crate_img]][crate] [![Documentation][docs_img]][docs] [![License][license_img]][license_file] [![Continuous Integration][travis_img]][travis] [![Code Coverage][codecov_img]][codecov] [![Crate Downloads][downloads_img]][crate] [![Crate Size][loc_img]][loc]
I have developed a collection of utility and convenience Rust modules that are useful to me, and may be useful to you also. This crate is a collection of largely-independent small modules. I do not currently offer features to disable modules independently of each other, but their compilation cost is small enough to essentially not matter. ## Modules 1. [`conv`](#conv) 1. [`exit`](#exit) 1. [`fmt`](#fmt) 1. [`pipe`](#pipe) 1. [`tap`](#tap) ## `conv` This module provides a single trait, of the same name, with a single generic method, also of the same name. This trait is a sibling to `Into`, but rather than placing its type parameter in the trait (`Into::::into`), `Conv` places it in the method: `Conv::conv::`. By placing the type parameter in the method name, `.conv` can be called in suffix position in expressions where the result type cannot be inferred and must be explicitly stated. ```rust use wyz::conv::Conv; let digits = 0xabcd.conv::().len(); ``` This is a trivial example, but writing a code context where `.conv` makes sense takes a lot more context than a `README` wants. ## `exit` This is a macro that calls `std::process::exit`. It can return a status code, and also print a message to `stderr`. ```rust use wyz::exit::exit; exit!(); exit!(2); exit!(3, "This is a {} message", "failure"); ``` The default call is `std::process::exit(1)`; a call may provide its own exit code and, in addition, a set of arguments to pass directly to `eprintln!`. The error message is not guaranteed to be emitted, as `stderr` may be closed at time of `exit!`. ## `fmt` Rust uses the `Debug` trait for automatic printing events in several parts of the standard library. This module provides wrapper types which forward their `Debug` implementation to a specified other formatting trait. It also implements extension methods on all types that have format trait implementations to wrap them in the corresponding shim type. ```rust use wyz::fmt::FmtForward as _; let val = 6; let addr = &val as *const i32; println!("{:?}", addr.fmt_pointer()); ``` This snippet uses the `Debug` format template, but will print the `Pointer` implementation of `*const i32`. This is useful for fitting your values into an error-handling framework that only uses `Debug`, such as the `fn main() -> Result` program layout. ## `pipe` Rust does not permit universal suffix-position function call syntax. That is, you can *always* call a function with `Scope::name(arguments…)`, but only *some* functions can be called as `first_arg.name(other_args…)`. Working in “data pipelines” – flows where the return value of one function is passed directly as the first argument to the next – is common enough in our field that it has a name in languages that support it: *method chaining*. A *method* is a function that the language considers to be treated specially in regards to only its first argument, and permits changing the abstract token series `function arg1 args…` into the series `arg1 function args…`. Rust restricts that order transformation to only functions defined in scope for some type (either `impl Type` or `impl Trait for Type` blocks) and that take a first argument named `self`. Other languages permit calling *any* function, regardless of its definition site in source code, in this manner, as long as the first argument is of the correct type for the first parameter of the function. In languages like F♯ and Elixir, this uses the call operator `|>` rather than the C++ family’s `.` caller. This operator is pronounced `pipe`. Rust does not have a pipe operator. The dot-caller is restricted to only the implementation blocks listed above, and this is not likely to change because it also performs limited type transformation operations in order to find a name that fits. This module provides a `Pipe` trait whose method, `pipe`, passes its `self` first argument as the argument to its second-order function: ```rust use wyz::pipe::Pipe; let final = 5 .pipe(|a| a + 10) .pipe(|a| a * 2); assert_eq!(final, 30); ``` Without language-level syntax support, piping into closures always requires restating the argument, and functions cannot curry the argument they receive from `pipe` and arguments from the environment in the manner that dot-called methods can. ```rust fn fma(a: i32, b: i32, c: i32) -> i32 { (a * b) + c } 5.pipe(|a| fma(a, 2, 3)); let fma_2_3 = |a| fma(a, 2, 3); 5.pipe(fma_2_3); ``` These are the only ways to express `5 |> fma(2, 3)`. Sorry. Bug the language team. ## `tap` Tapping is a cousin operation to piping, except that rather than pass the receiver by *value* into some function, and return the result of that function, it passes a *borrow* of a value into a function, and then returns the original value. It is useful for inserting an operation into an expression without changing the overall state (type or value) of the expression. ```rust use wyz::tap::Tap; let result = complex_value() .tap(|v| log::info!("First stage: {}", v)) .transform(other, args) .tap(|v| log::info!("Second stage: {}", v)); ``` The tap calls have no effect on the expression into which they are placed, except to induce side effects somewhere else in the system. Commenting out the two `.tap` calls does not change anything about `complex_value()`, `.transform`, or `result`; it only removes the log statements. This enables easily inserting or removing inspection points in an expression without otherwise altering it. [codecov]: https://codecov.io/gh/myrrlyn/wyz "Code Coverage" [codecov_img]: https://img.shields.io/codecov/c/github/myrrlyn/wyz.svg?logo=codecov "Code Coverage Display" [crate]: https://crates.io/crates/wyz "Crate Link" [crate_img]: https://img.shields.io/crates/v/wyz.svg?logo=rust "Crate Page" [docs]: https://docs.rs/wyz "Documentation" [docs_img]: https://docs.rs/wyz/badge.svg "Documentation Display" [downloads_img]: https://img.shields.io/crates/dv/wyz.svg?logo=rust "Crate Downloads" [license_file]: https://github.com/myrrlyn/wyz/blob/master/LICENSE.txt "License File" [license_img]: https://img.shields.io/crates/l/wyz.svg "License Display" [loc]: https://github.com/myrrlyn/wyz "Repository" [loc_img]: https://tokei.rs/b1/github/myrrlyn/wyz?category=code "Repository Size" [travis]: https://travis-ci.org/myrrlyn/wyz "Travis CI" [travis_img]: https://img.shields.io/travis/myrrlyn/wyz.svg?logo=travis "Travis CI Display" wyz-0.5.1/src/bidi.rs000064400000000000000000000127101046102023000125250ustar 00000000000000//! A bidirectional iterator that only checks its direction once. use core::iter::FusedIterator; /** An iterator that conditionally reverses itself upon creation. This acts as a conditional `.rev()` adapter: it reverses the direction of iteration, swapping `.next()` and `.next_back()`, but only if the provided condition is true. If the condition is false, then iteration proceeds normally. The condition is only evaluated when the adapter is constructed, and all calls to drive the iterator are branchless. ## Usage This can be constructed directly with `Bidi::new(some_iterator)`, but it is more conveniently accessed as an extension method on double-ended iterators. Import `wyz::BidiIterator` or `wyz::bidi::*;` and then call `.bidi()` in your iterator adapter sequence. ## Examples This can be used to hand-roll a `memmove` implementation that correctly handles the case where the destination begins in the source region: ```rust use wyz::bidi::*; unsafe fn memmove(from: *const T, to: *mut T, count: usize) { let src = from .. from.add(count); let rev = src.contains(&(to as *const T)); for idx in (0 .. count).bidi(rev) { to.add(idx).write(from.add(idx).read()); } } ``` This example detects if `to` is between `from` and `from.add(count)` and uses that to determine whether to iterate forward from `0` to `count - 1` or backward from `count - 1` to `0`. **/ pub struct Bidi where I: DoubleEndedIterator { /// The iterator being governed. inner: I, /// A pointer to either `I::next` or `I::next_back`. next: fn(&mut I) -> Option<::Item>, /// A pointer to either `I::next_back` or `I::next`. next_back: fn(&mut I) -> Option<::Item>, /// A pointer to either `I::nth` or `I::nth_back`. nth: fn(&mut I, usize) -> Option<::Item>, /// A pointer to either `I::nth_back` or `I::nth`. nth_back: fn(&mut I, usize) -> Option<::Item>, } impl Bidi where I: DoubleEndedIterator { /// Applies the `Bidi` adapter to a double-ended iterator and selects the /// direction of traversal. /// /// ## Parameters /// /// - `iter`: anything that can be made into a double-ended iterator /// - `cond`: determines whether iteration proceeds ordinarily or reversed pub fn new(iter: II, cond: bool) -> Self where II: IntoIterator { let inner = iter.into_iter(); if cond { Self { inner, next: ::next_back, next_back: ::next, nth: ::nth_back, nth_back: ::nth, } } else { Self { inner, next: ::next, next_back: ::next_back, nth: ::nth, nth_back: ::nth_back, } } } } impl Iterator for Bidi where I: DoubleEndedIterator { type Item = ::Item; #[inline] fn next(&mut self) -> Option { (&mut self.next)(&mut self.inner) } #[inline] fn nth(&mut self, n: usize) -> Option { (&mut self.nth)(&mut self.inner, n) } #[inline] #[cfg(not(tarpaulin_include))] fn size_hint(&self) -> (usize, Option) { self.inner.size_hint() } #[inline] #[cfg(not(tarpaulin_include))] fn count(self) -> usize { self.inner.count() } #[inline] #[cfg(not(tarpaulin_include))] fn last(mut self) -> Option { self.next_back() } } impl DoubleEndedIterator for Bidi where I: DoubleEndedIterator { #[inline] fn next_back(&mut self) -> Option { (&mut self.next_back)(&mut self.inner) } #[inline] fn nth_back(&mut self, n: usize) -> Option { (&mut self.nth_back)(&mut self.inner, n) } } impl ExactSizeIterator for Bidi where I: DoubleEndedIterator + ExactSizeIterator { #[inline] #[cfg(not(tarpaulin_include))] fn len(&self) -> usize { self.inner.len() } } impl FusedIterator for Bidi where I: DoubleEndedIterator + FusedIterator { } /// Extension trait that provides `.bidi()` for all double-ended iterators. pub trait BidiIterator where Self: Sized + IntoIterator, ::IntoIter: DoubleEndedIterator, { /// Conditionally reverses the direction of iteration. /// /// When `cond` is true, this adapter swaps the `next` and `nth` methods /// with `next_back` and `nth_back`. The resulting iterator is equivalent to /// `if cond { self.rev() } else { self }`. /// /// ## Examples /// /// ```rust /// use wyz::BidiIterator; /// /// let data = [1, 2, 3]; /// let mut iter = data.iter().copied().bidi(false); /// assert_eq!(iter.next(), Some(1)); /// assert_eq!(iter.next_back(), Some(3)); /// /// let mut iter = data.iter().copied().bidi(true); /// assert_eq!(iter.next(), Some(3)); /// assert_eq!(iter.next_back(), Some(1)); /// ``` fn bidi(self, cond: bool) -> Bidi { Bidi::new(self, cond) } } impl BidiIterator for I where I: Sized + IntoIterator, ::IntoIter: DoubleEndedIterator, { } #[cfg(test)] mod tests { use super::*; #[test] fn forward() { let mut iter = (0 .. 6).bidi(false); assert_eq!(iter.next(), Some(0)); assert_eq!(iter.next_back(), Some(5)); assert_eq!(iter.nth(1), Some(2)); assert_eq!(iter.nth_back(1), Some(3)); assert!(iter.next().is_none()); } #[test] fn reverse() { let mut iter = (0 .. 6).bidi(true); assert_eq!(iter.next(), Some(5)); assert_eq!(iter.next_back(), Some(0)); assert_eq!(iter.nth(1), Some(3)); assert_eq!(iter.nth_back(1), Some(2)); assert!(iter.next().is_none()); } } wyz-0.5.1/src/comu.rs000064400000000000000000000344421046102023000125670ustar 00000000000000/*! Trait-level `co`nst/`mu`table tracking. This module provides a system of marker types that can be used to encode write permissions into type parameters rather than duplicate structures. !*/ // This module has no compute logic of its own; it only exists in the // type-system and to forward to the standard library. #![cfg(not(tarpaulin_include))] use core::{ cmp, convert::TryFrom, fmt::{ self, Debug, Display, Formatter, Pointer, }, hash::{ Hash, Hasher, }, ops::Deref, ptr::NonNull, slice, }; use tap::Pipe; /// A basic `const` marker. #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct Const; /// A basic `mut` marker. #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct Mut; /// A frozen wrapper over some other `Mutability` marker. #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct Frozen where Inner: Mutability { inner: Inner, } /** Generalized mutability permissions. This trait enables referent structures to be generic over the write permissions of their referent data. As an example, the standard library defines `*const T` and `*mut T` as two duplicate type families, that cannot share any logic at all. An equivalent library implementation might be `Ptr`, where shared logic can be placed in an `impl Ptr` block, but unique logic (such as freezing a `Mut` pointer, or unfreezing a `Frozen`) can be placed in specialized `impl Ptr` blocks. **/ pub trait Mutability: 'static + Copy + Sized + self::seal::Sealed { /// Marks whether this type contains mutability permissions within it. /// /// This is `false` for `Const` and `true` for `Mut`. `Frozen` wrappers /// atop either of these types inherit their interior marker. const CONTAINS_MUTABILITY: bool = false; /// Counts the layers of `Frozen<>` wrapping around a base `Const` or `Mut`. const PEANO_NUMBER: usize = 0; /// Allow instances to be constructed generically. const SELF: Self; /// One of `*const` or `*mut`. const RENDER: &'static str; /// Freeze this type, wrapping it in a `const` marker that may later be /// removed to thaw it. fn freeze(self) -> Frozen { Frozen { inner: self } } /// Thaw a previously-frozen type, removing its `Frozen` marker and /// restoring it to `Self`. fn thaw(Frozen { inner }: Frozen) -> Self { inner } } impl Mutability for Const { const RENDER: &'static str = "*const"; const SELF: Self = Self; } impl self::seal::Sealed for Const { } impl Mutability for Frozen where Inner: Mutability + Sized { const CONTAINS_MUTABILITY: bool = Inner::CONTAINS_MUTABILITY; const PEANO_NUMBER: usize = 1 + Inner::PEANO_NUMBER; const RENDER: &'static str = Inner::RENDER; const SELF: Self = Self { inner: Inner::SELF }; } impl self::seal::Sealed for Frozen where Inner: Mutability + Sized { } impl Mutability for Mut { const CONTAINS_MUTABILITY: bool = true; const RENDER: &'static str = "*mut"; const SELF: Self = Self; } impl self::seal::Sealed for Mut { } /** A generic non-null pointer with type-system mutability tracking. # Type Parameters - `M`: The mutability permissions of the source pointer. - `T`: The referent type of the source pointer. **/ pub struct Address where M: Mutability, T: ?Sized, { /// The address value. inner: NonNull, /// The mutability permissions. comu: M, } impl Address where M: Mutability { /// The dangling pointer. pub const DANGLING: Self = Self { inner: NonNull::dangling(), comu: M::SELF, }; } impl Address where M: Mutability, T: ?Sized, { /// Constructs a new `Address` over some pointer value. /// /// You are responsible for selecting the correct `Mutability` marker. #[inline(always)] pub fn new(addr: NonNull) -> Self { Self { inner: addr, comu: M::SELF, } } /// Permanently converts an `Address<_>` into an `Address`. /// /// You should generally prefer [`Address::freeze`]. #[inline(always)] pub fn immut(self) -> Address { Address { inner: self.inner, comu: Const, } } /// Force an `Address` to be `Address`. /// /// ## Safety /// /// You should only call this on addresses you know to have been created /// with `Mut`able permissions and previously removed by [`Address::immut`]. /// /// You should prefer using [`Address::freeze`] for temporary, trackable, /// immutability constraints instead. #[inline(always)] pub unsafe fn assert_mut(self) -> Address { Address { inner: self.inner, comu: Mut, } } /// Freezes the `Address` so that it is read-only. #[inline(always)] pub fn freeze(self) -> Address, T> { let Self { inner, comu } = self; Address { inner, comu: comu.freeze(), } } /// Removes the `Address` type marker, returning the original pointer. #[inline(always)] pub fn into_inner(self) -> NonNull { self.inner } /// Gets the address as a read-only pointer. #[inline(always)] pub fn to_const(self) -> *const T { self.inner.as_ptr() as *const T } } impl Address { /// Gets the address as a write-capable pointer. #[inline(always)] #[allow(clippy::wrong_self_convention)] pub fn to_mut(self) -> *mut T { self.inner.as_ptr() } } impl Address, T> where M: Mutability, T: ?Sized, { /// Thaws the `Address` to its original mutability permission. #[inline(always)] pub fn thaw(self) -> Address { let Self { inner, comu } = self; Address { inner, comu: Mutability::thaw(comu), } } } /// Implement `*T -> *T` functions as `Address -> Address`. macro_rules! fwd { ($( $(@$unsafe:ident)? $name:ident $(< $($lt:lifetime),* $($typaram:ident$(: $($bound:ident),+ $(,)?)?),* $(,)* >)? $(, $arg:ident: $ty:ty)* $(=> $ret:ty)? );+ $(;)?) => { $( #[doc = concat!("Applies `<*T>::", stringify!($name), "`.")] /// /// See [original documentation][orig]. /// #[doc = concat!("[orig]: https://doc.rust-lang.org/std/primitive.pointer.html#method.", stringify!($name))] pub $($unsafe)? fn $name$(< $($lt,)* $($typaram$(: $($bound),+)?,)* >)?(self$(, $arg: $ty)*) $(-> $ret)? { self.with_ptr(|ptr| ptr.$name($($arg),*)) } )+ }; } /// Implement all other pointer functions. macro_rules! map { ($( $(@$unsafe:ident)? $name:ident $(< $($lt:lifetime),* $($typaram:ident$(: $($bound:ident),+ $(,)?)?),* $(,)? >)? $(, $arg:ident: $ty:ty $(as $map:expr)?)* $(=> $ret:ty)? );+ $(;)?) => { $( #[doc = concat!("Applies `<*T>::", stringify!($name), "`.")] /// /// See [original documentation][orig]. /// #[doc = concat!("[orig]: https://doc.rust-lang.org/std/primitive.pointer.html#method.", stringify!($name))] pub $($unsafe)? fn $name$(< $($lt,)* $($typaram$(: $($bound),+)?,)* >)?(self$(, $arg: $ty)*) $(-> $ret)? { self.inner.as_ptr().$name($($arg$(.pipe($map))?),*) } )+ }; } /// Port of the pointer inherent methods on `Address`es of `Sized` types. #[allow(clippy::missing_safety_doc)] impl Address where M: Mutability { fwd! { cast => Address; @unsafe offset, count: isize => Self; @unsafe add, count: usize => Self; @unsafe sub, count: usize => Self; wrapping_offset, count: isize => Self; wrapping_add, count: usize => Self; wrapping_sub, count: usize => Self; } map! { @unsafe offset_from, origin: Self as |orig| orig.to_const() as *mut T => isize; @unsafe read => T; @unsafe read_volatile => T; @unsafe read_unaligned => T; @unsafe copy_to, dest: Address as Address::to_mut, count: usize; @unsafe copy_to_nonoverlapping, dest: Address as Address::to_mut, count: usize; align_offset, align: usize => usize; } } /// Port of the pointer inherent methods on `Address`es of any type. impl Address where M: Mutability, T: ?Sized, { map! { @unsafe as_ref<'a> => Option<&'a T>; } /// Applies a pointer -> pointer function within an Address -> Address. #[track_caller] fn with_ptr(self, func: impl FnOnce(*mut T) -> *mut U) -> Address { self.inner .as_ptr() .pipe(func) .pipe(NonNull::new) .unwrap() .pipe(Address::new) } } /// Port of pointer inherent methods on mutable `Address`es of sized types. impl Address { map! { @unsafe copy_from, src: Address as Address::to_const, count: usize; @unsafe copy_from_nonoverlapping, src: Address as Address::to_const, count: usize; @unsafe write, value: T; @unsafe write_volatile, value: T; @unsafe write_unaligned, value: T; @unsafe replace, src: T => T; @unsafe swap, with: Self as Self::to_mut; } } /// Port of pointer inherent methods on mutable `Address`es of any type. impl Address where T: ?Sized { map! { @unsafe as_mut<'a> => Option<&'a mut T>; @unsafe drop_in_place; } } impl Clone for Address where M: Mutability, T: ?Sized, { #[inline(always)] fn clone(&self) -> Self { *self } } impl TryFrom<*const T> for Address where T: ?Sized { type Error = NullPtrError; #[inline(always)] fn try_from(elem: *const T) -> Result { NonNull::new(elem as *mut T) .ok_or(NullPtrError) .map(Self::new) } } impl From<&T> for Address where T: ?Sized { #[inline(always)] fn from(elem: &T) -> Self { Self::new(elem.into()) } } impl TryFrom<*mut T> for Address where T: ?Sized { type Error = NullPtrError; #[inline(always)] fn try_from(elem: *mut T) -> Result { NonNull::new(elem).ok_or(NullPtrError).map(Self::new) } } impl From<&mut T> for Address where T: ?Sized { #[inline(always)] fn from(elem: &mut T) -> Self { Self::new(elem.into()) } } impl Eq for Address where M: Mutability { } impl PartialEq> for Address where M1: Mutability, M2: Mutability, { #[inline] fn eq(&self, other: &Address) -> bool { self.inner.as_ptr() as usize == other.inner.as_ptr() as usize } } impl Ord for Address where M: Mutability { #[inline] fn cmp(&self, other: &Self) -> cmp::Ordering { self.partial_cmp(other) .expect("Addresses have a total ordering") } } impl PartialOrd> for Address where M1: Mutability, M2: Mutability, { #[inline] fn partial_cmp(&self, other: &Address) -> Option { (self.inner.as_ptr() as usize) .partial_cmp(&(other.inner.as_ptr() as usize)) } } impl Debug for Address where M: Mutability, T: ?Sized, { #[inline(always)] fn fmt(&self, fmt: &mut Formatter) -> fmt::Result { Debug::fmt(&self.to_const(), fmt) } } impl Pointer for Address where M: Mutability, T: ?Sized, { #[inline(always)] fn fmt(&self, fmt: &mut Formatter) -> fmt::Result { Pointer::fmt(&self.to_const(), fmt) } } impl Hash for Address where M: Mutability, T: ?Sized, { #[inline(always)] fn hash(&self, state: &mut H) where H: Hasher { self.inner.hash(state) } } impl Copy for Address where M: Mutability, T: ?Sized, { } impl self::seal::Sealed for Address where M: Mutability, T: ?Sized, { } /// Allows an `Address` to produce an ordinary reference. pub trait Referential<'a>: self::seal::Sealed { /// The created reference type. Must be one of `&T` or `&mut T`. type Ref: 'a + Deref; /// Converts the `Address` to a reference. /// /// ## Safety /// /// The caller is responsible for ensuring that the memory location that the /// `Address` describes contains an initialized value, and that the produced /// reference abides by the Rust `&`/`&mut` exclusion rules. unsafe fn to_ref(self) -> Self::Ref; /// Converts a reference back into an `Address`. fn from_ref(this: Self::Ref) -> Self; } impl<'a, T> Referential<'a> for Address where T: 'a + ?Sized { type Ref = &'a T; unsafe fn to_ref(self) -> Self::Ref { self.inner.as_ref() } fn from_ref(this: Self::Ref) -> Self { this.into() } } impl<'a, T> Referential<'a> for Address where T: 'a + ?Sized { type Ref = &'a mut T; unsafe fn to_ref(mut self) -> Self::Ref { self.inner.as_mut() } fn from_ref(this: Self::Ref) -> Self { this.into() } } impl<'a, M, T> Referential<'a> for Address, T> where M: Mutability, T: 'a + ?Sized, { type Ref = &'a T; unsafe fn to_ref(self) -> Self::Ref { self.inner.as_ref() } fn from_ref(this: Self::Ref) -> Self { Self::new(NonNull::from(this)) } } /// A generically-mutable reference. pub type Reference<'a, M, T> = as Referential<'a>>::Ref; /// Allows an `Address` to produce an ordinary slice reference. pub trait SliceReferential<'a>: Referential<'a> + self::seal::Sealed { /// The type of the element pointer. type ElementAddr; /// Constructs an ordinary slice reference from a base-address and a length. /// /// ## Parameters /// /// - `ptr`: The address of the base element in the slice. /// - `len`: The number of elements, beginning at `ptr`, in the slice. /// /// ## Safety /// /// The base address and the element count must describe a valid region of /// memory. unsafe fn from_raw_parts(ptr: Self::ElementAddr, len: usize) -> Self::Ref; } impl<'a, T> SliceReferential<'a> for Address where T: 'a { type ElementAddr = Address; unsafe fn from_raw_parts(ptr: Self::ElementAddr, len: usize) -> Self::Ref { slice::from_raw_parts(ptr.to_const(), len) } } impl<'a, M, T> SliceReferential<'a> for Address, [T]> where M: Mutability, T: 'a, { type ElementAddr = Address, T>; unsafe fn from_raw_parts(ptr: Self::ElementAddr, len: usize) -> Self::Ref { slice::from_raw_parts(ptr.to_const(), len) } } impl<'a, T> SliceReferential<'a> for Address where T: 'a { type ElementAddr = Address; unsafe fn from_raw_parts(ptr: Self::ElementAddr, len: usize) -> Self::Ref { slice::from_raw_parts_mut(ptr.to_mut(), len) } } /// [`Address`] cannot be constructed over null pointers. #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct NullPtrError; impl Display for NullPtrError { #[inline] fn fmt(&self, fmt: &mut Formatter) -> fmt::Result { write!(fmt, "wyz::Address cannot contain a null pointer") } } #[cfg(feature = "std")] impl std::error::Error for NullPtrError { } #[doc(hidden)] mod seal { #[doc(hidden)] pub trait Sealed {} } wyz-0.5.1/src/exit.rs000064400000000000000000000016051046102023000125700ustar 00000000000000/*! `exit!` macro The `exit!` macro simplifies exiting with an error code, and optionally printing an error message prior to exit. # Examples This example exits with status `1`. ```rust,should_panic wyz::exit!(); ``` This example exits with status `2`. ```rust,should_panic wyz::exit!(2); ``` This example exits with status `3`, and uses `eprintln!` to print an error message before exiting. Note that if `stderr` has been closed, this will crash the program with a panic due to `SIGPIPE`, and *not* call `process::exit()`. ```rust,should_panic wyz::exit!(3, "Error status: {}", "testing"); ``` !*/ #![cfg(feature = "std")] /// `exit!` macro #[macro_export] macro_rules! exit { () => { $crate::exit!(1); }; ( $num:expr $(,)? ) => { ::std::process::exit($num); }; ( $num:expr, $fmt:expr $( , $arg:expr )* $(,)? ) => {{ eprintln!($fmt $( , $arg )*); $crate::exit!($num); }}; } wyz-0.5.1/src/fmt.rs000064400000000000000000000252151046102023000124100ustar 00000000000000/*! Format forwarding This module provides wrapper types for each formatting trait other than `Debug` which, when `Debug`-formatted, forward to the original trait instead of `Debug`. Each wrapper type is a tuple struct so that it can be used as a named constructor, such as in `.map(FmtDisplay)`. In addition, a blanket trait adds extension methods `.fmt_>()` to provide the corresponding wrap. Any modifiers in the format template string or struct modifier are passed through to the desired trait implementation unchanged. The only effect of the forwarding types in this module is to change the `?` template character to one of the other trait signifiers. !*/ use core::{ fmt::{ self, Binary, Debug, Display, Formatter, LowerExp, LowerHex, Octal, Pointer, UpperExp, UpperHex, }, ops::{ Deref, DerefMut, }, }; /// Wraps any value with a format-forward to `Debug`. #[cfg(not(tarpaulin_include))] pub trait FmtForward: Sized { /// Causes `self` to use its `Binary` implementation when `Debug`-formatted. #[inline(always)] fn fmt_binary(self) -> FmtBinary where Self: Binary { FmtBinary(self) } /// Causes `self` to use its `Display` implementation when /// `Debug`-formatted. #[inline(always)] fn fmt_display(self) -> FmtDisplay where Self: Display { FmtDisplay(self) } /// Causes `self` to use its `LowerExp` implementation when /// `Debug`-formatted. #[inline(always)] fn fmt_lower_exp(self) -> FmtLowerExp where Self: LowerExp { FmtLowerExp(self) } /// Causes `self` to use its `LowerHex` implementation when /// `Debug`-formatted. #[inline(always)] fn fmt_lower_hex(self) -> FmtLowerHex where Self: LowerHex { FmtLowerHex(self) } /// Causes `self` to use its `Octal` implementation when `Debug`-formatted. #[inline(always)] fn fmt_octal(self) -> FmtOctal where Self: Octal { FmtOctal(self) } /// Causes `self` to use its `Pointer` implementation when /// `Debug`-formatted. #[inline(always)] fn fmt_pointer(self) -> FmtPointer where Self: Pointer { FmtPointer(self) } /// Causes `self` to use its `UpperExp` implementation when /// `Debug`-formatted. #[inline(always)] fn fmt_upper_exp(self) -> FmtUpperExp where Self: UpperExp { FmtUpperExp(self) } /// Causes `self` to use its `UpperHex` implementation when /// `Debug`-formatted. #[inline(always)] fn fmt_upper_hex(self) -> FmtUpperHex where Self: UpperHex { FmtUpperHex(self) } /// Formats each item in a sequence. /// /// This wrapper structure conditionally implements all of the formatting /// traits when `self` can be viewed as an iterator whose *items* implement /// them. It iterates over `&self` and prints each item according to the /// formatting specifier provided. #[inline(always)] fn fmt_list(self) -> FmtList where for<'a> &'a Self: IntoIterator { FmtList(self) } } impl FmtForward for T { } /// Forwards a type’s `Binary` formatting implementation to `Debug`. #[repr(transparent)] pub struct FmtBinary(pub T); /// Forwards a type’s `Display` formatting implementation to `Debug`. #[repr(transparent)] pub struct FmtDisplay(pub T); /// Renders each element of a stream into a list. #[repr(transparent)] pub struct FmtList(pub T) where for<'a> &'a T: IntoIterator; /// Forwards a type’s `LowerExp` formatting implementation to `Debug`. #[repr(transparent)] pub struct FmtLowerExp(pub T); /// Forwards a type’s `LowerHex` formatting implementation to `Debug`. #[repr(transparent)] pub struct FmtLowerHex(pub T); /// Forwards a type’s `Octal` formatting implementation to `Debug`. #[repr(transparent)] pub struct FmtOctal(pub T); /// Forwards a type’s `Pointer` formatting implementation to `Debug`. #[repr(transparent)] pub struct FmtPointer(pub T); /// Forwards a type’s `UpperExp` formatting implementation to `Debug`. #[repr(transparent)] pub struct FmtUpperExp(pub T); /// Forwards a type’s `UpperHex` formatting implementation to `Debug`. #[repr(transparent)] pub struct FmtUpperHex(pub T); macro_rules! fmt { ($($w:ty => $t:ident),* $(,)?) => { $( #[cfg(not(tarpaulin_include))] impl Binary for $w { #[inline(always)] fn fmt(&self, fmt: &mut Formatter) -> fmt::Result { Binary::fmt(&self.0, fmt) } } impl Debug for $w { #[inline(always)] fn fmt(&self, fmt: &mut Formatter) -> fmt::Result { ::fmt(&self.0, fmt) } } #[cfg(not(tarpaulin_include))] impl Display for $w { #[inline(always)] fn fmt(&self, fmt: &mut Formatter) -> fmt::Result { Display::fmt(&self.0, fmt) } } #[cfg(not(tarpaulin_include))] impl LowerExp for $w { #[inline(always)] fn fmt(&self, fmt: &mut Formatter) -> fmt::Result { LowerExp::fmt(&self.0, fmt) } } #[cfg(not(tarpaulin_include))] impl LowerHex for $w { #[inline(always)] fn fmt(&self, fmt: &mut Formatter) -> fmt::Result { LowerHex::fmt(&self.0, fmt) } } #[cfg(not(tarpaulin_include))] impl Octal for $w { #[inline(always)] fn fmt(&self, fmt: &mut Formatter) -> fmt::Result { Octal::fmt(&self.0, fmt) } } #[cfg(not(tarpaulin_include))] impl Pointer for $w { #[inline(always)] fn fmt(&self, fmt: &mut Formatter) -> fmt::Result { Pointer::fmt(&self.0, fmt) } } #[cfg(not(tarpaulin_include))] impl UpperExp for $w { #[inline(always)] fn fmt(&self, fmt: &mut Formatter) -> fmt::Result { UpperExp::fmt(&self.0, fmt) } } #[cfg(not(tarpaulin_include))] impl UpperHex for $w { #[inline(always)] fn fmt(&self, fmt: &mut Formatter) -> fmt::Result { UpperHex::fmt(&self.0, fmt) } } #[cfg(not(tarpaulin_include))] impl Deref for $w { type Target = T; #[inline(always)] fn deref(&self) -> &Self::Target { &self.0 } } #[cfg(not(tarpaulin_include))] impl DerefMut for $w { #[inline(always)] fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } } #[cfg(not(tarpaulin_include))] impl AsRef for $w { #[inline(always)] fn as_ref(&self) -> &T { &self.0 } } #[cfg(not(tarpaulin_include))] impl AsMut for $w { #[inline(always)] fn as_mut(&mut self) -> &mut T { &mut self.0 } } )* }; } fmt!( FmtBinary => Binary, FmtDisplay => Display, FmtLowerExp => LowerExp, FmtLowerHex => LowerHex, FmtOctal => Octal, FmtPointer => Pointer, FmtUpperExp => UpperExp, FmtUpperHex => UpperHex, ); impl Binary for FmtList where for<'a> &'a T: IntoIterator, for<'a> <&'a T as IntoIterator>::Item: Binary, { fn fmt(&self, fmt: &mut Formatter) -> fmt::Result { fmt.debug_list() .entries((&self.0).into_iter().map(FmtBinary)) .finish() } } impl Debug for FmtList where for<'a> &'a T: IntoIterator, for<'a> <&'a T as IntoIterator>::Item: Debug, { fn fmt(&self, fmt: &mut Formatter) -> fmt::Result { fmt.debug_list().entries((&self.0).into_iter()).finish() } } impl Display for FmtList where for<'a> &'a T: IntoIterator, for<'a> <&'a T as IntoIterator>::Item: Display, { fn fmt(&self, fmt: &mut Formatter) -> fmt::Result { fmt.debug_list() .entries((&self.0).into_iter().map(FmtDisplay)) .finish() } } impl LowerExp for FmtList where for<'a> &'a T: IntoIterator, for<'a> <&'a T as IntoIterator>::Item: LowerExp, { fn fmt(&self, fmt: &mut Formatter) -> fmt::Result { fmt.debug_list() .entries((&self.0).into_iter().map(FmtLowerExp)) .finish() } } impl LowerHex for FmtList where for<'a> &'a T: IntoIterator, for<'a> <&'a T as IntoIterator>::Item: LowerHex, { fn fmt(&self, fmt: &mut Formatter) -> fmt::Result { fmt.debug_list() .entries((&self.0).into_iter().map(FmtLowerHex)) .finish() } } impl Octal for FmtList where for<'a> &'a T: IntoIterator, for<'a> <&'a T as IntoIterator>::Item: Octal, { fn fmt(&self, fmt: &mut Formatter) -> fmt::Result { fmt.debug_list() .entries((&self.0).into_iter().map(FmtOctal)) .finish() } } impl UpperExp for FmtList where for<'a> &'a T: IntoIterator, for<'a> <&'a T as IntoIterator>::Item: UpperExp, { fn fmt(&self, fmt: &mut Formatter) -> fmt::Result { fmt.debug_list() .entries((&self.0).into_iter().map(FmtUpperExp)) .finish() } } impl UpperHex for FmtList where for<'a> &'a T: IntoIterator, for<'a> <&'a T as IntoIterator>::Item: UpperHex, { fn fmt(&self, fmt: &mut Formatter) -> fmt::Result { fmt.debug_list() .entries((&self.0).into_iter().map(FmtUpperHex)) .finish() } } #[cfg(not(tarpaulin_include))] impl Deref for FmtList where for<'a> &'a T: IntoIterator { type Target = T; #[inline(always)] fn deref(&self) -> &Self::Target { &self.0 } } #[cfg(not(tarpaulin_include))] impl DerefMut for FmtList where for<'a> &'a T: IntoIterator { #[inline(always)] fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } } #[cfg(not(tarpaulin_include))] impl AsRef for FmtList where for<'a> &'a T: IntoIterator { #[inline(always)] fn as_ref(&self) -> &T { &self.0 } } #[cfg(not(tarpaulin_include))] impl AsMut for FmtList where for<'a> &'a T: IntoIterator { #[inline(always)] fn as_mut(&mut self) -> &mut T { &mut self.0 } } #[cfg(all(test, feature = "alloc"))] mod tests { #[cfg(not(feature = "std"))] use alloc::format; #[cfg(feature = "std")] use std::format; use super::*; #[test] fn render_item() { let num = 29; assert_eq!(format!("{:?}", num.fmt_binary()), "11101"); assert_eq!(format!("{:?}", num.fmt_display()), "29"); assert_eq!(format!("{:?}", num.fmt_upper_hex()), "1D"); assert_eq!(format!("{:?}", num.fmt_octal()), "35"); assert_eq!(format!("{:?}", num.fmt_lower_hex()), "1d"); let num = 53.7; assert_eq!(format!("{:?}", num.fmt_lower_exp()), "5.37e1"); assert_eq!(format!("{:?}", num.fmt_upper_exp()), "5.37E1"); } #[test] fn render_list() { let list = [0, 1, 2, 3]; assert_eq!(format!("{:02b}", list.fmt_list()), "[00, 01, 10, 11]"); assert_eq!(format!("{:01?}", list.fmt_list()), "[0, 1, 2, 3]"); assert_eq!(format!("{:01}", list.fmt_list()), "[0, 1, 2, 3]"); let list = [-51.0, -1.2, 1.3, 54.0]; assert_eq!( format!("{:e}", list.fmt_list()), "[-5.1e1, -1.2e0, 1.3e0, 5.4e1]" ); assert_eq!( format!("{:E}", list.fmt_list()), "[-5.1E1, -1.2E0, 1.3E0, 5.4E1]" ); let list = [0, 10, 20, 30]; assert_eq!(format!("{:02x}", list.fmt_list()), "[00, 0a, 14, 1e]"); assert_eq!(format!("{:02o}", list.fmt_list()), "[00, 12, 24, 36]"); assert_eq!(format!("{:02X}", list.fmt_list()), "[00, 0A, 14, 1E]"); } } wyz-0.5.1/src/lib.rs000064400000000000000000000015531046102023000123670ustar 00000000000000/*! `wyz` – myrrlyn’s wyzyrdly library This crate consolidates all the small tools and conveniences I’ve built up in my experience building Rust crates. Each module has more documentation about what it contains. The modules are largely independent, and can be used individually. !*/ #![no_std] #![cfg_attr(debug_assertions, warn(missing_docs))] #![cfg_attr(not(debug_assertions), deny(missing_docs))] #[cfg(feature = "alloc")] extern crate alloc; #[cfg(feature = "std")] extern crate std; pub mod bidi; pub mod comu; pub mod fmt; pub mod range; #[cfg(all(feature = "std", feature = "garbage"))] pub mod wm; #[cfg(feature = "std")] #[macro_use] pub mod exit; pub use self::{ bidi::*, comu::*, fmt::*, range::*, }; #[cfg(feature = "std")] pub use self::exit::*; #[cfg(all(feature = "std", feature = "garbage"))] pub use self::wm::{ BgDrop, BgDropExt, }; wyz-0.5.1/src/range.rs000064400000000000000000000102661046102023000127160ustar 00000000000000//! Range utilities. use core::ops::{ Bound, Range, RangeBounds, }; /// Extension methods for working with various range types. pub trait RangeExt: RangeBounds where T: Ord { /// Normalizes a range-like type to a canonical half-open `Range`. /// /// ## Parameters /// /// - `self`: The range to normalize. /// - `start`: An optional fallback *inclusive* lower bound. /// - `end`: An optional fallback *exclusive* upper bound. /// /// ## Returns /// /// A `Range` whose start and end values are the following, in order of /// decreasing priority: /// /// - `self.start()`, or if absent, the `start` parameter, or if it is /// `None`, `0`. /// - `self.end()`, or if absent, the `end` parameter, or if it is `None`, /// !0`. fn normalize( self, start: impl Into>, end: impl Into>, ) -> Range; /// Finds the intersection between two range-likes. The produced `Range` /// spans only the elements common to both. /// /// This returns `None` if the ranges do not have an intersection (at least /// one element present in both ranges). fn intersection(self, other: R) -> Option> where R: RangeExt; /// Finds the union between two range-likes. The produced `Range` spans all /// elements present in at least one of them. /// /// This returns `None` if the ranges do not have an intersection (at least /// one element present in both ranges). fn union(self, other: R) -> Option> where R: RangeExt; } // TODO(myrrlyn): Use funty to extend this for all integers. impl RangeExt for R where R: RangeBounds { fn normalize( self, start: impl Into>, end: impl Into>, ) -> Range { let start = match self.start_bound() { Bound::Unbounded => start.into().unwrap_or(0), Bound::Included(&v) => v, Bound::Excluded(&v) => v.saturating_add(1), }; let end = match self.end_bound() { Bound::Unbounded => end.into().unwrap_or(!0), Bound::Included(&v) => v.saturating_add(1), Bound::Excluded(&v) => v, }; if start > end { end .. start } else { start .. end } } fn intersection(self, other: R2) -> Option> where R2: RangeExt { let Range { start: a1, end: a2 } = self.normalize(None, None); let Range { start: b1, end: b2 } = other.normalize(None, None); if b1 < a1 { return (b1 .. b2).intersection(a1 .. a2); } if !(a1 .. a2).contains(&b1) { return None; } let start = a1.max(b1); let end = a2.min(b2); if start > end { Some(end .. start) } else { Some(start .. end) } } fn union(self, other: R2) -> Option> where R2: RangeExt { let Range { start: a1, end: a2 } = self.normalize(None, None); let Range { start: b1, end: b2 } = other.normalize(None, None); if b1 < a1 { return (b1 .. b2).intersection(a1 .. a2); } if !(a1 .. a2).contains(&b1) { return None; } let start = a1.min(b1); let end = a2.max(b2); if start > end { Some(end .. start) } else { Some(start .. end) } } } #[cfg(test)] mod tests { use super::*; #[test] fn normalize() { let r = (..).normalize(1, 10); assert!(r.contains(&1)); assert!(r.contains(&9)); assert!(!r.contains(&0)); assert!(!r.contains(&10)); let r = (.. 10).normalize(1, 20); assert!(r.contains(&1)); assert!(r.contains(&9)); assert!(!r.contains(&0)); assert!(!r.contains(&10)); let r = (4 ..).normalize(6, 10); assert!(r.contains(&4)); assert!(r.contains(&9)); assert!(!r.contains(&3)); assert!(!r.contains(&10)); let r = (4 ..= 10).normalize(6, 8); assert!(r.contains(&4)); assert!(r.contains(&10)); assert!(!r.contains(&3)); assert!(!r.contains(&11)); let r = (..= 10).normalize(1, 8); assert!(r.contains(&1)); assert!(r.contains(&10)); assert!(!r.contains(&0)); assert!(!r.contains(&11)); } #[test] fn intersect() { let a = 3 .. 10; let b = 7 ..= 20; assert_eq!(a.intersection(b), Some(7 .. 10)); let c = 3 .. 10; let d = 13 ..= 20; assert!(c.intersection(d).is_none()); } #[test] fn union() { let a = 3 .. 10; let b = 7 ..= 20; assert_eq!(a.union(b), Some(3 .. 21)); let c = 3 .. 10; let d = 13 ..= 20; assert!(c.union(d).is_none()); } } wyz-0.5.1/src/wm.rs000064400000000000000000000337421046102023000122510ustar 00000000000000/*! Waste Management Do you drive your own garbage to the landfill or compost heap? Maybe you should, but that’s a lot of work and takes time out of your day, so you probably don’t. Instead, you give it to a worker that specializes in managing objects at the end of their time in your service. This module moves objects from the threads where they were working to a single, global, worker thread when they go out of scope. Since an object that is going out of scope can no longer be used, you could say that it is *garbage*; since there is only one worker thread to receive all such objects, you could say that the worker *collects* them. Wink wink, nudge nudge. Users need only wrap their values in `BgDrop` to have their garbage collected. `BgDrop` only accepts `'static` values, since the values are being sent to another thread that makes no guarantees about timeliness of destruction, and thus the garbage cannot have any lingering ties to live objects in the rest of the program. When a `BgDrop` goes out of scope, it attempts to send its interior value to the collector thread. The first `BgDrop` to drop must start the collector thread, which may result in an indefinite block until the thread begins. Once the collector is running, all `BgDrop` drops will *attempt* to send their internal value to the collector for destruction. If the send fails, then the value will be dropped on the sending thread, rather than on the collector. You can prevent future collections with `cancel_collection()`, which destroys the channel used to move values to the collector thread. You can also get the thread key for the collector with `collector()`. If you need to ensure that all pending destructions occur before program exit, you should end your program with a `cancel_collection()` and then `collector().unwrap().join()`. The collector guarantees that objects queued for destruction are either enqueued for future destruction *or* destroyed immediately, so the collector thread *will* receive a signal for each object not destroyed on its prior thread. !*/ #![cfg(all(feature = "std", feature = "garbage"))] use once_cell::sync::OnceCell; use tap::Pipe; use std::{ collections::VecDeque, marker::PhantomData, mem::{ self, ManuallyDrop, }, ops::{ Deref, DerefMut, }, sync::{ mpsc, Mutex, MutexGuard, Once, RwLock, }, thread, }; use typemap::TypeMap; /** Run an object’s destructor in the background. When `BgDrop`-wrapped objects go out of scope, the `BgDrop` destructor attempts to use a global background-thread to receive the wrapped value, so that its destructor is run on the worker thread. If the thread running a `BgDrop` destructor is able to send the value to the worker, then it resumes immediately, and does not wait for the worker to get around to actually running the wrapped destructor. This is similar to the disposal semantics of many GC systems, though the actual system used to determine when an object becomes garbage is still the compiler’s static lifetime analyzer. All `BgDrop` types use the same persistent worker thread, minimizing the program cost of deferral. If the function [`wm::shutdown()`] is called, all future `BgDrop`s become a noöp and run their contained destructors on their local threads. [`wm::shutdown()`]: ../fn.shutdown.html **/ #[repr(transparent)] pub struct BgDrop { inner: ManuallyDrop, } impl BgDrop { /// Instructs an object to run its destructor in the background. /// /// This function modifies the wrapped object’s `Drop` implementation to try /// to, on `drop`, send the inner object to a background thread for actual /// destruction. If the object cannot be sent to the background when its /// wrapper goes out of scope, then its destructor runs immediately, in the /// thread that had been holding the object when the modified destructor was /// called. /// /// If the wrapped object is successfully sent to the background, the /// modified destructor exits, and the current thread resumes work. Once /// enqueued, the inner object is guaranteed to be *eventually* destroyed, /// unless the program exits in a manner that prevents the background /// collector from emptying its work queue. #[inline(always)] pub fn new(value: T) -> Self { Self { inner: ManuallyDrop::new(value), } } /// Removes the background-destruction marker, returning the interior value. #[inline(always)] pub fn into_inner(mut self) -> T { unsafe { ManuallyDrop::take(&mut self.inner) } } /// Attempt to prevent double-deferral, which would cause the outer to send /// the inner to the worker thread, making the worker thread send the /// *actual* inner to itself for destruction. This is safe, but stupid. #[inline(always)] #[doc(hidden)] pub fn bg_drop(self) -> Self { self } #[inline(always)] fn dtor(&mut self) { // No destructor, no problem! Quit. if !mem::needs_drop::() { return; } // Ensure that the collector has been initialized. init(); // Pull the value into local scope, reärming the destructor. let val = unsafe { ManuallyDrop::take(&mut self.inner) }; // Get a local copy of the outbound channel, or exit. let sender = match sender() { Some(s) => s, None => return, }; // Enqueue the object into the transfer buffer. dq().entry::>() .or_insert_with(VecDeque::new) .pipe(|v| v.push_back(val)); // Send the dequeueïng destructor to the collector thread, or run it // locally if the send failed. if sender.send(dtor::).is_err() { dtor::(); } } } impl AsRef for BgDrop { fn as_ref(&self) -> &T { &*self.inner } } impl AsMut for BgDrop { fn as_mut(&mut self) -> &mut T { &mut *self.inner } } impl Deref for BgDrop { type Target = T; fn deref(&self) -> &Self::Target { &*self.inner } } impl DerefMut for BgDrop { fn deref_mut(&mut self) -> &mut Self::Target { &mut *self.inner } } impl Drop for BgDrop { fn drop(&mut self) { self.dtor(); } } /// Attaches a `BgDrop` constructor to all suitable types. pub trait BgDropExt: Sized + 'static { /// Modifies the object’s destructor to run in the background. /// /// When this value goes out of scope, it will attempt to send itself to a /// background thread where its *actual* destructor will be run. The actual /// destructor will run on the local thread only if the transfer to the /// background worker was unable to occur. /// /// The background worker is started only when the first value marked for /// deferred destruction actually drops, so the first call will block until /// the disposal system is initialized. /// /// The first value of each *type* to be deferred will modify the disposal /// system to handle its type. /// /// All subsequent drops of a type that has been deferred before will happen /// nearly instantaneously, as they must only observe that the system is set /// up for them, and move the value into the transfer queue. /// /// # Usage /// /// ```rust /// use wyz::wm::BgDropExt; /// /// vec![1, 2, 3, 4, 5].bg_drop(); /// vec![6, 7, 8, 9, 10].bg_drop(); /// ``` /// /// If you need to guarantee that your program remains open until all /// deferred objects are destroyed, you can block on [`wm::shutdown()`]. /// /// [`wm::shutdown()`]: ../fn.shutdown.html fn bg_drop(self) -> BgDrop { BgDrop::new(self) } } impl BgDropExt for T { } /** Stop the background disposal system. This function shuts down the disposal system, and ensures that all deferred destructors in the program are correctly handled. It disables all *future* deferred-drops from sending values to the worker thread, which forces them to run their destructors locally. In the meantime, the worker thread will pull all remaining values out of its work queue and destroy them, then terminate once it sees that its queue has been closed. When this function returns, the worker thread will have emptied its queue, torn down its transfer system, and exited. You may call this function more than once; it is idempotent. The worker system is program-global, and will only be started once and stopped once. Once this function is called, the program will never run deferred disposal again. Rust does not provide a portable `atexit` behavior, so you are responsible for calling this before your program terminates if you want to ensure that all deferred destructions actually take place. Future versions of this library may register `wm::shutdown()` with the sytem `atexit` handler. If this occurs, the function will be marked as deprecated on platforms where it is set. **/ pub fn shutdown() { static STOP: Once = Once::new(); STOP.call_once(|| { // Destroy the sender handle. let _: Option>> = SEND .get() // Lock the write guard, .and_then(|rw| rw.write().ok()) // And remove the sender handle from it. .and_then(|mut sender| sender.take()); // Close the destructor thread. let _: Option<()> = JOIN .get() // Lock the thread’s mutex, .and_then(|mx| mx.lock().ok()) // Remove the handle from it, .and_then(|mut mg| mg.take()) // And await the thread’s termination. .and_then(|jh| jh.join().ok()); }); } // Disposal system implementation type Dtor = fn() -> (); // The sender is never used concurrently. static SEND: OnceCell>>>> = OnceCell::new(); // The map is only ever used behind a mutex lock. static DUMP: OnceCell>> = OnceCell::new(); static JOIN: OnceCell>>> = OnceCell::new(); /// Initialize the collection system. #[inline(never)] fn init() { let (send, recv) = mpsc::channel::(); // Establish a base sending channel. This holds the collector open until // `cancel()` is called. SEND.get_or_init(|| { send.pipe(AssertThreadsafe::new) .pipe(Some) .pipe(RwLock::new) }); // Establish a transfer queue for all types. DUMP.get_or_init(|| { TypeMap::new().pipe(AssertThreadsafe::new).pipe(Mutex::new) }); // Start the collector thread. JOIN.get_or_init(|| { thread::spawn(move || { while let Ok(ev) = recv.recv() { (ev)() } let _ = mem::replace(&mut **dq(), TypeMap::new()); }) .pipe(Some) .pipe(Mutex::new) }); // TODO(myrrlyn): Register an `atexit` handler to run `shutdown()`. } /// Lock the transfer map. fn dq() -> MutexGuard<'static, AssertThreadsafe> { unsafe { DUMP.get_unchecked() } .lock() .expect("Collection buffer should never observe a panic") } /// Pull the front object out of a typed queue, and destroy it. fn dtor() { // Binding a value causes it to drop *after* any temporaries created in its // construction. let _tmp = dq() // View the deque containing objects of this type. .get_mut::>() // And pop the front value in the queue. It is acceptable to fail. .and_then(VecDeque::pop_front); // The mutex lock returned by `dq()` drops immediately after the semicolon, // and the `_tmp` binding drops immediately before the terminating brace. } /// Get a local copy of the sender, free of threading concerns. fn sender() -> Option> { // `sender` is only called after `SEND` is initialized unsafe { SEND.get_unchecked() } // Quit if the send channel could not be opened for reading .read() .ok()? // or if it contains `None` .as_ref()? .inner // and copy the sender into the local thread. .clone() .pipe(Some) } /// Look up a type’s location in the transfer map. struct Key(PhantomData); impl typemap::Key for Key { /// The transfer map holds some form of collection of the transferred types. /// /// The specific collection type is irrelevant, as long as it supports both /// insertion and removal, and has reasonable behavior characteristics. /// Since the map has to be completely locked for any transfer event, as the /// first transfer of each type must insert its queue into the map, there is /// no advantage in trying to make this mpsc-friendly. /// /// If Rust were to allow a construction like /// /// ```rust,ignore /// fn type_chan() -> ( /// &'static mpsc::Sender, /// &'static mpsc::Receiver, /// ) { /// static MAKE: Once = Once::new(); /// static mut CHAN: Option<(mpsc::Sender, mpsc::Receiver) = None; /// MAKE.call_once(|| unsafe { /// CHAN = Some(mpsc::channel()); /// }); /// (&CHAN.0, &CHAN.1) /// } /// ``` /// /// then a dynamic type-map would not be necessary at all, since each type /// would be granted its own dedicated channel at compile-time. But Rust /// does not, so, it is. type Value = VecDeque; } /** Get off my back, `rustc`. This is required because `static` vars must be `Sync`, and the thread-safety wrappers in use apparently inherit the `Sync`hronicity of their wrapped types. This module uses them correctly, and does not permit escape, so this struct is needed to get the compiler to accept our use. **/ #[repr(transparent)] struct AssertThreadsafe { inner: T, } impl AssertThreadsafe { fn new(inner: T) -> Self { Self { inner } } } unsafe impl Send for AssertThreadsafe { } unsafe impl Sync for AssertThreadsafe { } impl Deref for AssertThreadsafe { type Target = T; fn deref(&self) -> &Self::Target { &self.inner } } impl DerefMut for AssertThreadsafe { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.inner } } #[cfg(test)] mod tests { use super::*; use std::{ sync::atomic::{ AtomicUsize, Ordering::Relaxed, }, thread, time::Duration, }; #[test] fn trash_pickup() { static COUNTER: AtomicUsize = AtomicUsize::new(0); struct Deferrer(F); impl Drop for Deferrer { fn drop(&mut self) { (self.0)() } } let kept = Deferrer(|| { COUNTER.fetch_add(1, Relaxed); }); let sent = Deferrer(|| { COUNTER.fetch_add(1, Relaxed); }) .bg_drop(); drop(kept); drop(sent); while COUNTER.load(Relaxed) < 2 { thread::sleep(Duration::from_millis(100)); } } }