rust-for-linux.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Danilo Krummrich <dakr@kernel.org>
To: ojeda@kernel.org, alex.gaynor@gmail.com, wedsonaf@gmail.com,
	boqun.feng@gmail.com, gary@garyguo.net, bjorn3_gh@protonmail.com,
	benno.lossin@proton.me, a.hindborg@samsung.com,
	aliceryhl@google.com, akpm@linux-foundation.org
Cc: daniel.almeida@collabora.com, faith.ekstrand@collabora.com,
	boris.brezillon@collabora.com, lina@asahilina.net,
	mcanal@igalia.com, zhiw@nvidia.com, acurrid@nvidia.com,
	cjia@nvidia.com, jhubbard@nvidia.com, airlied@redhat.com,
	ajanulgu@redhat.com, lyude@redhat.com,
	linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org,
	linux-mm@kvack.org, Danilo Krummrich <dakr@kernel.org>
Subject: [PATCH v4 17/28] rust: alloc: implement `collect` for `IntoIter`
Date: Mon,  5 Aug 2024 17:19:36 +0200	[thread overview]
Message-ID: <20240805152004.5039-18-dakr@kernel.org> (raw)
In-Reply-To: <20240805152004.5039-1-dakr@kernel.org>

Currently, we can't implement `FromIterator`. There are a couple of
issues with this trait in the kernel, namely:

  - Rust's specialization feature is unstable. This prevents us to
    optimze for the special case where `I::IntoIter` equals `Vec`'s
    `IntoIter` type.
  - We also can't use `I::IntoIter`'s type ID either to work around this,
    since `FromIterator` doesn't require this type to be `'static`.
  - `FromIterator::from_iter` does return `Self` instead of
    `Result<Self, AllocError>`, hence we can't properly handle allocation
    failures.
  - Neither `Iterator::collect` nor `FromIterator::from_iter` can handle
    additional allocation flags.

Instead, provide `IntoIter::collect`, such that we can at least convert
`IntoIter` into a `Vec` again.

Reviewed-by: Alice Ryhl <aliceryhl@google.com>
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
 rust/kernel/alloc/kvec.rs | 78 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/rust/kernel/alloc/kvec.rs b/rust/kernel/alloc/kvec.rs
index 51426e0b28e0..160c6b3868fe 100644
--- a/rust/kernel/alloc/kvec.rs
+++ b/rust/kernel/alloc/kvec.rs
@@ -644,6 +644,84 @@ impl<T, A> IntoIter<T, A>
     fn as_raw_mut_slice(&mut self) -> *mut [T] {
         ptr::slice_from_raw_parts_mut(self.ptr, self.len)
     }
+
+    fn into_raw_parts(self) -> (*mut T, NonNull<T>, usize, usize) {
+        let me = ManuallyDrop::new(self);
+        let ptr = me.ptr;
+        let buf = me.buf;
+        let len = me.len;
+        let cap = me.cap;
+        (ptr, buf, len, cap)
+    }
+
+    /// Same as `Iterator::collect` but specialized for `Vec`'s `IntoIter`.
+    ///
+    /// Currently, we can't implement `FromIterator`. There are a couple of issues with this trait
+    /// in the kernel, namely:
+    ///
+    /// - Rust's specialization feature is unstable. This prevents us to optimze for the special
+    ///   case where `I::IntoIter` equals `Vec`'s `IntoIter` type.
+    /// - We also can't use `I::IntoIter`'s type ID either to work around this, since `FromIterator`
+    ///   doesn't require this type to be `'static`.
+    /// - `FromIterator::from_iter` does return `Self` instead of `Result<Self, AllocError>`, hence
+    ///   we can't properly handle allocation failures.
+    /// - Neither `Iterator::collect` nor `FromIterator::from_iter` can handle additional allocation
+    ///   flags.
+    ///
+    /// Instead, provide `IntoIter::collect`, such that we can at least convert a `IntoIter` into a
+    /// `Vec` again.
+    ///
+    /// Note that `IntoIter::collect` doesn't require `Flags`, since it re-uses the existing backing
+    /// buffer. However, this backing buffer may be shrunk to the actual count of elements.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// let v = kernel::kvec![1, 2, 3]?;
+    /// let mut it = v.into_iter();
+    ///
+    /// assert_eq!(it.next(), Some(1));
+    ///
+    /// let v = it.collect(GFP_KERNEL);
+    /// assert_eq!(v, [2, 3]);
+    ///
+    /// # Ok::<(), Error>(())
+    /// ```
+    pub fn collect(self, flags: Flags) -> Vec<T, A> {
+        let (mut ptr, buf, len, mut cap) = self.into_raw_parts();
+        let has_advanced = ptr != buf.as_ptr();
+
+        if has_advanced {
+            // SAFETY: Copy the contents we have advanced to at the beginning of the buffer.
+            // `ptr` is guaranteed to be between `buf` and `buf.add(cap)` and `ptr.add(len)` is
+            // guaranteed to be smaller than `buf.add(cap)`.
+            unsafe { ptr::copy(ptr, buf.as_ptr(), len) };
+            ptr = buf.as_ptr();
+        }
+
+        // This can never fail, `len` is guaranteed to be smaller than `cap`.
+        let layout = core::alloc::Layout::array::<T>(len).unwrap();
+
+        // SAFETY: `buf` points to the start of the backing buffer and `len` is guaranteed to be
+        // smaller than `cap`. Depending on `alloc` this operation may shrink the buffer or leaves
+        // it as it is.
+        ptr = match unsafe { A::realloc(Some(buf.cast()), layout, flags) } {
+            // If we fail to shrink, which likely can't even happen, continue with the existing
+            // buffer.
+            Err(_) => ptr,
+            Ok(ptr) => {
+                cap = len;
+                ptr.as_ptr().cast()
+            }
+        };
+
+        // SAFETY: If the iterator has been advanced, the advanced elements have been copied to
+        // the beginning of the buffer and `len` has been adjusted accordingly. `ptr` is guaranteed
+        // to point to the start of the backing buffer. `cap` is either the original capacity or,
+        // after shrinking the buffer, equal to `len`. `alloc` is guaranteed to be unchanged since
+        // `into_iter` has been called on the original `Vec`.
+        unsafe { Vec::from_raw_parts(ptr, len, cap) }
+    }
 }
 
 impl<T, A> Iterator for IntoIter<T, A>
-- 
2.45.2


  parent reply	other threads:[~2024-08-05 15:21 UTC|newest]

Thread overview: 100+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-08-05 15:19 [PATCH v4 00/28] Generic `Allocator` support for Rust Danilo Krummrich
2024-08-05 15:19 ` [PATCH v4 01/28] rust: alloc: add `Allocator` trait Danilo Krummrich
2024-08-06 15:00   ` Alice Ryhl
2024-08-06 16:03   ` Benno Lossin
2024-08-06 18:30     ` Danilo Krummrich
2024-08-06 20:04       ` Benno Lossin
2024-08-07  9:36         ` Danilo Krummrich
2024-08-07 20:00           ` Benno Lossin
2024-08-07 18:19   ` Gary Guo
2024-08-05 15:19 ` [PATCH v4 02/28] rust: alloc: separate `aligned_size` from `krealloc_aligned` Danilo Krummrich
2024-08-06 16:06   ` Benno Lossin
2024-08-05 15:19 ` [PATCH v4 03/28] rust: alloc: rename `KernelAllocator` to `Kmalloc` Danilo Krummrich
2024-08-06 16:07   ` Benno Lossin
2024-08-07 18:22   ` Gary Guo
2024-08-05 15:19 ` [PATCH v4 04/28] rust: alloc: implement `Allocator` for `Kmalloc` Danilo Krummrich
2024-08-06 16:51   ` Benno Lossin
2024-08-06 18:55     ` Danilo Krummrich
2024-08-07  7:14       ` Benno Lossin
2024-08-07 10:11         ` Danilo Krummrich
2024-08-07 20:15           ` Benno Lossin
2024-08-07 23:05             ` Danilo Krummrich
2024-08-08  8:55               ` Benno Lossin
2024-08-08  9:02                 ` Benno Lossin
2024-08-08  9:42                 ` Danilo Krummrich
2024-08-05 15:19 ` [PATCH v4 05/28] rust: alloc: add module `allocator_test` Danilo Krummrich
2024-08-06 16:54   ` Benno Lossin
2024-08-06 18:58     ` Danilo Krummrich
2024-08-07  7:20       ` Benno Lossin
2024-08-07 10:16         ` Danilo Krummrich
2024-08-05 15:19 ` [PATCH v4 06/28] rust: alloc: implement `Vmalloc` allocator Danilo Krummrich
2024-08-06 17:00   ` Benno Lossin
2024-08-06 19:01     ` Danilo Krummrich
2024-08-07  7:23       ` Benno Lossin
2024-08-05 15:19 ` [PATCH v4 07/28] rust: alloc: implement `KVmalloc` allocator Danilo Krummrich
2024-08-05 15:19 ` [PATCH v4 08/28] rust: types: implement `Unique<T>` Danilo Krummrich
2024-08-06 17:22   ` Benno Lossin
2024-08-06 17:28     ` Miguel Ojeda
2024-08-06 23:16       ` Danilo Krummrich
2024-08-06 23:12     ` Danilo Krummrich
2024-08-07  7:27       ` Benno Lossin
2024-08-07 10:13         ` Danilo Krummrich
2024-08-05 15:19 ` [PATCH v4 09/28] rust: alloc: implement kernel `Box` Danilo Krummrich
2024-08-06 19:47   ` Benno Lossin
2024-08-06 23:01     ` Danilo Krummrich
2024-08-07  7:49       ` Benno Lossin
2024-08-07  7:51         ` Alice Ryhl
2024-08-07  8:01           ` Benno Lossin
2024-08-07 10:44             ` Danilo Krummrich
2024-08-07 10:38         ` Danilo Krummrich
2024-08-07 19:45           ` Benno Lossin
2024-08-08 17:44         ` Danilo Krummrich
2024-08-08 19:44           ` Benno Lossin
2024-08-05 15:19 ` [PATCH v4 10/28] rust: treewide: switch to our kernel `Box` type Danilo Krummrich
2024-08-07 12:42   ` Alice Ryhl
2024-08-07 20:57   ` Benno Lossin
2024-08-07 21:16     ` Benno Lossin
2024-08-07 23:08     ` Danilo Krummrich
2024-08-05 15:19 ` [PATCH v4 11/28] rust: alloc: remove `BoxExt` extension Danilo Krummrich
2024-08-08  6:48   ` Benno Lossin
2024-08-05 15:19 ` [PATCH v4 12/28] rust: alloc: add `Box` to prelude Danilo Krummrich
2024-08-08  6:49   ` Benno Lossin
2024-08-05 15:19 ` [PATCH v4 13/28] rust: alloc: import kernel `Box` type in types.rs Danilo Krummrich
2024-08-05 15:19 ` [PATCH v4 14/28] rust: alloc: import kernel `Box` type in init.rs Danilo Krummrich
2024-08-05 15:19 ` [PATCH v4 15/28] rust: alloc: implement kernel `Vec` type Danilo Krummrich
2024-08-05 15:19 ` [PATCH v4 16/28] rust: alloc: implement `IntoIterator` for `Vec` Danilo Krummrich
2024-08-05 15:19 ` Danilo Krummrich [this message]
2024-08-05 15:19 ` [PATCH v4 18/28] rust: treewide: switch to the kernel `Vec` type Danilo Krummrich
2024-08-07 13:47   ` Alice Ryhl
2024-08-08  9:08   ` Benno Lossin
2024-08-05 15:19 ` [PATCH v4 19/28] rust: alloc: remove `VecExt` extension Danilo Krummrich
2024-08-07 12:42   ` Alice Ryhl
2024-08-08  9:14   ` Benno Lossin
2024-08-05 15:19 ` [PATCH v4 20/28] rust: alloc: add `Vec` to prelude Danilo Krummrich
2024-08-07 13:55   ` Alice Ryhl
2024-08-08  8:40   ` Benno Lossin
2024-08-05 15:19 ` [PATCH v4 21/28] rust: alloc: remove `GlobalAlloc` and `krealloc_aligned` Danilo Krummrich
2024-08-06 19:07   ` Björn Roy Baron
2024-08-06 21:14     ` Miguel Ojeda
2024-08-07 21:23   ` Benno Lossin
2024-08-07 23:16     ` Danilo Krummrich
2024-08-05 15:19 ` [PATCH v4 22/28] rust: error: use `core::alloc::LayoutError` Danilo Krummrich
2024-08-07 13:55   ` Alice Ryhl
2024-08-08  7:42   ` Benno Lossin
2024-08-05 15:19 ` [PATCH v4 23/28] rust: error: check for config `test` in `Error::name` Danilo Krummrich
2024-08-07 13:54   ` Alice Ryhl
2024-08-05 15:19 ` [PATCH v4 24/28] rust: alloc: implement `contains` for `Flags` Danilo Krummrich
2024-08-07 13:53   ` Alice Ryhl
2024-08-05 15:19 ` [PATCH v4 25/28] rust: alloc: implement `Cmalloc` in module allocator_test Danilo Krummrich
2024-08-08  9:35   ` Benno Lossin
2024-08-08 10:07     ` Danilo Krummrich
2024-08-05 15:19 ` [PATCH v4 26/28] rust: str: test: replace `alloc::format` Danilo Krummrich
2024-08-07 13:51   ` Alice Ryhl
2024-08-08  7:22   ` Benno Lossin
2024-08-12 13:07     ` Danilo Krummrich
2024-08-05 15:19 ` [PATCH v4 27/28] rust: alloc: update module comment of alloc.rs Danilo Krummrich
2024-08-07 13:50   ` Alice Ryhl
2024-08-08  6:59   ` Benno Lossin
2024-08-05 15:19 ` [PATCH v4 28/28] kbuild: rust: remove the `alloc` crate Danilo Krummrich
2024-08-08  6:58   ` Benno Lossin
2024-08-08  9:45   ` Benno Lossin

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20240805152004.5039-18-dakr@kernel.org \
    --to=dakr@kernel.org \
    --cc=a.hindborg@samsung.com \
    --cc=acurrid@nvidia.com \
    --cc=airlied@redhat.com \
    --cc=ajanulgu@redhat.com \
    --cc=akpm@linux-foundation.org \
    --cc=alex.gaynor@gmail.com \
    --cc=aliceryhl@google.com \
    --cc=benno.lossin@proton.me \
    --cc=bjorn3_gh@protonmail.com \
    --cc=boqun.feng@gmail.com \
    --cc=boris.brezillon@collabora.com \
    --cc=cjia@nvidia.com \
    --cc=daniel.almeida@collabora.com \
    --cc=faith.ekstrand@collabora.com \
    --cc=gary@garyguo.net \
    --cc=jhubbard@nvidia.com \
    --cc=lina@asahilina.net \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-mm@kvack.org \
    --cc=lyude@redhat.com \
    --cc=mcanal@igalia.com \
    --cc=ojeda@kernel.org \
    --cc=rust-for-linux@vger.kernel.org \
    --cc=wedsonaf@gmail.com \
    --cc=zhiw@nvidia.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).