From f17c0402b7fa166193809bf7e722abe063e8fb49 Mon Sep 17 00:00:00 2001 From: Martin Habovstiak Date: Thu, 14 Dec 2023 17:21:37 +0100 Subject: [PATCH] Add tools that help with making code `const` There are various challenges when making code `const`: making it conditional, awkward copying of slices... This change adds tools that help dealing with these challenges. --- internals/src/const_tools.rs | 92 ++++++++++++++++++++++++++++++++++++ internals/src/lib.rs | 1 + 2 files changed, 93 insertions(+) create mode 100644 internals/src/const_tools.rs diff --git a/internals/src/const_tools.rs b/internals/src/const_tools.rs new file mode 100644 index 00000000..f475a23c --- /dev/null +++ b/internals/src/const_tools.rs @@ -0,0 +1,92 @@ +//! Contains tools (workarounds) to make implementing `const fn`s easier. + +/// Copies first `$len` bytes from `$slice` and returns them as an array. +/// +/// Returns `None` if `$len > $slice.len()`. `$len` must be (obviously) statically known. +/// Calling from non-const context doesn't affect performance. +#[macro_export] +macro_rules! copy_byte_array_from_slice { + ($slice:expr, $len:expr) => { + if $len > $slice.len() { + None + } else { + let mut array = [0u8; $len]; + // Note: produces same assemble as copy_from_slice + let mut i = 0; + while i < $len { + array[i] = $slice[i]; + i += 1; + } + Some(array) + } + }; +} +pub use copy_byte_array_from_slice; + +/// Concatenates two byte slices or byte arrays (or combination) to a single array. +/// +/// # Panics +/// +/// This macro panics if `$len` is not equal to the sum of `$a.len()` and `$b.len()`. +#[macro_export] +macro_rules! concat_bytes_to_arr { + ($a:expr, $b:expr, $len:expr) => {{ + // avoid repeated eval + let a = $a; + let b = $b; + + #[allow(unconditional_panic)] + let _ = [(); 1][($len != a.len() + b.len()) as usize]; + + let mut output = [0u8; $len]; + let mut i = 0; + while i < a.len() { + output[i] = $a[i]; + i += 1; + } + while i < a.len() + b.len() { + output[i] = b[i - a.len()]; + i += 1; + } + output + }}; +} +pub use concat_bytes_to_arr; + +#[macro_export] +/// Enables const fn in specified Rust version +macro_rules! cond_const { + ($($(#[$attr:meta])* $vis:vis const(in $ver:ident $(= $human_ver:literal)?) fn $name:ident$(<$($gen:tt)*>)?($($args:tt)*) $(-> $ret:ty)? $body:block)+ ) => { + $( + #[cfg($ver)] + $(#[$attr])* + $( + #[doc = "\nNote: the function is only `const` in Rust "] + #[doc = $human_ver] + #[doc = "."] + )? + $vis const fn $name$(<$($gen)*>)?($($args)*) $(-> $ret)? $body + + #[cfg(not($ver))] + $(#[$attr])* + $vis fn $name$(<$($gen)*>)?($($args)*) $(-> $ret)? $body + )+ + }; + ($($(#[$attr:meta])* $vis:vis const(in $ver:ident $(= $human_ver:literal)?) unsafe fn $name:ident$(<$($gen:tt)*>)?($($args:tt)*) $(-> $ret:ty)? $body:block)+ ) => { + $( + #[cfg($ver)] + $(#[$attr])* + $( + #[doc = "\nNote: the function is only `const` in Rust "] + #[doc = $human_ver] + #[doc = " and newer."] + )? + $vis const unsafe fn $name$(<$($gen)*>)?($($args)*) $(-> $ret)? $body + + #[cfg(not($ver))] + $(#[$attr])* + $vis unsafe fn $name$(<$($gen)*>)?($($args)*) $(-> $ret)? $body + )+ + }; +} +pub use cond_const; diff --git a/internals/src/lib.rs b/internals/src/lib.rs index b81da6b8..025fc553 100644 --- a/internals/src/lib.rs +++ b/internals/src/lib.rs @@ -20,6 +20,7 @@ extern crate alloc; #[cfg(feature = "std")] extern crate std; +pub mod const_tools; pub mod error; pub mod macros; mod parse;