From 2734112253d7fd0028f08aa20d702f4821b0f151 Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Sat, 8 Jun 2019 21:53:51 -0700 Subject: [PATCH] Add Vec::flatten (as unstable, of course) This has been a common question on IRC for years -- with people wanting to flatten Vecs of `[u8; 4]` colours or `[f32; 3]` linalg vectors. I'm irrationally excited that const generics has a reached a state where this works now. (Named after `Iterator::flatten`, which is the same conceptual operation, just slower and doesn't currently work right with array `Item`s.) --- src/liballoc/lib.rs | 1 + src/liballoc/vec.rs | 79 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+) diff --git a/src/liballoc/lib.rs b/src/liballoc/lib.rs index bfc008e14a48..6a9af626e2f3 100644 --- a/src/liballoc/lib.rs +++ b/src/liballoc/lib.rs @@ -77,6 +77,7 @@ #![feature(box_syntax)] #![feature(cfg_target_has_atomic)] #![feature(coerce_unsized)] +#![cfg_attr(not(bootstrap), feature(const_generics))] #![feature(dispatch_from_dyn)] #![feature(core_intrinsics)] #![feature(custom_attribute)] diff --git a/src/liballoc/vec.rs b/src/liballoc/vec.rs index 92fe0834dd02..dc5c25583a32 100644 --- a/src/liballoc/vec.rs +++ b/src/liballoc/vec.rs @@ -1369,6 +1369,85 @@ impl Vec { } } +#[cfg(not(bootstrap))] +impl Vec<[T; N]> { + /// Flatten a `Vec<[T; N]>` into an N-times longer `Vec`. + /// + /// This is O(1) and non-allocating. + /// + /// # Panics + /// + /// If the length of the result would be too large to store in a `usize`, + /// which can only happen if `T` is a zero-sized type. + /// + /// # Examples + /// + /// ``` + /// #![feature(vec_flatten)] + /// + /// let v = vec![ [1, 2, 3], [7, 8, 9] ]; + /// assert_eq!(v.flatten(), [1, 2, 3, 7, 8, 9]); + /// + /// let v = vec![[0; 7]; 13]; + /// assert_eq!(v.flatten().len(), 7 * 13); + /// + /// let v = vec![[(); 500]; 2_000]; + /// assert_eq!(v.flatten().len(), 1_000_000); + /// + /// enum Never {} + /// let v: Vec<[Never; 0]> = vec![[], [], []]; + /// assert_eq!(v.flatten().len(), 0); + /// ``` + /// + /// ```should_panic + /// #![feature(vec_flatten)] + /// + /// let v = vec![[(); std::usize::MAX]; 2]; + /// v.flatten(); // panics for length overflow + /// ``` + #[inline] + #[unstable(feature = "vec_flatten", issue = "88888888", + reason = "new API, and needs const generics stabilized first")] + pub fn flatten(mut self) -> Vec { + if N == 0 { + // The allocator-related safety implications of switching pointers + // between ZSTs and other types are non-obvious, so just do the + // simple thing -- this check is compile-time anyway. + return Vec::new(); + } + + let ptr = self.as_mut_ptr() as *mut T; + let (len, cap) = + if mem::size_of::() == 0 { + // Since ZSTs are limited only by the size of the counters, + // it's completely possible to overflow here. Capacity just + // saturates because it usually comes in as `usize::MAX` so + // checking the multiplication would make it almost always panic. + ( + self.len().checked_mul(N).expect("length overflow"), + self.capacity().saturating_mul(N), + ) + } else { + // But for types with an actual size these multiplications + // cannot overflow as the memory is allocated in self, so don't + // codegen a reference to panic machinery. + (self.len() * N, self.capacity() * N) + }; + mem::forget(self); + + // SAFETY: + // - The pointer came from the same allocator with which it's being used. + // - The layout of the allocation hasn't changed, because the alignment + // of an array matches that of its elements and we increased the length + // by the appropriate amount to counteract the decrease in type size. + // - Length is less than or equal to capacity because we increased both + // proportionately or set capacity to the largest possible value. + unsafe { + Vec::from_raw_parts(ptr, len, cap) + } + } +} + impl Vec { /// Resizes the `Vec` in-place so that `len` is equal to `new_len`. ///