From a42f3fc66d0f60615d29b9f53cb74d45773f7219 Mon Sep 17 00:00:00 2001 From: Alex Konradi Date: Fri, 22 Nov 2024 13:35:11 -0500 Subject: [PATCH 1/4] Correctly use `const_panic` feature in this crate Indirect the checking of size through a function that is evaluated in this crate, not in the crate that is invoking the macro. This ensures that the declared `const_panic` feature is correctly evaluated as a feature of this crate, not the consuming crate. --- src/lib.rs | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 10e86d2..69a3c77 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -41,11 +41,7 @@ macro_rules! concat_arrays { impl ArrayConcatComposed { const HAVE_SAME_SIZE: bool = core::mem::size_of::<[T; N]>() == core::mem::size_of::(); - #[cfg(feature="const_panic")] - const PANIC: bool = Self::HAVE_SAME_SIZE || panic!("Size Mismatch"); - - #[cfg(not(feature="const_panic"))] - const PANIC: bool = !["Size mismatch"][!Self::HAVE_SAME_SIZE as usize].is_empty(); + const PANIC: bool = $crate::_const_assert_same_size::<[T; N], Self>(); #[inline(always)] const fn have_same_size(&self) -> bool { @@ -67,6 +63,25 @@ macro_rules! concat_arrays { }); } +/// Assert at compile time that these types have the same size. +/// +/// This is an implementation detail of the crate and should only be used by the +/// macros in this crate. +#[doc(hidden)] +pub const fn _const_assert_same_size() -> bool { + let have_same_size = core::mem::size_of::() == core::mem::size_of::(); + + #[cfg(feature = "const_panic")] + { + return have_same_size || panic!("Size Mismatch"); + } + + #[cfg(not(feature = "const_panic"))] + { + return !["Size mismatch"][!have_same_size as usize].is_empty(); + } +} + #[allow(dead_code)] #[cfg(test)] mod tests { From 571f689643a4a787d670a76f6789fbea26a09e87 Mon Sep 17 00:00:00 2001 From: Daniel Bloom Date: Fri, 21 Feb 2025 18:12:07 -0800 Subject: [PATCH 2/4] feat: `split_array` --- README.md | 7 ++- src/lib.rs | 130 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 134 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index a74df35..609aafb 100644 --- a/README.md +++ b/README.md @@ -3,14 +3,16 @@ # array-concat -Macros for concatenating arrays. +Macros for concatenating and splitting arrays. To add to your Cargo.toml: + ```toml array-concat = "0.5.3" ``` ## Example + ```rust use array_concat::*; @@ -31,6 +33,9 @@ fn main() { assert_eq!([1, 2, 3, 4, 5], c); assert_eq!([S(true), S(false)], F); + // Split the array into three parts with lengths: 1, 3, and 1 + assert_eq!(([1], [2, 3, 4], [5]), split_array!(c, 1, 3, 1)); + let a = [1, 2, 3]; let b = [4, 5]; let c = concat_arrays!(a, b); // Size is inferred by the assert below diff --git a/src/lib.rs b/src/lib.rs index 69a3c77..ce7a724 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -39,8 +39,6 @@ macro_rules! concat_arrays { } impl ArrayConcatComposed { - const HAVE_SAME_SIZE: bool = core::mem::size_of::<[T; N]>() == core::mem::size_of::(); - const PANIC: bool = $crate::_const_assert_same_size::<[T; N], Self>(); #[inline(always)] @@ -63,10 +61,122 @@ macro_rules! concat_arrays { }); } +/// Flatten a nested tuple based on the number of nestings. +/// +/// This is an implementation detail of the crate and should only be used by the +/// macros in this crate. +#[macro_export] +#[doc(hidden)] +macro_rules! flatten_split { + (($($tail:tt)*), $head:expr, $pop:expr) => { + ($head, $($tail)*) + }; + // We can dramatically reduce macro recursion by adding an additional_case + (($($tail:tt)*), $head:expr, $pop1:expr, $pop2:expr$(, $remaining:expr)+) => { + flatten_split!( + ($head.1.2, $head.2, $($tail)*), + $head.1.1$(, + $remaining)+ + ) + }; + (($($tail:tt)*), $head:expr, $pop:expr$(, $remaining:expr)+) => { + flatten_split!( + ($head.2, $($tail)*), + $head.1$(, + $remaining)+ + ) + }; +} + +/// Split the provided array into the specified sizes. +#[macro_export] +macro_rules! split_array { + ($array:expr, $size:expr) => ($array); + ($array:expr, $size0:expr, $($sizes:expr),+) => ({ + struct ArrayConcatDecomposedMarkerBase(core::marker::PhantomData<(T, A)>); + struct ArrayConcatDecomposedMarker(core::marker::PhantomData<(T, A, B)>); + + #[repr(C)] + struct ArrayConcatDecomposed([T; 0], A, B); + + trait Storage { + type Data; + } + impl Storage for [T; A] { + type Data = [T; A]; + } + + impl ArrayConcatDecomposedMarkerBase { + #[inline(always)] + const fn default(_: &[T]) -> Self { + Self(core::marker::PhantomData) + } + #[inline(always)] + const fn concat(self, _: [(); N]) -> ArrayConcatDecomposedMarkerBase { + ArrayConcatDecomposedMarkerBase(core::marker::PhantomData) + } + } + impl ArrayConcatDecomposedMarkerBase { + #[inline(always)] + const fn concat(self, _: [(); B]) -> ArrayConcatDecomposedMarker { + ArrayConcatDecomposedMarker(core::marker::PhantomData) + } + } + + impl Storage for ArrayConcatDecomposedMarker { + type Data = ArrayConcatDecomposed; + } + + impl ArrayConcatDecomposedMarker { + #[inline(always)] + const fn concat(self, _: [(); C]) -> ArrayConcatDecomposedMarker, [T; C]> { + ArrayConcatDecomposedMarker(core::marker::PhantomData) + } + #[inline(always)] + const fn make(self, full: [T; N]) -> ArrayConcatDecomposed { + #[repr(C)] + union ArrayConcatComposed { + full: core::mem::ManuallyDrop<[T; N]>, + decomposed: core::mem::ManuallyDrop>, + } + + impl ArrayConcatComposed { + const PANIC: bool = $crate::_const_assert_same_size::<[T; N], Self>(); + + #[inline(always)] + const fn have_same_size(&self) -> bool { + Self::PANIC + } + } + + let composed = ArrayConcatComposed:: { + full: core::mem::ManuallyDrop::new(full) + }; + + // Sanity check that composed's two fields are the same size + composed.have_same_size(); + + // SAFETY: Sizes of both fields in composed are the same so this assignment should be sound + core::mem::ManuallyDrop::into_inner(unsafe { composed.decomposed }) + } + } + + + let array = $array; + let decomposed = ArrayConcatDecomposedMarkerBase::default(&array) + .concat([(); $size0]) + $(.concat([(); $sizes])) + *.make(array); + + $crate::flatten_split!((), decomposed, $size0$(, $sizes)*) + }); +} + /// Assert at compile time that these types have the same size. /// /// This is an implementation detail of the crate and should only be used by the /// macros in this crate. +#[inline(always)] #[doc(hidden)] pub const fn _const_assert_same_size() -> bool { let have_same_size = core::mem::size_of::() == core::mem::size_of::(); @@ -99,6 +209,22 @@ mod tests { assert_eq!([1, 2, 3, 4, 5, 6], d); } + #[test] + fn test_simple_split() { + let d: [u32; 6] = concat_arrays!(A, B); + const D: [u32; 6] = concat_arrays!(A, B); + + const A_B: ([u32; 3], [u32; 3]) = split_array!(D, A.len(), B.len()); + + assert_eq!((A, B), A_B); + assert_eq!((A, B), split_array!(d, 3, 3)); + assert_eq!(([1], [2, 3, 4, 5, 6]), split_array!(d, 1, 5)); + assert_eq!(([1, 2, 3, 4, 5], [6]), split_array!(d, 5, 1)); + assert_eq!(([1], [2, 3, 4, 5], [6]), split_array!(d, 1, 4, 1)); + assert_eq!(([1], [2, 3], [4, 5, 6]), split_array!(d, 1, 2, 3)); + assert_eq!(([1], [2], [3], [4], [5], [6]), split_array!(d, 1, 1, 1, 1, 1, 1)); + } + #[test] fn test_different_sizes() { let e = concat_arrays!(A, C); From 4610e1389a021da9f595c17eaae0118344d7789e Mon Sep 17 00:00:00 2001 From: Daniel Bloom Date: Fri, 21 Feb 2025 18:16:44 -0800 Subject: [PATCH 3/4] fmt --- src/lib.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index ce7a724..7f364dc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -222,7 +222,10 @@ mod tests { assert_eq!(([1, 2, 3, 4, 5], [6]), split_array!(d, 5, 1)); assert_eq!(([1], [2, 3, 4, 5], [6]), split_array!(d, 1, 4, 1)); assert_eq!(([1], [2, 3], [4, 5, 6]), split_array!(d, 1, 2, 3)); - assert_eq!(([1], [2], [3], [4], [5], [6]), split_array!(d, 1, 1, 1, 1, 1, 1)); + assert_eq!( + ([1], [2], [3], [4], [5], [6]), + split_array!(d, 1, 1, 1, 1, 1, 1) + ); } #[test] From 1e58159a5f2a1141bd1de02cf8e6d08b20575ea2 Mon Sep 17 00:00:00 2001 From: Daniel Bloom Date: Fri, 21 Feb 2025 19:32:10 -0800 Subject: [PATCH 4/4] fix --- src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 7f364dc..095d57e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -73,14 +73,14 @@ macro_rules! flatten_split { }; // We can dramatically reduce macro recursion by adding an additional_case (($($tail:tt)*), $head:expr, $pop1:expr, $pop2:expr$(, $remaining:expr)+) => { - flatten_split!( + $crate::flatten_split!( ($head.1.2, $head.2, $($tail)*), $head.1.1$(, $remaining)+ ) }; (($($tail:tt)*), $head:expr, $pop:expr$(, $remaining:expr)+) => { - flatten_split!( + $crate::flatten_split!( ($head.2, $($tail)*), $head.1$(, $remaining)+