From 019c860a96b38abdef5bfd559ad073e4ffd2b9a1 Mon Sep 17 00:00:00 2001 From: bolshoytoster Date: Tue, 1 Sep 2026 20:50:46 +0100 Subject: [PATCH 1/4] Add rayon support --- Cargo.toml | 1 + src/lib.rs | 2 ++ src/rayon.rs | 95 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 98 insertions(+) create mode 100644 src/rayon.rs diff --git a/Cargo.toml b/Cargo.toml index f453d3d0..f31e40d8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,6 +24,7 @@ arbitrary = { version = "1.4", optional = true, default-features = false } borsh = { version = "1.8", optional = true, features = ["derive", "unstable__schema"], default-features = false } bytes = { version = "1.12", optional = true, default-features = false } defmt = { version = "1.1", optional = true, default-features = false } +rayon = { version = "1", optional = true } serde_core = { version = "1.0", optional = true, default-features = false } malloc_size_of = { version = "0.1", optional = true, default-features = false } diff --git a/src/lib.rs b/src/lib.rs index f8133c14..16d7a027 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -71,6 +71,8 @@ mod macros; #[cfg(feature = "malloc_size_of")] mod mallocsizeof; mod rawsmallvec; +#[cfg(feature = "rayon")] +mod rayon; mod references; #[cfg(feature = "serde")] mod serde; diff --git a/src/rayon.rs b/src/rayon.rs new file mode 100644 index 00000000..7a37a448 --- /dev/null +++ b/src/rayon.rs @@ -0,0 +1,95 @@ +//! The code is heavily inspired by `rayon/vec.rs`, since it's exactly what we +//! need, except it's all private + +use { + crate::SmallVec, + core::{ + mem::take, + ptr::{ + self, + drop_in_place + }, + slice + }, + rayon::{ + iter::plumbing::{ + Producer, + UnindexedConsumer, + bridge_producer_consumer + }, + prelude::ParallelIterator + } +}; + +pub(crate) struct SliceDrain<'a, T>(slice::IterMut<'a, T>); + +impl Iterator for SliceDrain<'_, T> { + type Item = T; + + fn next(&mut self) -> Option { + self.0.next().map(|val| unsafe { ptr::read(val) }) + } +} + +impl DoubleEndedIterator for SliceDrain<'_, T> { + fn next_back(&mut self) -> Option { + self.0.next_back().map(|val| unsafe { ptr::read(val) }) + } +} + +impl ExactSizeIterator for SliceDrain<'_, T> { + fn len(&self) -> usize { + self.0.len() + } +} + +impl Drop for SliceDrain<'_, T> { + fn drop(&mut self) { + unsafe { drop_in_place(take(&mut self.0).into_slice()) }; + } +} + +struct DrainProducer<'a, T>(&'a mut [T]); + +impl<'a, T: Send> Producer for DrainProducer<'a, T> { + type IntoIter = SliceDrain<'a, T>; + type Item = T; + + fn into_iter(mut self) -> SliceDrain<'a, T> { + SliceDrain(take(&mut self.0).iter_mut()) + } + + fn split_at(mut self, index: usize) -> (Self, Self) { + let (left, right) = take(&mut self.0).split_at_mut(index); + + (DrainProducer(left), DrainProducer(right)) + } +} + +impl Drop for DrainProducer<'_, T> { + fn drop(&mut self) { + unsafe { drop_in_place(self.0) }; + } +} + +impl ParallelIterator for SmallVec { + type Item = T; + + fn drive_unindexed>(mut self, consumer: C) -> C::Result { + let len = self.len(); + + bridge_producer_consumer( + len, + DrainProducer(unsafe { + // SAFETY: set_len(0) is always valid + // All items will either be passed out or dropped by DrainProducer/SliceDrop, so + // there shouldn't be any possibility for leakage + self.set_len(0); + + // SAFETY: set_len didn't deallocate/drop the elements, so they are still valid. + slice::from_raw_parts_mut(self.as_mut_ptr(), len) + }), + consumer + ) + } +} From 53a19e29f4ce27ada382081cfaef95a7637c0b35 Mon Sep 17 00:00:00 2001 From: bolshoytoster Date: Tue, 1 Sep 2026 22:50:46 +0100 Subject: [PATCH 2/4] Add rayon test + new formatting --- Cargo.toml | 4 ++++ src/rayon.rs | 10 ++++++---- tests/rayon.rs | 19 +++++++++++++++++++ 3 files changed, 29 insertions(+), 4 deletions(-) create mode 100644 tests/rayon.rs diff --git a/Cargo.toml b/Cargo.toml index f31e40d8..a32ded76 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -40,6 +40,10 @@ required-features = ["arbitrary"] name = "bytes" required-features = ["bytes"] +[[test]] +name = "rayon" +required-features = ["rayon"] + [[test]] name = "serde" required-features = ["serde"] diff --git a/src/rayon.rs b/src/rayon.rs index 7a37a448..2440f9fe 100644 --- a/src/rayon.rs +++ b/src/rayon.rs @@ -21,7 +21,7 @@ use { } }; -pub(crate) struct SliceDrain<'a, T>(slice::IterMut<'a, T>); +struct SliceDrain<'a, T>(slice::IterMut<'a, T>); impl Iterator for SliceDrain<'_, T> { type Item = T; @@ -82,11 +82,13 @@ impl ParallelIterator for SmallVec { len, DrainProducer(unsafe { // SAFETY: set_len(0) is always valid - // All items will either be passed out or dropped by DrainProducer/SliceDrop, so - // there shouldn't be any possibility for leakage + // All items will either be passed out or dropped by + // DrainProducer/SliceDrop, so there shouldn't + // be any possibility for leakage self.set_len(0); - // SAFETY: set_len didn't deallocate/drop the elements, so they are still valid. + // SAFETY: set_len didn't deallocate/drop the elements, so they + // are still valid. slice::from_raw_parts_mut(self.as_mut_ptr(), len) }), consumer diff --git a/tests/rayon.rs b/tests/rayon.rs new file mode 100644 index 00000000..42ff3ea9 --- /dev/null +++ b/tests/rayon.rs @@ -0,0 +1,19 @@ +use { + rayon::{ + iter::ParallelIterator, + prelude::ParallelSlice + }, + smallvec::SmallVec +}; + +#[test] +fn rayon() { + assert_eq!( + [0, 1, 2, 3] + .par_chunks(2) + .map(SmallVec::::from) + .flatten() + .collect::>(), + [0, 1, 2, 3] + ); +} From 24e412d93a7bbffc656e5686a46f9a866211d83b Mon Sep 17 00:00:00 2001 From: bolshoytoster Date: Wed, 2 Sep 2026 00:09:58 +0100 Subject: [PATCH 3/4] Exempt rayon test from miri --- tests/rayon.rs | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/tests/rayon.rs b/tests/rayon.rs index 42ff3ea9..ed6266a7 100644 --- a/tests/rayon.rs +++ b/tests/rayon.rs @@ -1,13 +1,14 @@ -use { - rayon::{ - iter::ParallelIterator, - prelude::ParallelSlice - }, - smallvec::SmallVec -}; - +#[cfg(not(miri))] #[test] fn rayon() { + use { + rayon::{ + iter::ParallelIterator, + prelude::ParallelSlice + }, + smallvec::SmallVec + }; + assert_eq!( [0, 1, 2, 3] .par_chunks(2) From ecf5379c5203af8e3285152b6432ec1ff2410e53 Mon Sep 17 00:00:00 2001 From: bolshoytoster Date: Wed, 2 Sep 2026 00:20:24 +0100 Subject: [PATCH 4/4] Add explicit minor version for rayon, and move use to module level --- Cargo.toml | 2 +- tests/rayon.rs | 19 ++++++++++--------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index a32ded76..161317d1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,7 +24,7 @@ arbitrary = { version = "1.4", optional = true, default-features = false } borsh = { version = "1.8", optional = true, features = ["derive", "unstable__schema"], default-features = false } bytes = { version = "1.12", optional = true, default-features = false } defmt = { version = "1.1", optional = true, default-features = false } -rayon = { version = "1", optional = true } +rayon = { version = "1.12", optional = true, default-features = false } serde_core = { version = "1.0", optional = true, default-features = false } malloc_size_of = { version = "0.1", optional = true, default-features = false } diff --git a/tests/rayon.rs b/tests/rayon.rs index ed6266a7..f367b7e9 100644 --- a/tests/rayon.rs +++ b/tests/rayon.rs @@ -1,14 +1,15 @@ -#[cfg(not(miri))] +#![cfg(not(miri))] + +use { + rayon::{ + iter::ParallelIterator, + prelude::ParallelSlice + }, + smallvec::SmallVec +}; + #[test] fn rayon() { - use { - rayon::{ - iter::ParallelIterator, - prelude::ParallelSlice - }, - smallvec::SmallVec - }; - assert_eq!( [0, 1, 2, 3] .par_chunks(2)