Skip to content

Commit

Permalink
Implement Atom & AtomLogic for small, pow2-sized integer arrays
Browse files Browse the repository at this point in the history
In theory we could implement `Atom` for `[T; N]` where `T` packs into
a representation with X bytes such that `N * X` is smaller than the
largest supported atomic size. However, implementing anything generic
like that is really tricky due to impl-overlap check and various other
issues. It would be really nice to support `[Foo; 2]` as long as `Foo`
implements `Atom<Repr = u8>`. But again, this is really tricky and if
possible at all, results in a bunch of helper traits. Adding this fixed
set of impls should cover the most important use cases.
  • Loading branch information
LukasKalbertodt committed Mar 3, 2024
1 parent f06caa4 commit 738841e
Show file tree
Hide file tree
Showing 2 changed files with 82 additions and 0 deletions.
65 changes: 65 additions & 0 deletions src/impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -429,6 +429,58 @@ macro_rules! impl_option_non_zero {
#[cfg(target_has_atomic = "ptr")] impl_option_non_zero!(NonZeroUsize = usize);
#[cfg(target_has_atomic = "ptr")] impl_option_non_zero!(NonZeroIsize = isize);

macro_rules! impl_int8_arrays {
($elem:ident, $len:literal, $repr:ident) => {
impl Atom for [$elem; $len] {
type Repr = $repr;
fn pack(self) -> Self::Repr {
$repr::from_ne_bytes(self.map(|e| e as _))
}
fn unpack(src: Self::Repr) -> Self {
src.to_ne_bytes().map(|e| e as _)
}
}
impl AtomLogic for [$elem; $len] {}
};
}

#[cfg(target_has_atomic = "16")] impl_int8_arrays!(u8, 2, u16);
#[cfg(target_has_atomic = "32")] impl_int8_arrays!(u8, 4, u32);
#[cfg(target_has_atomic = "64")] impl_int8_arrays!(u8, 8, u64);
#[cfg(target_has_atomic = "16")] impl_int8_arrays!(i8, 2, u16);
#[cfg(target_has_atomic = "32")] impl_int8_arrays!(i8, 4, u32);
#[cfg(target_has_atomic = "64")] impl_int8_arrays!(i8, 8, u64);

macro_rules! impl_int_arrays {
($unsigned_elem:ident, $signed_elem:ident, $len:literal, $repr:ident, $nested:tt, $flat:tt) => {
impl_small_primitive_array!($unsigned_elem, $len, $repr, $nested, $flat);
impl_small_primitive_array!($signed_elem, $len, $repr, $nested, $flat);
};
}

macro_rules! impl_small_primitive_array {
($elem:ident, $len:literal, $repr:ident, $nested:tt, $flat:tt) => {
impl Atom for [$elem; $len] {
type Repr = $repr;
fn pack(self) -> Self::Repr {
let $nested = self.map(|x| x.to_ne_bytes());
Self::Repr::from_ne_bytes($flat)
}
fn unpack(src: Self::Repr) -> Self {
let $flat = src.to_ne_bytes();
$nested.map($elem::from_ne_bytes)
}
}
impl AtomLogic for [$elem; $len] {}
};
}
#[cfg(target_has_atomic = "32")] impl_int_arrays!(u16, i16, 2, u32,
[[b0, b1], [b2, b3]], [b0, b1, b2, b3]);
#[cfg(target_has_atomic = "64")] impl_int_arrays!(u16, i16, 4, u64,
[[b0, b1], [b2, b3], [b4, b5], [b6, b7]], [b0, b1, b2, b3, b4, b5, b6, b7]);
#[cfg(target_has_atomic = "64")] impl_int_arrays!(u32, i32, 2, u64,
[[b0, b1, b2, b3], [b4, b5, b6, b7]], [b0, b1, b2, b3, b4, b5, b6, b7]);

/// This is just a dummy module to have doc tests.
///
/// ```
Expand Down Expand Up @@ -468,6 +520,19 @@ macro_rules! impl_option_non_zero {
/// assert_impl_atom::<f32>();
/// assert_impl_atom::<f64>();
///
/// assert_impl_atom::<[u8; 2]>();
/// assert_impl_atom::<[u8; 4]>();
/// assert_impl_atom::<[u8; 8]>();
/// assert_impl_atom::<[i8; 2]>();
/// assert_impl_atom::<[i8; 4]>();
/// assert_impl_atom::<[i8; 8]>();
/// assert_impl_atom::<[u16; 2]>();
/// assert_impl_atom::<[u16; 4]>();
/// assert_impl_atom::<[i16; 2]>();
/// assert_impl_atom::<[i16; 4]>();
/// assert_impl_atom::<[u32; 2]>();
/// assert_impl_atom::<[i32; 2]>();
///
/// assert_impl_atom::<core::num::NonZeroU8>();
/// assert_impl_atom::<core::num::NonZeroI8>();
/// assert_impl_atom::<core::num::NonZeroU16>();
Expand Down
17 changes: 17 additions & 0 deletions src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,23 @@ gen_tests_for_primitives!(_f32, f32, 7.0f32, 33.0f32, [n n]);
gen_tests_for_primitives!(_f64, f64, 7.0f64, 33.0f64, [n n]);
gen_tests_for_primitives!(_char, char, 'x', '♥', [n n]);

// Arrays. They do implement `AtomLogic` but the logic tests don't work with them.
// mod ty val0 val1 [logic int]
gen_tests_for_primitives!(_u8array2, [u8; 2], [3u8, 79], [17u8, 240], [n n]);
gen_tests_for_primitives!(_u8array4, [u8; 4], [3u8, 79, 13, 230], [17u8, 240, 59, 184], [n n]);
gen_tests_for_primitives!(_u8array8, [u8; 8], [3u8, 79, 13, 230, 4, 80, 14, 231],
[17u8, 240, 59, 184, 18, 241, 60, 185], [n n]);
gen_tests_for_primitives!(_u16array2, [u16; 2], [3u16, 257], [17u16, 9999], [n n]);
gen_tests_for_primitives!(_u16array4, [u16; 4], [3u16, 257, 13, 230], [17u16, 9999, 59, 17003], [n n]);
gen_tests_for_primitives!(_u32array2, [u32; 2], [3u32, 77977], [17u32, 190247], [n n]);
gen_tests_for_primitives!(_i8array2, [i8; 2], [3i8, -79], [-17i8, 113], [n n]);
gen_tests_for_primitives!(_i8array4, [i8; 4], [3i8, -79, 13, -120], [-17i8, 113, -59, -98], [n n]);
gen_tests_for_primitives!(_i8array8, [i8; 8], [3i8, -79, 13, -120, 4, 80, -14, 111],
[-17i8, 113, -59, -98, -18, 114, 60, -128], [n n]);
gen_tests_for_primitives!(_i16array2, [i16; 2], [3i16, -257], [17i16, -9999], [n n]);
gen_tests_for_primitives!(_i16array4, [i16; 4], [3i16, -257, 13, 230], [17i16, -9999, -59, 17003], [n n]);
gen_tests_for_primitives!(_i32array2, [i32; 2], [3i32, -77977], [-17i32, 190247], [n n]);

mod _ptr {
use super::*;
generic_tests!(Foo, Foo::Nothing, Foo::Set(0b101));
Expand Down

0 comments on commit 738841e

Please sign in to comment.