constant macro arrays
init
| -rw-r--r-- | .gitignore | 1 | ||||
| -rw-r--r-- | Cargo.toml | 16 | ||||
| -rw-r--r-- | LICENSE | 21 | ||||
| -rw-r--r-- | README.md | 14 | ||||
| -rw-r--r-- | src/lib.rs | 117 |
5 files changed, 169 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ffa3bbd --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +Cargo.lock
\ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..13da985 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "car" +version = "0.1.0" +authors = ["bend-n <[email protected]>"] +description = "std::array::{map, from_fn} in const" +edition = "2021" +repository = "https://github.com/bend-n/car" +license = "MIT" + +[dependencies] +proc-macro2 = "1.0.79" +quote = "1.0.35" +syn = { version = "2.0.55", features = ["full"] } + +[lib] +proc_macro = true @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 bendn + +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. diff --git a/README.md b/README.md new file mode 100644 index 0000000..35d66ce --- /dev/null +++ b/README.md @@ -0,0 +1,14 @@ +# `car` + +macro based array manipulation constant techniques + +## you wanted a quick LUT in const? here you go! + +```rust +// please note that this is in no way performant or a good idea. +let squares: [usize; 0xffffffff] = car::from_fn!(|i| i * 2); +``` + +## completely stable! + +for once!
\ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..7c211fc --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,117 @@ +//! provides some ([`map`](array::map) and [`from_fn`](core::array::from_fn)) [`core::array`] fn related functions as const macros. +//! ``` +//! const X: [usize; 6] = car::map!(car::from_fn!(|x| x), |x| x * 24); +//! ``` +#![forbid(unsafe_code)] +use quote::quote; +use syn::{parse::Parse, punctuated::Punctuated, spanned::Spanned, *}; + +/// [From fn](std::array::from_fn) in const. +/// ``` +/// const OUT: [u8; 8] = car::from_fn!(|x| (x as u32 * 611170012 >> 24) as u8); +/// assert_eq!(OUT, [0, 36, 72, 109, 145, 182, 218, 255]); +/// ``` + +#[proc_macro] +pub fn from_fn(input: proc_macro::TokenStream) -> proc_macro::TokenStream { + let ExprClosure { inputs, body, .. } = parse_macro_input!(input as ExprClosure); + if inputs.len() > 1 { + let n = inputs.len(); + return Error::new_spanned(inputs, format!("expected one (or 0) inputs, found {n}")) + .into_compile_error() + .into(); + }; + let inputs = inputs.into_iter(); + quote! { unsafe { + // dont worry about this whole area + const fn uninit_array<T, const N: usize>() -> [::core::mem::MaybeUninit<T>; N] { unsafe { ::core::mem::MaybeUninit::<[::core::mem::MaybeUninit<T>; N]>::uninit().assume_init() } } + const unsafe fn aai<T, const N: usize>(array: [::core::mem::MaybeUninit<T>; N]) -> [T; N] { transmute_unchecked(array) } + const unsafe fn transmute_unchecked<T, U>(value: T) -> U { unsafe { #[repr(C)] union Transmute<T, U> { t: ::core::mem::ManuallyDrop<T>, u: ::core::mem::ManuallyDrop<U> } ::core::mem::ManuallyDrop::into_inner(Transmute { t: ::core::mem::ManuallyDrop::new(value) }.u) } } + + let mut __out = uninit_array(); + let mut i = 0usize; + while i < __out.len() { + __out[i] = ::core::mem::MaybeUninit::new({ + // disable mutation (cant shadow) + let i = i; + let __out = (); + + #(let #inputs = i)*; + + #body + }); + i += 1; + } + aai(__out) + } }.into() +} + +/// [Map](array::map) in const. +/// ``` +/// assert_eq!(car::map!([1, 2, 3], |x| x * 2), [2, 4, 6]); +/// ``` +#[proc_macro] +pub fn map(input: proc_macro::TokenStream) -> proc_macro::TokenStream { + struct Args { + array: Expr, + f: ExprClosure, + } + impl Parse for Args { + fn parse(input: parse::ParseStream) -> Result<Self> { + let mut args = Punctuated::<Expr, Token![,]>::parse_terminated(input)?; + if args.len() != 2 { + return Err(input.error(format!( + "arg count mismatch, expected 2 (array expression) found {}", + args.len() + ))); + } + let f = match args.pop().unwrap().into_value() { + Expr::Closure(x) => x, + x => return Err(Error::new_spanned(x, "mismatched types: expected closure")), + }; + let array = args.pop().unwrap().into_value(); + + Ok(Self { array, f }) + } + } + + let Args { + array, + f: ExprClosure { body, inputs, .. }, + } = parse_macro_input!(input as Args); + let n = inputs.len(); + let ns = inputs.span(); + let mut inputs = inputs.into_iter(); + let Some(binding) = inputs.next() else { + return Error::new(ns, format!("expected one (or 2) inputs, found {n} inputs")) + .into_compile_error() + .into(); + }; + let index = inputs.next().into_iter(); + quote! { unsafe { + // dont worry about this whole area + const fn uninit_array<T, U, const N: usize>(/* steal the length */ _len: &[U; N]) -> [::core::mem::MaybeUninit<T>; N] { unsafe { ::core::mem::MaybeUninit::<[::core::mem::MaybeUninit<T>; N]>::uninit().assume_init() } } + const unsafe fn aai<T, const N: usize>(array: [::core::mem::MaybeUninit<T>; N]) -> [T; N] { transmute_unchecked(array) } + const unsafe fn transmute_unchecked<T, U>(value: T) -> U { unsafe { #[repr(C)] union Transmute<T, U> { t: ::core::mem::ManuallyDrop<T>, u: ::core::mem::ManuallyDrop<U> } ::core::mem::ManuallyDrop::into_inner(Transmute { t: ::core::mem::ManuallyDrop::new(value) }.u) } } + + let __arr = #array; + let mut __out = uninit_array(&__arr); + let size = __arr.len(); + let __ap = __arr.as_ptr(); + let __arr = ::core::mem::ManuallyDrop::new(__arr); + let mut i = 0usize; + while i < size { + __out[i] = ::core::mem::MaybeUninit::new({ + let i = i; + let #binding = __ap.add(i).read(); + let __out = (); + let __arr = (); + #(let #index = i;)* + + #body + }); + i += 1; + } + aai(__out) + }}.into() +} |