diff --git a/CHANGELOG.md b/CHANGELOG.md index 2fce0a31a2..c396cb2c7c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,38 @@ +* **`1.0.0`** + * **Use GATs for `ArrayLength`** ! + * Bump MSRV to 1.65.0 + * Use Rust 2021 edition [#118](https://github.com/fizyk20/generic-array/pull/118) + non-PR upgrade later with GATs. + * Allow `arr!` macro in `const` [#129](https://github.com/fizyk20/generic-array/pull/129) + * Add `arr!` repeat-expressions [#130](https://github.com/fizyk20/generic-array/pull/130) + * Implement `const-default` trait support [#131](https://github.com/fizyk20/generic-array/pull/131) + * Make `as_slice()/from_slice()` const. + * Add const `from_array`/`into_array` methods. + * Make `ArrayLength: 'static` + * Replace `From<&[T]>` with `TryFrom<&[T]>` + * Add `try_from_iter` for fallible construction from iterator. + * Use `typenum`'s `const-generics` feature for `const N: usize`-based `From` implementations between `[T; N]` and `GenericArray` + * Also added the `IntoArrayLength` trait and `ConstArrayLength` type-alias for working with typenum's `Const` easier. + * `alloc` crate feature + * Added `box_arr!` macro with the same syntax as `arr!`, but returns a `Box>` + * Moving between heap and stack + * `impl TryFrom> for GenericArray` + * `impl TryFrom> for GenericArray` + * `impl From> for Vec` + * `impl From> for Box<[T]>` + * Methods for converting between `Box>` and `Vec`/`Box<[T]>` + * `GenericSequence` and `FunctionalSequence` implemented for `Box>`, allowing for heap-based manipulation of fixed-size arrays. + * `Deserialize` no longer requires `T: Default` + * Make `IntoArrayLength`, `MappedSequence`, and `FunctionalSequence` safe traits. + * Simplify `arr!` macro syntax. + * `arr![1, 2, 3, 4]` or `arr![T; N]` forms, no explicit length for first variant. + * No longer casts given expressions internally. + * Type-deduction works similarly to `vec![]`, in that an empty array has an unknown type + * Add the `internals` Cargo feature to expose dangerous things. + * Added additional methods for working with chunks of arrays. + * Added `From` impls for tuples with 1-12 (inclusive) items of the same type, matching the standard library. + * Workaround potential Rust/LLVM regressions with `FunctionalSequence::zip()`/`::map()` + * Improve documentation + * **`0.14.6`** * Add an optional `Zeroize` impl for `GenericArray` ([#126](https://github.com/fizyk20/generic-array/pull/126) and [#112](https://github.com/fizyk20/generic-array/pull/112)) * Cleanup some unsafe ([#125](https://github.com/fizyk20/generic-array/pull/125)) and typos ([#114](https://github.com/fizyk20/generic-array/pull/114)) diff --git a/Cargo.toml b/Cargo.toml index 32abfb3e54..67bf41dd1b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,9 +1,9 @@ [package] name = "generic-array" -version = "0.14.6" -authors = [ "Bartłomiej Kamiński ", "Aaron Trent " ] -edition = "2018" +version = "1.0.0" +authors = ["Bartłomiej Kamiński ", "Aaron Trent "] +edition = "2021" description = "Generic types implementing functionality of arrays" readme = "README.md" @@ -15,7 +15,7 @@ repository = "https://github.com/fizyk20/generic-array.git" keywords = ["generic", "array"] categories = ["data-structures", "no-std"] -include = ["src/**/*", "LICENSE", "README.md", "CHANGELOG.md", "build.rs"] +include = ["src/**/*", "LICENSE", "README.md", "CHANGELOG.md"] [badges] travis-ci = { repository = "fizyk20/generic-array" } @@ -24,21 +24,36 @@ travis-ci = { repository = "fizyk20/generic-array" } name = "generic_array" [features] -more_lengths = [] +alloc = [] +internals = [] [dependencies] -typenum = "1.12" +typenum = { version = "1.16", features = ["const-generics"] } const-default = { version = "1", optional = true, default-features = false } serde = { version = "1.0", optional = true, default-features = false } zeroize = { version = "1", optional = true, default-features = false } +faster-hex = { version = "0.8", optional = true, default-features = false } -[dev_dependencies] +[dev-dependencies] # this can't yet be made optional, see https://github.com/rust-lang/cargo/issues/1596 serde_json = "1.0" bincode = "1.0" +criterion = { version = "0.5", features = ["html_reports"] } +rand = "0.8" -[build_dependencies] -version_check = "0.9" +[[bench]] +name = "hex" +harness = false + +[profile.bench] +opt-level = 3 +lto = 'fat' +codegen-units = 1 [package.metadata.docs.rs] -features = ["serde", "zeroize"] \ No newline at end of file +# all but "internals", don't show those on docs.rs +features = ["serde", "zeroize", "const-default", "alloc"] +rustdoc-args = ["--cfg", "docsrs"] + +[package.metadata.playground] +all-features = true diff --git a/DESIGN.md b/DESIGN.md deleted file mode 100644 index de5e90f33d..0000000000 --- a/DESIGN.md +++ /dev/null @@ -1,585 +0,0 @@ -Design and Usage Notes -====================== - -## Sections - -1. [How it Works](#how-it-works) -2. [Initialization](#initialization) -3. [Functional Programming](#functional-programming) -4. [Miscellaneous Utilities](#miscellaneous-utilities) -5. [Safety](#safety) -6. [Optimization](#optimization) -7. [The Future](#the-future) - -**NOTE**: This document uses `
` sections, so look out for collapsible parts with an arrow on the left. - -# How it works - -`generic-array` is a method of achieving fixed-length fixed-size stack-allocated generic arrays without needing const generics in stable Rust. - -That is to say this: - -```rust -struct Foo { - data: [i32; N], -} -``` - -or anything similar is not currently supported. - -However, Rust's type system is sufficiently advanced, and a "hack" for solving this was created in the form of the `typenum` crate, which recursively defines integer values in binary as nested types, and operations which can be applied to those type-numbers, such as `Add`, `Sub`, etc. - -e.g. `6` would be `UInt, B1>, B0>` - -Over time, I've come to see `typenum` as less of a hack and more as an elegant solution. - -The recursive binary nature of `typenum` is what makes `generic-array` possible, so: - -```rust -struct Foo> { - data: GenericArray, -} -``` - -is supported. - -I often see questions about why `ArrayLength` requires the element type `T` in it's signature, even though it's not used in the inner `ArrayType`. - -This is because `GenericArray` itself does not define the actual array. Rather, it is defined as: - -```rust -pub struct GenericArray> { - data: N::ArrayType, -} -``` - -The trait `ArrayLength` does all the real heavy lifting for defining the data, with implementations on `UInt`, `UInt` and `UTerm`, which correspond to even, odd and zero numeric values, respectively. - -`ArrayLength`'s implementations use type-level recursion to peel away each least significant bit and form sort of an opaque binary tree of contiguous data the correct physical size to store `N` elements of `T`. The tree, or block of data, is then stored inside of `GenericArray` to be reinterpreted as the array. - -For example, `GenericArray` more or less expands to (at compile time): - -
-Expand for code - -```rust -GenericArray { - // UInt, B1>, B0> - data: EvenData { - // UInt, B1> - left: OddData { - // UInt - left: OddData { - left: (), // UTerm - right: (), // UTerm - data: T, // Element 0 - }, - // UInt - right: OddData { - left: (), // UTerm - right: (), // UTerm - data: T, // Element 1 - }, - data: T // Element 2 - }, - // UInt, B1> - right: OddData { - // UInt - left: OddData { - left: (), // UTerm - right: (), // UTerm - data: T, // Element 3 - }, - // UInt - right: OddData { - left: (), // UTerm - right: (), // UTerm - data: T, // Element 4 - }, - data: T // Element 5 - } - } -} -``` - -
- -This has the added benefit of only being `log2(N)` deep, which is important for things like `Drop`, which we'll go into later. - -Then, we take `data` and cast it to `*const T` or `*mut T` and use it as a slice like: - -```rust -unsafe { - slice::from_raw_parts( - self as *const Self as *const T, - N::to_usize() - ) -} -``` - -It is useful to note that because `typenum` is compile-time with nested generics, `to_usize`, even if it isn't a `const fn`, *does* expand to effectively `1 + 2 + 4 + 8 + ...` and so forth, which LLVM is smart enough to reduce to a single compile-time constant. This helps hint to the optimizers about things such as bounds checks. - -So, to reiterate, we're working with a raw block of contiguous memory the correct physical size to store `N` elements of `T`. It's really no different from how normal arrays are stored. - -## Pointer Safety - -Of course, casting pointers around and constructing blocks of data out of thin air is normal for C, but here in Rust we try to be a bit less prone to segfaults. Therefore, great care is taken to minimize casual `unsafe` usage and restrict `unsafe` to specific parts of the API, making heavy use those exposed safe APIs internally. - -For example, the above `slice::from_raw_parts` is only used twice in the entire library, once for `&[T]` and `slice::from_raw_parts_mut` once for `&mut [T]`. Everything else goes through those slices. - -# Initialization - -## Constant - -"Constant" initialization, that is to say - without dynamic values, can be done via the `arr![]` macro, which works almost exactly like `vec![]`, but with an additional type parameter. - -Example: - -```rust -let my_arr = arr![i32; 1, 2, 3, 4, 5, 6, 7, 8]; -``` - -## Dynamic - -Although some users have opted to use their own initializers, as of version `0.9` and beyond `generic-array` includes safe methods for initializing elements in the array. - -The `GenericSequence` trait defines a `generate` method which can be used like so: - -```rust -use generic_array::{GenericArray, sequence::GenericSequence}; - -let squares: GenericArray = - GenericArray::generate(|i: usize| i as i32 * 2); -``` - -and `GenericArray` additionally implements `FromIterator`, although `from_iter` ***will*** panic if the number of elements is not *at least* `N`. It will ignore extra items. - -The safety of these operations is described later. - -# Functional Programming - -In addition to `GenericSequence`, this crate provides a `FunctionalSequence`, which allows extremely efficient `map`, `zip` and `fold` operations on `GenericArray`s. - -As described at the end of the [Optimization](#optimization) section, `FunctionalSequence` uses clever specialization tactics to provide optimized methods wherever possible, while remaining perfectly safe. - -Some examples, taken from `tests/generic.rs`: - -
-Expand for code - -This is so extensive to show how you can build up to processing totally arbitrary sequences, but for the most part these can be used on `GenericArray` instances without much added complexity. - -```rust -/// Super-simple fixed-length i32 `GenericArray`s -pub fn generic_array_plain_zip_sum(a: GenericArray, b: GenericArray) -> i32 { - a.zip(b, |l, r| l + r) - .map(|x| x + 1) - .fold(0, |a, x| x + a) -} - -pub fn generic_array_variable_length_zip_sum(a: GenericArray, b: GenericArray) -> i32 -where - N: ArrayLength, -{ - a.zip(b, |l, r| l + r) - .map(|x| x + 1) - .fold(0, |a, x| x + a) -} - -pub fn generic_array_same_type_variable_length_zip_sum(a: GenericArray, b: GenericArray) -> i32 -where - N: ArrayLength + ArrayLength<>::Output>, - T: Add, -{ - a.zip(b, |l, r| l + r) - .map(|x| x + 1) - .fold(0, |a, x| x + a) -} - -/// Complex example using fully generic `GenericArray`s with the same length. -/// -/// It's mostly just the repeated `Add` traits, which would be present in other systems anyway. -pub fn generic_array_zip_sum + ArrayLength>(a: GenericArray, b: GenericArray) -> i32 -where - A: Add, - N: ArrayLength<>::Output> + - ArrayLength<<>::Output as Add>::Output>, - >::Output: Add, - <>::Output as Add>::Output: Add, -{ - a.zip(b, |l, r| l + r) - .map(|x| x + 1) - .fold(0, |a, x| x + a) -} -``` -
- -and if you really want to go off the deep end and support any arbitrary *`GenericSequence`*: - -
-Expand for code - -```rust -/// Complex example function using generics to pass N-length sequences, zip them, and then map that result. -/// -/// If used with `GenericArray` specifically this isn't necessary -pub fn generic_sequence_zip_sum(a: A, b: B) -> i32 -where - A: FunctionalSequence, // `.zip` - B: FunctionalSequence, // `.zip` - A: MappedGenericSequence, // `i32` -> `i32` - B: MappedGenericSequence>, // `i32` -> `i32`, prove A and B can map to the same output - A::Item: Add, // `l + r` - MappedSequence: MappedGenericSequence + FunctionalSequence, // `.map` - SequenceItem>: Add, // `x + 1` - MappedSequence, i32, i32>: Debug, // `println!` - MappedSequence, i32, i32>: FunctionalSequence, // `.fold` - SequenceItem, i32, i32>>: Add // `x + a`, note the order -{ - let c = a.zip(b, |l, r| l + r).map(|x| x + 1); - - println!("{:?}", c); - - c.fold(0, |a, x| x + a) -} -``` - -of course, as I stated before, that's almost never necessary, especially when you know the concrete types of all the components. - -
- -The [`numeric-array`](https://crates.io/crates/numeric-array) crate uses these to apply numeric operations across all elements in a `GenericArray`, making full use of all the optimizations described in the last section here. - -# Miscellaneous Utilities - -Although not usually advertised, `generic-array` contains traits for lengthening, shortening, splitting and concatenating arrays. - -For example, these snippets are taken from `tests/mod.rs`: - -
-Expand for code - -Appending and prepending elements: - -```rust -use generic_array::sequence::Lengthen; - -#[test] -fn test_append() { - let a = arr![i32; 1, 2, 3]; - - let b = a.append(4); - - assert_eq!(b, arr![i32; 1, 2, 3, 4]); -} - -#[test] -fn test_prepend() { - let a = arr![i32; 1, 2, 3]; - - let b = a.prepend(4); - - assert_eq!(b, arr![i32; 4, 1, 2, 3]); -} -``` - -Popping elements from the front of back of the array: - -```rust -use generic_array::sequence::Shorten; - -let a = arr![i32; 1, 2, 3, 4]; - -let (init, last) = a.pop_back(); - -assert_eq!(init, arr![i32; 1, 2, 3]); -assert_eq!(last, 4); - -let (head, tail) = a.pop_front(); - -assert_eq!(head, 1); -assert_eq!(tail, arr![i32; 2, 3, 4]); -``` - -and of course concatenating and splitting: - -```rust -use generic_array::sequence::{Concat, Split}; - -let a = arr![i32; 1, 2]; -let b = arr![i32; 3, 4]; - -let c = a.concat(b); - -assert_eq!(c, arr![i32; 1, 2, 3, 4]); - -let (d, e) = c.split(); - -assert_eq!(d, arr![i32; 1]); -assert_eq!(e, arr![i32; 2, 3, 4]); -``` -
- -`Split` and `Concat` in these examples use type-inference to determine the lengths of the resulting arrays. - -# Safety - -As stated earlier, for raw reinterpretations such as this, safety is a must even while working with unsafe code. Great care is taken to reduce or eliminate undefined behavior. - -For most of the above code examples, the biggest potential undefined behavior hasn't even been applicable for one simple reason: they were all primitive values. - -The simplest way to lead into this is to post these questions: - -1. What if the element type of the array implements `Drop`? -2. What if `GenericArray::generate` opens a bunch of files? -3. What if halfway through opening each of the files, one is not found? -4. What if the resulting error is unwrapped, causing the generation function to panic? - -For a fully initialized `GenericArray`, the expanded structure as described in the [How It Works](#how-it-works) can implement `Drop` naturally, recursively dropping elements. As it is only `log2(N)` deep, the recursion is very small overall. - -In fact, I tested it while writing this, the size of the array itself overflows the stack before any recursive calls to `drop` can. - -However, ***partially*** initialized arrays, such as described in the above hypothetical, pose an issue where `drop` could be called on uninitialized data, which is undefined behavior. - -To solve this, `GenericArray` implements two components named `ArrayBuilder` and `ArrayConsumer`, which work very similarly. - -`ArrayBuilder` creates a block of wholly uninitialized memory via `mem::unintialized()`, and stores that in a `ManuallyDrop` wrapper. `ManuallyDrop` does exactly what it says on the tin, and simply doesn't drop the value unless manually requested to. - -So, as we're initializing our array, `ArrayBuilder` keeps track of the current position through it, and if something happens, `ArrayBuilder` itself will iteratively and manually `drop` all currently initialized elements, ignoring any uninitialized ones, because those are just raw memory and should be ignored. - -`ArrayConsumer` does almost the same, "moving" values out of the array and into something else, like user code. It uses `ptr::read` to "move" the value out, and increments a counter saying that value is no longer valid in the array. - -If a panic occurs in the user code with that element, it's dropped naturally as it was moved into that scope. `ArrayConsumer` then proceeds to iteratively and manually `drop` all *remaining* elements. - -Combined, these two systems provide a safe system for building and consuming `GenericArray`s. In fact, they are used extensively inside the library itself for `FromIterator`, `GenericSequence` and `FunctionalSequence`, among others. - -Even `GenericArray`s implementation of `Clone` makes use of this via: - -```rust -impl Clone for GenericArray -where - N: ArrayLength, -{ - fn clone(&self) -> GenericArray { - self.map(|x| x.clone()) - } -} -``` - -where `.map` is from the `FunctionalSequence`, and uses those builder and consumer structures to safely move and initialize values. Although, in this particular case, a consumer is not necessary as we're using references. More on how that is automatically deduced is described in the next section. - -# Optimization - -Rust and LLVM is smart. Crazy smart. However, it's not magic. - -In my experience, most of Rust's "zero-cost" abstractions stem more from the type system, rather than explicit optimizations. Most Rust code is very easily optimizable and inlinable by design, so it can be simplified and compacted rather well, as opposed to the spaghetti code of some other languages. - -Unfortunately, unless `rustc` or LLVM can "prove" things about code to simplify it, it must still be run, and can prevent further optimization. - -A great example of this, and why I created the `GenericSequence` and `FunctionalSequence` traits, are iterators. - -Custom iterators are slow. Not terribly slow, but slow enough to prevent some rather important optimizations. - -Take `GenericArrayIter` for example: - -
-Expand for code - -```rust -pub struct GenericArrayIter> { - array: ManuallyDrop>, - index: usize, - index_back: usize, -} - -impl Iterator for GenericArrayIter -where - N: ArrayLength, -{ - type Item = T; - - #[inline] - fn next(&mut self) -> Option { - if self.index < self.index_back { - let p = unsafe { - Some(ptr::read(self.array.get_unchecked(self.index))) - }; - - self.index += 1; - - p - } else { - None - } - } - - //and more -} -``` -
- -Seems simple enough, right? Move an element out of the array with `ptr::read` and increment the index. If the iterator is dropped, the remaining elements are dropped exactly as they would with `ArrayConsumer`. `index_back` is provided for `DoubleEndedIterator`. - -Unfortunately, that single `if` statement is terrible. In my mind, this is one of the biggest flaws of the iterator design. A conditional jump on a mutable variable unrelated to the data we are accessing on each call foils the optimizer and generates suboptimal code for the above iterator, even when we use `get_unchecked`. - -The optimizer is unable to see that we are simply accessing memory sequentially. In fact, almost all iterators are like this. Granted, this is usually fine and, especially if they have to handle errors, it's perfectly acceptable. - -However, there is one iterator in the standard library that is optimized perfectly: the slice iterator. So perfectly in fact that it allows the optimizer to do something even more special: **auto-vectorization**! We'll get to that later. - -It's a bit frustrating as to *why* slice iterators can be so perfectly optimized, and it basically boils down to that the iterator itself does not own the data the slice refers to, so it uses raw pointers to the array/sequence/etc. rather than having to use an index on a stack allocated and always moving array. It can check for if the iterator is empty by comparing some `front` and `back` pointers for equality, and because those directly correspond to the position in memory of the next element, LLVM can see that and make optimizations. - -So, the gist of that is: always use slice iterators where possible. - -Here comes the most important part of all of this: `ArrayBuilder` and `ArrayConsumer` don't iterate the arrays themselves. Instead, we use slice iterators (immutable and mutable), with `zip` or `enumerate`, to apply operations to the entire array, incrementing the position in both `ArrayBuilder` or `ArrayConsumer` to keep track. - -For example, `GenericSequence::generate` for `GenericArray` is: - -
-Expand for code - -```rust -fn generate(mut f: F) -> GenericArray -where - F: FnMut(usize) -> T, -{ - unsafe { - let mut destination = ArrayBuilder::new(); - - { - let (destination_iter, position) = destination.iter_position(); - - for (i, dst) in destination_iter.enumerate() { - ptr::write(dst, f(i)); - - *position += 1; - } - } - - destination.into_inner() - } -} -``` - -where `ArrayBuilder::iter_position` is just an internal convenience function: - -```rust -pub unsafe fn iter_position(&mut self) -> (slice::IterMut, &mut usize) { - (self.array.iter_mut(), &mut self.position) -} -``` -
- -Of course, this may appear to be redundant, if we're using an iterator that keeps track of the position itself, and the builder is also keeping track of the position. However, the two are decoupled. - -If the generation function doesn't have a chance at panicking, and/or the array element type doesn't implement `Drop`, the optimizer deems the `Drop` implementation on `ArrayBuilder` (and `ArrayConsumer`) dead code, and therefore `position` is never actually read from, so it becomes dead code as well, and is removed. - -So for simple non-`Drop`/non-panicking elements and generation functions, `generate` becomes a very simple loop that uses a slice iterator to write values to the array. - -Next, let's take a look at a more complex example where this *really* shines: `.zip` - -To cut down on excessively verbose code, `.zip` uses `FromIterator` for building the array, which has almost identical code to `generate`, so it will be omitted. - -The first implementation of `.zip` is defined as: - -
-Expand for code - -```rust -fn inverted_zip( - self, - lhs: GenericArray, - mut f: F, -) -> MappedSequence, B, U> -where - GenericArray: - GenericSequence + MappedGenericSequence, - Self: MappedGenericSequence, - Self::Length: ArrayLength + ArrayLength, - F: FnMut(B, Self::Item) -> U, -{ - unsafe { - let mut left = ArrayConsumer::new(lhs); - let mut right = ArrayConsumer::new(self); - - let (left_array_iter, left_position) = left.iter_position(); - let (right_array_iter, right_position) = right.iter_position(); - - FromIterator::from_iter(left_array_iter.zip(right_array_iter).map(|(l, r)| { - let left_value = ptr::read(l); - let right_value = ptr::read(r); - - *left_position += 1; - *right_position += 1; - - f(left_value, right_value) - })) - } -} -``` -
- -The gist of this is that we have two `GenericArray` instances that need to be zipped together and mapped to a new sequence. This employs two `ArrayConsumer`s, and more or less use the same pattern as the previous example. - -Again, the position values can be optimized out, and so can the slice iterator adapters. - -We can go a step further with this, however. - -Consider this: - -```rust -let a = arr![i32; 1, 3, 5, 7]; -let b = arr![i32; 2, 4, 6, 8]; - -let c = a.zip(b, |l, r| l + r); - -assert_eq!(c, arr![i32; 3, 7, 11, 15]); -``` - -when compiled with: - -``` -cargo rustc --lib --profile test --release -- -C target-cpu=native -C opt-level=3 --emit asm -``` - -will produce assembly with the following relevant instructions taken from the entire program: - -```asm -; Copy constant to register -vmovaps __xmm@00000007000000050000000300000001(%rip), %xmm0 - -; Copy constant to register -vmovaps __xmm@00000008000000060000000400000002(%rip), %xmm0 - -; Add the two values together -vpaddd 192(%rsp), %xmm0, %xmm1 - -; Copy constant to register -vmovaps __xmm@0000000f0000000b0000000700000003(%rip), %xmm0 - -; Compare result of the addition with the last constant -vpcmpeqb 128(%rsp), %xmm0, %xmm0 -``` - -so, aside from a bunch of obvious hygiene instructions around those selected instructions, -it seriously boils down that `.zip` call to a ***SINGLE*** SIMD instruction. In fact, it continues to do this for even larger arrays. Although it does fall back to individual additions for fewer than four elements, as it can't fit those into an SSE register evenly. - -Using this property of auto-vectorization without sacrificing safety, I created the [`numeric-array`](https://crates.io/crates/numeric-array) crate which makes use of this to wrap `GenericArray` and implement numeric traits so that almost *all* operations can be auto-vectorized, even complex ones like fused multiple-add. - -It doesn't end there, though. You may have noticed that the function name for zip above wasn't `zip`, but `inverted_zip`. - -This is because `generic-array` employs a clever specialization tactic to ensure `.zip` works corrects with: - -1. `a.zip(b, ...)` -2. `(&a).zip(b, ...)` -3. `(&a).zip(&b, ...)` -4. `a.zip(&b, ...)` - -wherein `GenericSequence` and `FunctionalSequence` have default implementations of `zip` variants, with concrete implementations for `GenericArray`. As `GenericSequence` is implemented for `&GenericArray`, where calling `into_iter` on produces a slice iterator, it can use "naive" iterator adapters to the same effect, while the specialized implementations use `ArrayConsumer`. - -The result is that any combination of move or reference calls to `.zip`, `.map` and `.fold` produce code that can be optimized, none of them falling back to slow non-slice iterators. All perfectly safe with the `ArrayBuilder` and `ArrayConsumer` systems. - -Honestly, `GenericArray` is better than standard arrays at this point. - -# The Future - -If/when const generics land in stable Rust, my intention is to reorient this crate or create a new crate to provide traits and wrappers for standard arrays to provide the same safety and performance discussed above. \ No newline at end of file diff --git a/README.md b/README.md index ff1ccc2452..2dab5530cf 100644 --- a/README.md +++ b/README.md @@ -4,59 +4,82 @@ This crate implements generic array types for Rust. -**Requires minumum Rust version of 1.36.0, or 1.41.0 for `From<[T; N]>` implementations** +**Requires minumum Rust version of 1.65.0 [Documentation](http://fizyk20.github.io/generic-array/generic_array/) ## Usage -The Rust arrays `[T; N]` are problematic in that they can't be used generically with respect to `N`, so for example this won't work: +Before Rust 1.51, arrays `[T; N]` were problematic in that they couldn't be generic with respect to the length `N`, so this wouldn't work: ```rust struct Foo { - data: [i32; N] + data: [i32; N], } ``` -**generic-array** defines a new trait `ArrayLength` and a struct `GenericArray>`, which let the above be implemented as: +Since 1.51, the below syntax is valid: ```rust -struct Foo> { - data: GenericArray +struct Foo { + data: [i32; N], } ``` -The `ArrayLength` trait is implemented by default for [unsigned integer types](http://fizyk20.github.io/generic-array/typenum/uint/index.html) from [typenum](http://fizyk20.github.io/generic-array/typenum/index.html) crate: +However, the const-generics we have as of writing this are still the minimum-viable product (`min_const_generics`), so many situations still result in errors, such as this example: ```rust -use generic_array::typenum::U5; +trait Bar { + const LEN: usize; + + // Error: cannot perform const operation using `Self` + fn bar(&self) -> Foo<{ Self::LEN }>; +} +``` -struct Foo> { +**generic-array** defines a new trait `ArrayLength` and a struct `GenericArray`, which lets the above be implemented as: + +```rust +struct Foo { data: GenericArray } -fn main() { - let foo = Foo::{data: GenericArray::default()}; +trait Bar { + type LEN: ArrayLength; + fn bar(&self) -> Foo; } ``` -For example, `GenericArray` would work almost like `[T; 5]`: +The `ArrayLength` trait is implemented for [unsigned integer types](http://fizyk20.github.io/generic-array/typenum/uint/index.html) from [typenum](http://fizyk20.github.io/generic-array/typenum/index.html) crate. For example, `GenericArray` would work almost like `[T; 5]`: ```rust use generic_array::typenum::U5; -struct Foo> { +struct Foo { data: GenericArray } -fn main() { - let foo = Foo::{data: GenericArray::default()}; -} +let foo = Foo:: { data: GenericArray::default() }; ``` -In version 0.1.1 an `arr!` macro was introduced, allowing for creation of arrays as shown below: +The `arr!` macro is provided to allow easier creation of literal arrays, as shown below: ```rust -let array = arr![u32; 1, 2, 3]; +let array = arr![1, 2, 3]; +// array: GenericArray assert_eq!(array[2], 3); ``` + +## Feature flags + +```toml +[dependencies.generic-array] +features = [ + "more_lengths", # Expands From/Into implementation for more array lengths + "serde", # Serialize/Deserialize implementation + "zeroize", # Zeroize implementation for setting array elements to zero + "const-default", # Compile-time const default value support via trait + "alloc", # Enables From/TryFrom implementations between GenericArray and Vec/Box<[T]> + "faster-hex" # Enables internal use of the `faster-hex` crate for faster hex encoding via SIMD +] +``` \ No newline at end of file diff --git a/benches/hex.rs b/benches/hex.rs new file mode 100644 index 0000000000..ff879c9ae3 --- /dev/null +++ b/benches/hex.rs @@ -0,0 +1,46 @@ +use criterion::{ + criterion_group, criterion_main, measurement::WallTime, BenchmarkGroup, Criterion, +}; +use generic_array::{typenum::*, ArrayLength, GenericArray}; +use rand::RngCore; + +use std::{fmt::UpperHex, io::Write}; + +fn criterion_benchmark(c: &mut Criterion) { + let mut hex = c.benchmark_group("hex"); + + let mut rng = rand::thread_rng(); + + macro_rules! all_hex_benches { + ($($len:ty,)*) => { + $(bench_hex::<$len>(&mut rng, &mut hex);)* + } + } + + all_hex_benches!( + U1, U2, U4, U8, U12, U15, U16, U32, U64, U100, U128, U160, U255, U256, U500, U512, U900, + U1023, U1024, Sum, U2048, U4096, Prod, U10000, + ); + + hex.finish(); +} + +criterion_group!(benches, criterion_benchmark); +criterion_main!(benches); + +fn bench_hex(mut rng: impl RngCore, g: &mut BenchmarkGroup<'_, WallTime>) +where + GenericArray: UpperHex, +{ + let mut fixture = Box::>::default(); + rng.fill_bytes(fixture.as_mut_slice()); + + g.bench_function(format!("N{:08}", N::USIZE), |b| { + let mut out = Vec::with_capacity(N::USIZE * 2); + + b.iter(|| { + _ = write!(out, "{:X}", &*fixture); + out.clear(); + }); + }); +} diff --git a/build.rs b/build.rs deleted file mode 100644 index f01d2a7827..0000000000 --- a/build.rs +++ /dev/null @@ -1,5 +0,0 @@ -fn main() { - if version_check::is_min_version("1.41.0").unwrap_or(false) { - println!("cargo:rustc-cfg=relaxed_coherence"); - } -} diff --git a/rustfmt.toml b/rustfmt.toml index 3dc0db27b3..45e2070f5a 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -1,3 +1,2 @@ reorder_imports = true -reorder_imported_names = true use_try_shorthand = true diff --git a/src/arr.rs b/src/arr.rs index 8274a9cc36..d80ba9c5b6 100644 --- a/src/arr.rs +++ b/src/arr.rs @@ -1,127 +1,117 @@ //! Implementation for `arr!` macro. -use super::ArrayLength; -use core::ops::Add; -use typenum::U1; - -/// Helper trait for `arr!` macro -pub trait AddLength>: ArrayLength { - /// Resulting length - type Output: ArrayLength; -} - -impl AddLength for N1 -where - N1: ArrayLength + Add, - N2: ArrayLength, - >::Output: ArrayLength, -{ - type Output = >::Output; -} - -/// Helper type for `arr!` macro -pub type Inc = >::Output; - -#[doc(hidden)] +/// Macro allowing for easy construction of Generic Arrays. +/// +/// Type-inference works similarly to `vec![]` +/// +/// **`arr!` can be used in `const` expressions.** +/// +/// Example: +/// ``` +/// # use generic_array::arr; +/// use generic_array::typenum::U6; +/// +/// let test = arr![1, 2, 3]; // implicit length +/// let test = arr![1; 6]; // explicit length via `Const` +/// let test = arr![1; U6]; // explicit length via typenum +/// ``` +/// +/// # NOTES AND LIMITATIONS +/// * As of `generic-array 1.0`, [`From`]/[`from_array`](crate::GenericArray::from_array) can be used directly for a wide range of regular arrays. +/// * The `[T; N: ArrayLength]` and `[T; usize]` explicit forms are limited to `Copy` values. Use +/// [`GenericArray::generate(|| value.clone())`](crate::GenericSequence::generate) for non-`Copy` items. +/// * The `[T; usize]` explicit and `[0, 1, 2, 3]` implicit forms are limited to lengths supported by [`Const`](typenum::Const) #[macro_export] -macro_rules! arr_impl { - (@replace_expr $e:expr) => { 1 }; - (@count_ty) => { $crate::typenum::U0 }; - (@count_ty $val:expr$(, $vals:expr)* $(,)?) => { $crate::typenum::Add1<$crate::arr_impl!(@count_ty $($vals),*)> }; - ($T:ty; $($x:expr),*) => ({ - const __INPUT_LENGTH: usize = 0 $(+ $crate::arr_impl!(@replace_expr $x) )*; - type __OutputLength = $crate::arr_impl!(@count_ty $($x),*); +macro_rules! arr { + ($($x:expr),* $(,)*) => ( $crate::GenericArray::from_array([$($x),*]) ); + ($x:expr; $N:ty) => ({ + // Bypass `from_array` to allow for any Unsigned array length + const __INPUT_LENGTH: usize = <$N as $crate::typenum::Unsigned>::USIZE; #[inline(always)] - const fn __do_transmute>(arr: [T; __INPUT_LENGTH]) -> $crate::GenericArray { - unsafe { $crate::transmute(arr) } + const fn __do_transmute(arr: [T; __INPUT_LENGTH]) -> $crate::GenericArray { + unsafe { $crate::const_transmute(arr) } } - const _: [(); <__OutputLength as $crate::typenum::Unsigned>::USIZE] = [(); __INPUT_LENGTH]; + __do_transmute::<_, $N>([$x; __INPUT_LENGTH]) + }); + ($x:expr; $n:expr) => ( $crate::GenericArray::from_array([$x; $n]) ); +} + +/// Like [`arr!`], but returns a `Box>` +/// +/// Unlike [`arr!`], this is not limited by stack size, only the heap. +/// +/// Example: +/// ``` +/// # use generic_array::{box_arr, typenum::{self, *}}; +/// // allocate a 16MB Buffer of u128 elements (16 bytes * 10 ^ 6) +/// # #[cfg(not(miri))] +/// let test = box_arr![1u128; typenum::Exp]; +/// // test: Box> +/// ``` +/// +/// # NOTES AND LIMITATIONS +/// * The `[T; usize]` explicit and `[0, 1, 2, 3]` implicit forms are limited to lengths supported by [`Const`](typenum::Const) +#[cfg(feature = "alloc")] +#[macro_export] +macro_rules! box_arr { + ($($x:expr),* $(,)*) => ({ + // deduce length based on a ZST array of units + $crate::GenericArray::__from_vec_helper([$($crate::box_arr_helper!(@unit $x)),*], $crate::alloc::vec![$($x),*]) + }); + ($x:expr; $N:ty) => ( $crate::GenericArray::<_, $N>::try_from_vec($crate::alloc::vec![$x; <$N as $crate::typenum::Unsigned>::USIZE]).unwrap() ); + ($x:expr; $n:expr) => ({ + const __LEN: usize = $n; - __do_transmute::<$T, __OutputLength>([$($x as $T),*]) + $crate::GenericArray::<_, <$crate::typenum::Const<__LEN> as $crate::IntoArrayLength>::ArrayLength>::try_from_vec($crate::alloc::vec![$x; __LEN]).unwrap() }); - ($T:ty; $x:expr; $N: ty) => ({ - const __INPUT_LENGTH: usize = <$N as $crate::typenum::Unsigned>::USIZE; +} + +#[cfg(feature = "alloc")] +mod alloc_helper { + use crate::{ArrayLength, GenericArray, IntoArrayLength}; + impl GenericArray { + #[doc(hidden)] #[inline(always)] - const fn __do_transmute>(arr: [T; __INPUT_LENGTH]) -> $crate::GenericArray { - unsafe { $crate::transmute(arr) } + pub fn __from_vec_helper( + _empty: [(); U], + vec: alloc::vec::Vec, + ) -> alloc::boxed::Box> + where + typenum::Const: IntoArrayLength, + { + unsafe { GenericArray::try_from_vec(vec).unwrap_unchecked() } } - - __do_transmute::<$T, $N>([$x; __INPUT_LENGTH]) - }); + } } -/// Macro allowing for easy generation of Generic Arrays. -/// Example: `let test = arr![u32; 1, 2, 3];` +// TODO: Remove this somehow? +#[cfg(feature = "alloc")] +#[doc(hidden)] #[macro_export] -macro_rules! arr { - ($T:ty; $(,)*) => ({ - unsafe { $crate::transmute::<[$T; 0], $crate::GenericArray<$T, $crate::typenum::U0>>([]) } - }); - ($T:ty; $($x:expr),* $(,)*) => ( - $crate::arr_impl!($T; $($x),*) - ); - ($T:ty; $x:expr; $N:ty) => ( - $crate::arr_impl!($T; $x; $N) - ); - ($($x:expr,)+) => (arr![$($x),+]); - () => ("""Macro requires a type, e.g. `let array = arr![u32; 1, 2, 3];`") +macro_rules! box_arr_helper { + (@unit $e:expr) => { + () + }; } mod doctests_only { - /// - /// # With ellision /// /// Testing that lifetimes aren't transmuted when they're ellided. /// /// ```compile_fail /// #[macro_use] extern crate generic_array; - /// fn main() { - /// fn unsound_lifetime_extension<'a, A>(a: &'a A) -> &'static A { - /// arr![&A; a][0] - /// } - /// } - /// ``` - /// - /// ```rust - /// #[macro_use] extern crate generic_array; - /// fn main() { - /// fn unsound_lifetime_extension<'a, A>(a: &'a A) -> &'a A { - /// arr![&A; a][0] - /// } - /// } - /// ``` - /// - /// # Without ellision - /// - /// Testing that lifetimes aren't transmuted when they're specified explicitly. - /// - /// ```compile_fail - /// #[macro_use] extern crate generic_array; - /// fn main() { - /// fn unsound_lifetime_extension<'a, A>(a: &'a A) -> &'static A { - /// arr![&'a A; a][0] - /// } - /// } - /// ``` - /// - /// ```compile_fail - /// #[macro_use] extern crate generic_array; - /// fn main() { - /// fn unsound_lifetime_extension<'a, A>(a: &'a A) -> &'static A { - /// arr![&'static A; a][0] - /// } + /// fn unsound_lifetime_extension<'a, A>(a: &'a A) -> &'static A { + /// arr![a as &A][0] /// } /// ``` /// /// ```rust /// #[macro_use] extern crate generic_array; - /// fn main() { - /// fn unsound_lifetime_extension<'a, A>(a: &'a A) -> &'a A { - /// arr![&'a A; a][0] - /// } + /// fn unsound_lifetime_extension<'a, A>(a: &'a A) -> &'a A { + /// arr![a][0] /// } /// ``` #[allow(dead_code)] diff --git a/src/functional.rs b/src/functional.rs index 6f2a6bfab6..6140791285 100644 --- a/src/functional.rs +++ b/src/functional.rs @@ -2,35 +2,29 @@ //! //! Please see `tests/generics.rs` for examples of how to best use these in your generic functions. -use super::ArrayLength; use core::iter::FromIterator; use crate::sequence::*; /// Defines the relationship between one generic sequence and another, /// for operations such as `map` and `zip`. -pub unsafe trait MappedGenericSequence: GenericSequence -where - Self::Length: ArrayLength, -{ +pub trait MappedGenericSequence: GenericSequence { /// Mapped sequence type type Mapped: GenericSequence; } -unsafe impl<'a, T, U, S: MappedGenericSequence> MappedGenericSequence for &'a S +impl<'a, T, U, S: MappedGenericSequence> MappedGenericSequence for &'a S where &'a S: GenericSequence, S: GenericSequence>::Length>, - >::Length: ArrayLength, { type Mapped = >::Mapped; } -unsafe impl<'a, T, U, S: MappedGenericSequence> MappedGenericSequence for &'a mut S +impl<'a, T, U, S: MappedGenericSequence> MappedGenericSequence for &'a mut S where &'a mut S: GenericSequence, S: GenericSequence>::Length>, - >::Length: ArrayLength, { type Mapped = >::Mapped; } @@ -40,15 +34,15 @@ pub type MappedSequence = <>::Mapped as GenericSequence>::Sequence; /// Defines functional programming methods for generic sequences -pub unsafe trait FunctionalSequence: GenericSequence { +pub trait FunctionalSequence: GenericSequence { /// Maps a `GenericSequence` to another `GenericSequence`. /// /// If the mapping function panics, any already initialized elements in the new sequence /// will be dropped, AND any unused elements in the source sequence will also be dropped. + #[inline] fn map(self, f: F) -> MappedSequence where Self: MappedGenericSequence, - Self::Length: ArrayLength, F: FnMut(Self::Item) -> U, { FromIterator::from_iter(self.into_iter().map(f)) @@ -59,12 +53,33 @@ pub unsafe trait FunctionalSequence: GenericSequence { /// /// If the mapping function panics, any already initialized elements in the new sequence /// will be dropped, AND any unused elements in the source sequences will also be dropped. + /// + /// **WARNING**: If using the `alloc` crate feature, mixing stack-allocated + /// `GenericArray` and heap-allocated `Box>` within [`zip`](FunctionalSequence::zip) + /// should be done with care or avoided. + /// + /// For copy-types, it could be easy to accidentally move the array + /// out of the `Box` when zipping with a stack-allocated array, which could cause a stack-overflow + /// if the array is sufficiently large. However, that being said, the second where clause + /// ensuring they map to the same sequence type will catch common errors, such as: + /// + /// ```compile_fail + /// # use generic_array::{*, functional::FunctionalSequence}; + /// # #[cfg(feature = "alloc")] + /// fn test() { + /// let stack = arr![1, 2, 3, 4]; + /// let heap = box_arr![5, 6, 7, 8]; + /// let mixed = stack.zip(heap, |a, b| a + b); + /// // --- ^^^^ expected struct `GenericArray`, found struct `Box` + /// } + /// # #[cfg(not(feature = "alloc"))] + /// # compile_error!("requires alloc feature to test this properly"); + /// ``` #[inline] fn zip(self, rhs: Rhs, f: F) -> MappedSequence where Self: MappedGenericSequence, Rhs: MappedGenericSequence>, - Self::Length: ArrayLength + ArrayLength, Rhs: GenericSequence, F: FnMut(Self::Item, Rhs::Item) -> U, { @@ -74,6 +89,7 @@ pub unsafe trait FunctionalSequence: GenericSequence { /// Folds (or reduces) a sequence of data into a single value. /// /// If the fold function panics, any unused elements will be dropped. + #[inline] fn fold(self, init: U, f: F) -> U where F: FnMut(U, Self::Item) -> U, @@ -82,12 +98,9 @@ pub unsafe trait FunctionalSequence: GenericSequence { } } -unsafe impl<'a, T, S: GenericSequence> FunctionalSequence for &'a S where - &'a S: GenericSequence -{ -} +impl<'a, T, S: GenericSequence> FunctionalSequence for &'a S where &'a S: GenericSequence {} -unsafe impl<'a, T, S: GenericSequence> FunctionalSequence for &'a mut S where +impl<'a, T, S: GenericSequence> FunctionalSequence for &'a mut S where &'a mut S: GenericSequence { } diff --git a/src/hex.rs b/src/hex.rs index 3f3477b75c..c86056c724 100644 --- a/src/hex.rs +++ b/src/hex.rs @@ -8,12 +8,9 @@ //! use generic_array::arr; //! use generic_array::typenum; //! -//! fn main() { -//! let array = arr![u8; 10, 20, 30]; -//! assert_eq!(format!("{:x}", array), "0a141e"); -//! } +//! let array = arr![10u8, 20, 30]; +//! assert_eq!(format!("{:x}", array), "0a141e"); //! ``` -//! use core::{cmp::min, fmt, ops::Add, str}; @@ -21,85 +18,111 @@ use typenum::*; use crate::{ArrayLength, GenericArray}; -static LOWER_CHARS: &[u8] = b"0123456789abcdef"; -static UPPER_CHARS: &[u8] = b"0123456789ABCDEF"; +#[inline(always)] +fn hex_encode_fallback(src: &[u8], dst: &mut [u8]) { + if dst.len() < src.len() * 2 { + unsafe { core::hint::unreachable_unchecked() }; + } + + let alphabet = match UPPER { + true => b"0123456789ABCDEF", + false => b"0123456789abcdef", + }; + + dst.chunks_exact_mut(2).zip(src).for_each(|(s, c)| { + s[0] = alphabet[(c >> 4) as usize]; + s[1] = alphabet[(c & 0xF) as usize]; + }); +} + +#[inline] +fn hex_encode(src: &[u8], dst: &mut [u8]) { + debug_assert!(dst.len() >= (src.len() * 2)); -impl> fmt::LowerHex for GenericArray + #[cfg(any(miri, not(feature = "faster-hex")))] + hex_encode_fallback::(src, dst); + + // the `unwrap_unchecked` is to avoid the length checks + #[cfg(all(feature = "faster-hex", not(miri)))] + match UPPER { + true => unsafe { faster_hex::hex_encode_upper(src, dst).unwrap_unchecked() }, + false => unsafe { faster_hex::hex_encode(src, dst).unwrap_unchecked() }, + }; +} + +fn generic_hex( + arr: &GenericArray, + f: &mut fmt::Formatter<'_>, +) -> fmt::Result where - T: Add, - >::Output: ArrayLength, + N: Add, + Sum: ArrayLength, { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let max_digits = f.precision().unwrap_or_else(|| self.len() * 2); - let max_hex = (max_digits >> 1) + (max_digits & 1); + let max_digits = N::USIZE * 2; + let max_digits = match f.precision() { + Some(precision) if precision < max_digits => precision, + _ => max_digits, + }; + + // ceil(max_digits / 2) + let max_bytes = (max_digits >> 1) + (max_digits & 1); + + let input = { + // LLVM can't seem to automatically prove this + if max_bytes > N::USIZE { + unsafe { core::hint::unreachable_unchecked() }; + } - if T::USIZE < 1024 { - // For small arrays use a stack allocated - // buffer of 2x number of bytes - let mut res = GenericArray::>::default(); + &arr[..max_bytes] + }; - self.iter().take(max_hex).enumerate().for_each(|(i, c)| { - res[i * 2] = LOWER_CHARS[(c >> 4) as usize]; - res[i * 2 + 1] = LOWER_CHARS[(c & 0xF) as usize]; - }); + if N::USIZE <= 1024 { + // For small arrays use a stack allocated buffer of 2x number of bytes + let mut buf = GenericArray::>::default(); - f.write_str(unsafe { str::from_utf8_unchecked(&res[..max_digits]) })?; + if N::USIZE < 16 { + // for the smallest inputs, don't bother limiting to max_bytes, + // just process the entire array. When "faster-hex" is enabled, + // this avoids its logic that winds up going to the fallback anyway + hex_encode_fallback::(arr, &mut buf); } else { - // For large array use chunks of up to 1024 bytes (2048 hex chars) - let mut buf = [0u8; 2048]; - let mut digits_left = max_digits; - - for chunk in self[..max_hex].chunks(1024) { - chunk.iter().enumerate().for_each(|(i, c)| { - buf[i * 2] = LOWER_CHARS[(c >> 4) as usize]; - buf[i * 2 + 1] = LOWER_CHARS[(c & 0xF) as usize]; - }); - - let n = min(chunk.len() * 2, digits_left); - f.write_str(unsafe { str::from_utf8_unchecked(&buf[..n]) })?; - digits_left -= n; - } + hex_encode::(input, &mut buf); + } + + f.write_str(unsafe { str::from_utf8_unchecked(buf.get_unchecked(..max_digits)) })?; + } else { + // For large array use chunks of up to 1024 bytes (2048 hex chars) + let mut buf = [0u8; 2048]; + let mut digits_left = max_digits; + + for chunk in input.chunks(1024) { + hex_encode::(chunk, &mut buf); + + let n = min(chunk.len() * 2, digits_left); + // SAFETY: n will always be within bounds due to the above min + f.write_str(unsafe { str::from_utf8_unchecked(buf.get_unchecked(..n)) })?; + digits_left -= n; } - Ok(()) } + Ok(()) } -impl> fmt::UpperHex for GenericArray +impl fmt::LowerHex for GenericArray where - T: Add, - >::Output: ArrayLength, + N: Add, + Sum: ArrayLength, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let max_digits = f.precision().unwrap_or_else(|| self.len() * 2); - let max_hex = (max_digits >> 1) + (max_digits & 1); - - if T::USIZE < 1024 { - // For small arrays use a stack allocated - // buffer of 2x number of bytes - let mut res = GenericArray::>::default(); - - self.iter().take(max_hex).enumerate().for_each(|(i, c)| { - res[i * 2] = UPPER_CHARS[(c >> 4) as usize]; - res[i * 2 + 1] = UPPER_CHARS[(c & 0xF) as usize]; - }); + generic_hex::<_, false>(self, f) + } +} - f.write_str(unsafe { str::from_utf8_unchecked(&res[..max_digits]) })?; - } else { - // For large array use chunks of up to 1024 bytes (2048 hex chars) - let mut buf = [0u8; 2048]; - let mut digits_left = max_digits; - - for chunk in self[..max_hex].chunks(1024) { - chunk.iter().enumerate().for_each(|(i, c)| { - buf[i * 2] = UPPER_CHARS[(c >> 4) as usize]; - buf[i * 2 + 1] = UPPER_CHARS[(c & 0xF) as usize]; - }); - - let n = min(chunk.len() * 2, digits_left); - f.write_str(unsafe { str::from_utf8_unchecked(&buf[..n]) })?; - digits_left -= n; - } - } - Ok(()) +impl fmt::UpperHex for GenericArray +where + N: Add, + Sum: ArrayLength, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + generic_hex::<_, true>(self, f) } } diff --git a/src/impl_alloc.rs b/src/impl_alloc.rs new file mode 100644 index 0000000000..10b0fb8aca --- /dev/null +++ b/src/impl_alloc.rs @@ -0,0 +1,180 @@ +use alloc::{boxed::Box, vec::Vec}; + +use crate::{ArrayLength, GenericArray, LengthError}; + +impl TryFrom> for GenericArray { + type Error = crate::LengthError; + + fn try_from(v: Vec) -> Result { + if v.len() != N::USIZE { + return Err(crate::LengthError); + } + + unsafe { + let mut destination = crate::ArrayBuilder::new(); + + destination.extend(v.into_iter()); + + Ok(destination.assume_init()) + } + } +} + +impl GenericArray { + /// Converts a `Box>` into `Box<[T]>` without reallocating. + /// + /// This operation is O(1), constant-time regardless of the array length N. + #[inline] + pub fn into_boxed_slice(self: Box>) -> Box<[T]> { + unsafe { + // SAFETY: Box ensures the array is properly aligned + Box::from_raw(core::ptr::slice_from_raw_parts_mut( + Box::into_raw(self) as *mut T, + N::USIZE, + )) + } + } + + /// Converts a `Box>` into `Vec` without reallocating. + /// + /// This operation is O(1), constant-time regardless of the array length N. + #[inline] + pub fn into_vec(self: Box>) -> Vec { + Vec::from(self.into_boxed_slice()) + } + + /// Attempts to convert a `Box<[T]>` into `Box>` without reallocating. + /// + /// This operation is O(1), constant-time regardless of the array length N. + #[inline] + pub fn try_from_boxed_slice(slice: Box<[T]>) -> Result>, LengthError> { + if slice.len() != N::USIZE { + return Err(LengthError); + } + + Ok(unsafe { Box::from_raw(Box::into_raw(slice) as *mut _) }) + } + + /// Attempts to convert a `Vec` into `Box>` without reallocating. + /// + /// This operation is O(1) **if the `Vec` has the same length and capacity as `N`**, + /// otherwise it will be forced to call `Vec::shrink_to_fit` which is O(N), + /// where N is the number of elements. + #[inline] + pub fn try_from_vec(vec: Vec) -> Result>, LengthError> { + Self::try_from_boxed_slice(vec.into_boxed_slice()) + } + + /// Alternative to `Box::>::default()` that won't overflow the stack for very large arrays. + /// + /// The standard `Box::default()` calls `default` on the inner type, creating it on the stack, + /// and then moves it onto the heap. Optimized release builds often remove this step, but debug builds + /// may have issues. + #[inline] + pub fn default_boxed() -> Box> + where + T: Default, + { + Box::>::generate(|_| T::default()) + } + + /// Like [`GenericArray::try_from_iter`] but returns a `Box>` instead. + pub fn try_boxed_from_iter(iter: I) -> Result>, LengthError> + where + I: IntoIterator, + { + let mut iter = iter.into_iter(); + + // pre-checks + match iter.size_hint() { + // if the lower bound is greater than N, array will overflow + (n, _) if n > N::USIZE => return Err(LengthError), + // if the upper bound is smaller than N, array cannot be filled + (_, Some(n)) if n < N::USIZE => return Err(LengthError), + _ => {} + } + + let mut v = Vec::with_capacity(N::USIZE); + v.extend((&mut iter).take(N::USIZE)); + + if v.len() != N::USIZE || iter.next().is_some() { + return Err(LengthError); + } + + Ok(GenericArray::try_from_vec(v).unwrap()) + } +} + +impl TryFrom> for GenericArray { + type Error = crate::LengthError; + + #[inline] + fn try_from(value: Box<[T]>) -> Result { + Vec::from(value).try_into() + } +} + +impl From> for Box<[T]> { + #[inline] + fn from(value: GenericArray) -> Self { + Box::new(value).into_boxed_slice() + } +} + +impl From> for Vec { + #[inline] + fn from(value: GenericArray) -> Self { + Box::<[T]>::from(value).into() + } +} + +impl IntoIterator for Box> { + type IntoIter = alloc::vec::IntoIter; + type Item = T; + + fn into_iter(self) -> Self::IntoIter { + GenericArray::into_vec(self).into_iter() + } +} + +impl FromIterator for Box> { + /// Create a `Box` from an iterator. + /// + /// Will panic if the number of elements is not exactly the array length. + /// + /// See [`GenericArray::try_boxed_from_iter]` for a fallible alternative. + fn from_iter>(iter: I) -> Self { + match GenericArray::try_boxed_from_iter(iter) { + Ok(res) => res, + Err(_) => crate::from_iter_length_fail(N::USIZE), + } + } +} + +use crate::functional::{FunctionalSequence, MappedGenericSequence}; +use crate::GenericSequence; + +unsafe impl GenericSequence for Box> { + type Length = N; + type Sequence = Box>; + + fn generate(mut f: F) -> Self::Sequence + where + F: FnMut(usize) -> T, + { + let mut v = Vec::with_capacity(N::USIZE); + for i in 0..N::USIZE { + v.push(f(i)); + } + GenericArray::try_from_vec(v).unwrap() + } +} + +impl MappedGenericSequence for Box> { + type Mapped = Box>; +} + +impl FunctionalSequence for Box> where + Self: GenericSequence +{ +} diff --git a/src/impl_const_default.rs b/src/impl_const_default.rs index d4ab5c24f6..484084ae22 100644 --- a/src/impl_const_default.rs +++ b/src/impl_const_default.rs @@ -2,13 +2,12 @@ use crate::{ArrayLength, GenericArray, GenericArrayImplEven, GenericArrayImplOdd}; use const_default::ConstDefault; -use core::marker::PhantomData; impl ConstDefault for GenericArrayImplEven { const DEFAULT: Self = Self { parent1: U::DEFAULT, parent2: U::DEFAULT, - _marker: PhantomData, + _marker: core::marker::PhantomData, }; } @@ -20,11 +19,23 @@ impl ConstDefault for GenericArrayImplOdd> ConstDefault for GenericArray +impl ConstDefault for GenericArray where - U::ArrayType: ConstDefault, + U::ArrayType: ConstDefault, { const DEFAULT: Self = Self { data: ConstDefault::DEFAULT, }; } + +// `T: ConstDefault` is intentionally redundant to provide better hints in the docs +impl GenericArray +where + Self: ConstDefault, +{ + /// Returns the constant "default value" for an array using [ConstDefault] + #[inline(always)] + pub const fn const_default() -> Self { + Self::DEFAULT + } +} diff --git a/src/impl_serde.rs b/src/impl_serde.rs index 55f824d7f2..4b012bb4c1 100644 --- a/src/impl_serde.rs +++ b/src/impl_serde.rs @@ -6,10 +6,9 @@ use core::marker::PhantomData; use serde::de::{self, SeqAccess, Visitor}; use serde::{ser::SerializeTuple, Deserialize, Deserializer, Serialize, Serializer}; -impl Serialize for GenericArray +impl Serialize for GenericArray where T: Serialize, - N: ArrayLength, { #[inline] fn serialize(&self, serializer: S) -> Result @@ -30,35 +29,69 @@ struct GAVisitor { _n: PhantomData, } -impl<'de, T, N> Visitor<'de> for GAVisitor +// to avoid extra computation when testing for extra elements in the sequence +struct Dummy; +impl<'de> Deserialize<'de> for Dummy { + fn deserialize(_deserializer: D) -> Result + where + D: Deserializer<'de>, + { + Ok(Dummy) + } +} + +impl<'de, T, N: ArrayLength> Visitor<'de> for GAVisitor where - T: Deserialize<'de> + Default, - N: ArrayLength, + T: Deserialize<'de>, { type Value = GenericArray; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("struct GenericArray") + write!(formatter, "struct GenericArray", N::USIZE) } fn visit_seq
(self, mut seq: A) -> Result, A::Error> where A: SeqAccess<'de>, { - let mut result = GenericArray::default(); - for i in 0..N::USIZE { - result[i] = seq - .next_element()? - .ok_or_else(|| de::Error::invalid_length(i, &self))?; + match seq.size_hint() { + Some(n) if n != N::USIZE => { + return Err(de::Error::invalid_length(n, &self)); + } + _ => {} + } + + unsafe { + let mut dst = crate::ArrayBuilder::new(); + + let (dst_iter, position) = dst.iter_position(); + + for dst in dst_iter { + match seq.next_element()? { + Some(el) => { + dst.write(el); + *position += 1; + } + None => break, + } + } + + if *position == N::USIZE { + if seq.size_hint() != Some(0) && seq.next_element::()?.is_some() { + return Err(de::Error::invalid_length(*position + 1, &self)); + } + + return Ok(dst.assume_init()); + } + + Err(de::Error::invalid_length(*position, &self)) } - Ok(result) } } -impl<'de, T, N> Deserialize<'de> for GenericArray +impl<'de, T, N: ArrayLength> Deserialize<'de> for GenericArray where - T: Deserialize<'de> + Default, - N: ArrayLength, + T: Deserialize<'de>, { fn deserialize(deserializer: D) -> Result, D::Error> where @@ -102,4 +135,11 @@ mod tests { let size = bincode::serialized_size(&array).unwrap(); assert_eq!(size, 1); } + + #[test] + #[should_panic] + fn test_too_many() { + let serialized = "[1, 2, 3, 4, 5]"; + let _ = serde_json::from_str::>(serialized).unwrap(); + } } diff --git a/src/impl_zeroize.rs b/src/impl_zeroize.rs index 4edf558fc6..91e5421c72 100644 --- a/src/impl_zeroize.rs +++ b/src/impl_zeroize.rs @@ -2,7 +2,7 @@ use crate::{ArrayLength, GenericArray}; use zeroize::Zeroize; -impl> Zeroize for GenericArray { +impl Zeroize for GenericArray { fn zeroize(&mut self) { self.as_mut_slice().iter_mut().zeroize() } diff --git a/src/impls.rs b/src/impls.rs index 987bd6c2e3..a4f6584bb2 100644 --- a/src/impls.rs +++ b/src/impls.rs @@ -3,267 +3,199 @@ use core::cmp::Ordering; use core::fmt::{self, Debug}; use core::hash::{Hash, Hasher}; -use super::{ArrayLength, GenericArray}; +use typenum::{consts, Const}; + +use super::{ArrayLength, ConstArrayLength, GenericArray, IntoArrayLength}; use crate::functional::*; use crate::sequence::*; -impl Default for GenericArray -where - N: ArrayLength, -{ +impl Default for GenericArray { #[inline(always)] fn default() -> Self { Self::generate(|_| T::default()) } } -impl Clone for GenericArray -where - N: ArrayLength, -{ +impl Clone for GenericArray { + #[inline] fn clone(&self) -> GenericArray { self.map(Clone::clone) } } -impl Copy for GenericArray -where - N: ArrayLength, - N::ArrayType: Copy, -{ -} +impl Copy for GenericArray where N::ArrayType: Copy {} -impl PartialEq for GenericArray -where - N: ArrayLength, -{ +impl PartialEq for GenericArray { + #[inline(always)] fn eq(&self, other: &Self) -> bool { **self == **other } } -impl Eq for GenericArray where N: ArrayLength {} +impl Eq for GenericArray {} -impl PartialOrd for GenericArray -where - N: ArrayLength, -{ +impl PartialOrd for GenericArray { + #[inline(always)] fn partial_cmp(&self, other: &GenericArray) -> Option { PartialOrd::partial_cmp(self.as_slice(), other.as_slice()) } } -impl Ord for GenericArray -where - N: ArrayLength, -{ +impl Ord for GenericArray { + #[inline(always)] fn cmp(&self, other: &GenericArray) -> Ordering { Ord::cmp(self.as_slice(), other.as_slice()) } } -impl Debug for GenericArray -where - N: ArrayLength, -{ +impl Debug for GenericArray { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - self[..].fmt(fmt) + self.as_slice().fmt(fmt) } } -impl Borrow<[T]> for GenericArray -where - N: ArrayLength, -{ +impl Borrow<[T]> for GenericArray { #[inline(always)] fn borrow(&self) -> &[T] { - &self[..] + self.as_slice() } } -impl BorrowMut<[T]> for GenericArray -where - N: ArrayLength, -{ +impl BorrowMut<[T]> for GenericArray { #[inline(always)] fn borrow_mut(&mut self) -> &mut [T] { - &mut self[..] + self.as_mut_slice() } } -impl AsRef<[T]> for GenericArray -where - N: ArrayLength, -{ +impl AsRef<[T]> for GenericArray { #[inline(always)] fn as_ref(&self) -> &[T] { - &self[..] + self.as_slice() } } -impl AsMut<[T]> for GenericArray -where - N: ArrayLength, -{ +impl AsMut<[T]> for GenericArray { #[inline(always)] fn as_mut(&mut self) -> &mut [T] { - &mut self[..] + self.as_mut_slice() } } -impl Hash for GenericArray -where - N: ArrayLength, -{ +impl Hash for GenericArray { fn hash(&self, state: &mut H) where H: Hasher, { - Hash::hash(&self[..], state) + Hash::hash(self.as_slice(), state) } } -macro_rules! impl_from { - ($($n: expr => $ty: ty),*) => { - $( - impl From<[T; $n]> for GenericArray { - #[inline(always)] - fn from(arr: [T; $n]) -> Self { - unsafe { $crate::transmute(arr) } - } - } - - #[cfg(relaxed_coherence)] - impl From> for [T; $n] { - #[inline(always)] - fn from(sel: GenericArray) -> [T; $n] { - unsafe { $crate::transmute(sel) } - } - } - - impl<'a, T> From<&'a [T; $n]> for &'a GenericArray { - #[inline] - fn from(slice: &[T; $n]) -> &GenericArray { - unsafe { &*(slice.as_ptr() as *const GenericArray) } - } - } - - impl<'a, T> From<&'a mut [T; $n]> for &'a mut GenericArray { - #[inline] - fn from(slice: &mut [T; $n]) -> &mut GenericArray { - unsafe { &mut *(slice.as_mut_ptr() as *mut GenericArray) } - } - } +impl From<[T; N]> for GenericArray> +where + Const: IntoArrayLength, +{ + #[inline(always)] + fn from(value: [T; N]) -> Self { + GenericArray::from_array(value) + } +} - #[cfg(not(relaxed_coherence))] - impl Into<[T; $n]> for GenericArray { - #[inline(always)] - fn into(self) -> [T; $n] { - unsafe { $crate::transmute(self) } - } - } +impl From>> for [T; N] +where + Const: IntoArrayLength, +{ + #[inline(always)] + fn from(value: GenericArray>) -> Self { + value.into_array() + } +} - impl AsRef<[T; $n]> for GenericArray { - #[inline] - fn as_ref(&self) -> &[T; $n] { - unsafe { $crate::transmute(self) } - } - } +impl<'a, T, const N: usize> From<&'a [T; N]> for &'a GenericArray> +where + Const: IntoArrayLength, +{ + #[inline(always)] + fn from(slice: &'a [T; N]) -> Self { + unsafe { &*(slice.as_ptr() as *const GenericArray>) } + } +} - impl AsMut<[T; $n]> for GenericArray { - #[inline] - fn as_mut(&mut self) -> &mut [T; $n] { - unsafe { $crate::transmute(self) } - } - } - )* +impl<'a, T, const N: usize> From<&'a mut [T; N]> for &'a mut GenericArray> +where + Const: IntoArrayLength, +{ + #[inline(always)] + fn from(slice: &'a mut [T; N]) -> Self { + unsafe { &mut *(slice.as_mut_ptr() as *mut GenericArray>) } } } -impl_from! { - 1 => ::typenum::U1, - 2 => ::typenum::U2, - 3 => ::typenum::U3, - 4 => ::typenum::U4, - 5 => ::typenum::U5, - 6 => ::typenum::U6, - 7 => ::typenum::U7, - 8 => ::typenum::U8, - 9 => ::typenum::U9, - 10 => ::typenum::U10, - 11 => ::typenum::U11, - 12 => ::typenum::U12, - 13 => ::typenum::U13, - 14 => ::typenum::U14, - 15 => ::typenum::U15, - 16 => ::typenum::U16, - 17 => ::typenum::U17, - 18 => ::typenum::U18, - 19 => ::typenum::U19, - 20 => ::typenum::U20, - 21 => ::typenum::U21, - 22 => ::typenum::U22, - 23 => ::typenum::U23, - 24 => ::typenum::U24, - 25 => ::typenum::U25, - 26 => ::typenum::U26, - 27 => ::typenum::U27, - 28 => ::typenum::U28, - 29 => ::typenum::U29, - 30 => ::typenum::U30, - 31 => ::typenum::U31, - 32 => ::typenum::U32 +impl AsRef<[T; N]> for GenericArray> +where + Const: IntoArrayLength, +{ + #[inline(always)] + fn as_ref(&self) -> &[T; N] { + unsafe { core::mem::transmute(self) } + } +} +impl AsMut<[T; N]> for GenericArray> +where + Const: IntoArrayLength, +{ + #[inline(always)] + fn as_mut(&mut self) -> &mut [T; N] { + unsafe { core::mem::transmute(self) } + } } -#[cfg(feature = "more_lengths")] -impl_from! { - 33 => ::typenum::U33, - 34 => ::typenum::U34, - 35 => ::typenum::U35, - 36 => ::typenum::U36, - 37 => ::typenum::U37, - 38 => ::typenum::U38, - 39 => ::typenum::U39, - 40 => ::typenum::U40, - 41 => ::typenum::U41, - 42 => ::typenum::U42, - 43 => ::typenum::U43, - 44 => ::typenum::U44, - 45 => ::typenum::U45, - 46 => ::typenum::U46, - 47 => ::typenum::U47, - 48 => ::typenum::U48, - 49 => ::typenum::U49, - 50 => ::typenum::U50, - 51 => ::typenum::U51, - 52 => ::typenum::U52, - 53 => ::typenum::U53, - 54 => ::typenum::U54, - 55 => ::typenum::U55, - 56 => ::typenum::U56, - 57 => ::typenum::U57, - 58 => ::typenum::U58, - 59 => ::typenum::U59, - 60 => ::typenum::U60, - 61 => ::typenum::U61, - 62 => ::typenum::U62, - 63 => ::typenum::U63, - 64 => ::typenum::U64, +macro_rules! impl_tuple { + (@T $t:ident) => { T }; - 70 => ::typenum::U70, - 80 => ::typenum::U80, - 90 => ::typenum::U90, + ($($len:ty => ($($t:ident,)*);)*) => {$( + impl From<($(impl_tuple!(@T $t),)*)> for GenericArray { + #[inline] + #[allow(non_snake_case)] + fn from(tuple: ($(impl_tuple!(@T $t),)*)) -> Self { + let ($($t,)*) = tuple; + GenericArray::from_array([$($t,)*]) + } + } + + impl From> for ($(impl_tuple!(@T $t),)*) { + #[inline] + #[allow(non_snake_case)] + fn from(array: GenericArray) -> Self { + let [$($t),*] = array.into_array(); + ($($t,)*) + } + } + )*}; +} - 100 => ::typenum::U100, - 200 => ::typenum::U200, - 300 => ::typenum::U300, - 400 => ::typenum::U400, - 500 => ::typenum::U500, +impl_tuple! { + consts::U1 => (A,); + consts::U2 => (A,B,); + consts::U3 => (A,B,C,); + consts::U4 => (A,B,C,D,); + consts::U5 => (A,B,C,D,E,); + consts::U6 => (A,B,C,D,E,F,); + consts::U7 => (A,B,C,D,E,F,G,); + consts::U8 => (A,B,C,D,E,F,G,H,); + consts::U9 => (A,B,C,D,E,F,G,H,I,); + consts::U10 => (A,B,C,D,E,F,G,H,I,J,); + consts::U11 => (A,B,C,D,E,F,G,H,I,J,K,); + consts::U12 => (A,B,C,D,E,F,G,H,I,J,K,L,); +} - 128 => ::typenum::U128, - 256 => ::typenum::U256, - 512 => ::typenum::U512, +#[cfg(test)] +mod tests { + use crate::*; - 1000 => ::typenum::U1000, - 1024 => ::typenum::U1024 + #[test] + fn test_from_inference() { + let a = arr![1, 2, 3, 4]; + let _: [i8; 4] = a.into(); + } } diff --git a/src/internal.rs b/src/internal.rs new file mode 100644 index 0000000000..7897d1fe82 --- /dev/null +++ b/src/internal.rs @@ -0,0 +1,134 @@ +use crate::*; + +pub trait Sealed {} + +impl Sealed for [T; 0] {} + +/// **UNSAFE**: Creates an array one element at a time using a mutable iterator of pointers. +/// +/// You MUST increment the position while iterating to mark off created elements, +/// which will be dropped if `into_inner` is not called. +pub struct ArrayBuilder { + array: GenericArray, N>, + position: usize, +} + +impl ArrayBuilder { + /// Begin building an array + #[inline(always)] + pub const fn new() -> ArrayBuilder { + ArrayBuilder { + array: GenericArray::uninit(), + position: 0, + } + } + + /// Consume an iterator, `.zip`-ing it to fill some or all of the array. This does not check if the + /// iterator had extra elements or too few elements. + /// + /// This makes no attempt to continue where a previous `extend` leaves off. Therefore, it should + /// only be used once per `ArrayBuilder`. + #[inline(always)] + pub unsafe fn extend(&mut self, source: impl Iterator) { + let (destination, position) = (self.array.iter_mut(), &mut self.position); + + destination.zip(source).for_each(|(dst, src)| { + dst.write(src); + *position += 1; + }); + } + + /// Returns true if the write position equals the array size + #[inline(always)] + pub const fn is_full(&self) -> bool { + self.position == N::USIZE + } + + /// Creates a mutable iterator for writing to the array elements. + /// + /// You MUST increment the position value (given as a mutable reference) as you iterate + /// to mark how many elements have been created. + /// + /// ``` + /// #[cfg(feature = "internals")] + /// # { + /// # use generic_array::{GenericArray, internals::ArrayBuilder, typenum::U5}; + /// # struct SomeType; + /// fn make_some_struct() -> SomeType { SomeType } + /// unsafe { + /// let mut builder = ArrayBuilder::::new(); + /// let (dst_iter, position) = builder.iter_position(); + /// for dst in dst_iter { + /// dst.write(make_some_struct()); + /// // MUST be done AFTER ownership of the value has been given to `dst.write` + /// *position += 1; + /// } + /// let your_array = builder.assume_init(); + /// } + /// # } + /// ``` + #[inline(always)] + pub unsafe fn iter_position(&mut self) -> (slice::IterMut>, &mut usize) { + (self.array.iter_mut(), &mut self.position) + } + + /// When done writing (assuming all elements have been written to), + /// get the inner array. + #[inline(always)] + pub unsafe fn assume_init(self) -> GenericArray { + debug_assert!(self.is_full()); + + let array = ptr::read(&self.array); + mem::forget(self); + GenericArray::assume_init(array) + } +} + +impl Drop for ArrayBuilder { + fn drop(&mut self) { + unsafe { + ptr::drop_in_place( + // Same cast as MaybeUninit::slice_assume_init_mut + self.array.get_unchecked_mut(..self.position) as *mut [MaybeUninit] + as *mut [T], + ); + } + } +} + +/// **UNSAFE**: Consumes an array one element at a time. +/// +/// You MUST increment the position while iterating and any leftover elements +/// will be dropped if position does not go to N +pub struct ArrayConsumer { + array: ManuallyDrop>, + position: usize, +} + +impl ArrayConsumer { + /// Give ownership of the array to the consumer + #[inline(always)] + pub const fn new(array: GenericArray) -> ArrayConsumer { + ArrayConsumer { + array: ManuallyDrop::new(array), + position: 0, + } + } + + /// Creates an iterator and mutable reference to the internal position + /// to keep track of consumed elements. + /// + /// You MUST increment the position as you iterate to mark off consumed elements. + #[inline(always)] + pub unsafe fn iter_position(&mut self) -> (slice::Iter, &mut usize) { + (self.array.iter(), &mut self.position) + } +} + +impl Drop for ArrayConsumer { + fn drop(&mut self) { + unsafe { + ptr::drop_in_place(self.array.get_unchecked_mut(self.position..)); + } + } +} diff --git a/src/iter.rs b/src/iter.rs index 46bb04b5b9..29d40e4e58 100644 --- a/src/iter.rs +++ b/src/iter.rs @@ -5,8 +5,8 @@ use core::iter::FusedIterator; use core::mem::ManuallyDrop; use core::{cmp, fmt, mem, ptr}; -/// An iterator that moves out of a `GenericArray` -pub struct GenericArrayIter> { +/// An iterator that moves out of a [`GenericArray`] +pub struct GenericArrayIter { // Invariants: index <= index_back <= N // Only values in array[index..index_back] are alive at any given time. // Values from array[..index] and array[index_back..] are already moved/dropped. @@ -27,30 +27,27 @@ mod test { } } -impl GenericArrayIter -where - N: ArrayLength, -{ +impl GenericArrayIter { /// Returns the remaining items of this iterator as a slice - #[inline] + #[inline(always)] pub fn as_slice(&self) -> &[T] { - &self.array.as_slice()[self.index..self.index_back] + // SAFETY: index and index_back are guaranteed to be within bounds + unsafe { self.array.get_unchecked(self.index..self.index_back) } } /// Returns the remaining items of this iterator as a mutable slice - #[inline] + #[inline(always)] pub fn as_mut_slice(&mut self) -> &mut [T] { - &mut self.array.as_mut_slice()[self.index..self.index_back] + // SAFETY: index and index_back are guaranteed to be within bounds + unsafe { self.array.get_unchecked_mut(self.index..self.index_back) } } } -impl IntoIterator for GenericArray -where - N: ArrayLength, -{ +impl IntoIterator for GenericArray { type Item = T; type IntoIter = GenericArrayIter; + #[inline] fn into_iter(self) -> Self::IntoIter { GenericArrayIter { array: ManuallyDrop::new(self), @@ -61,10 +58,7 @@ where } // Based on work in rust-lang/rust#49000 -impl fmt::Debug for GenericArrayIter -where - N: ArrayLength, -{ +impl fmt::Debug for GenericArrayIter { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_tuple("GenericArrayIter") .field(&self.as_slice()) @@ -72,28 +66,16 @@ where } } -impl Drop for GenericArrayIter -where - N: ArrayLength, -{ - #[inline] +impl Drop for GenericArrayIter { fn drop(&mut self) { - if mem::needs_drop::() { - // Drop values that are still alive. - for p in self.as_mut_slice() { - unsafe { - ptr::drop_in_place(p); - } - } + unsafe { + ptr::drop_in_place(self.as_mut_slice()); } } } // Based on work in rust-lang/rust#49000 -impl Clone for GenericArrayIter -where - N: ArrayLength, -{ +impl Clone for GenericArrayIter { fn clone(&self) -> Self { // This places all cloned elements at the start of the new array iterator, // not at their original indices. @@ -101,7 +83,7 @@ where let mut array = unsafe { ptr::read(&self.array) }; let mut index_back = 0; - for (dst, src) in array.as_mut_slice().into_iter().zip(self.as_slice()) { + for (dst, src) in array.as_mut_slice().iter_mut().zip(self.as_slice()) { unsafe { ptr::write(dst, src.clone()) }; index_back += 1; } @@ -114,10 +96,7 @@ where } } -impl Iterator for GenericArrayIter -where - N: ArrayLength, -{ +impl Iterator for GenericArrayIter { type Item = T; #[inline] @@ -133,6 +112,7 @@ where } } + #[inline] fn fold(mut self, init: B, mut f: F) -> B where F: FnMut(B, Self::Item) -> B, @@ -144,7 +124,7 @@ where index_back, } = self; - let remaining = &array[*index..index_back]; + let remaining = array.get_unchecked(*index..index_back); remaining.iter().fold(init, |acc, src| { let value = ptr::read(src); @@ -155,35 +135,39 @@ where }) }; - // ensure the drop happens here after iteration - drop(self); + // The current iterator is now empty after the remaining items are + // consumed by the above folding. Dropping it is unnecessary, + // so avoid the drop codegen and forget it instead. The iterator + // will still drop on panics from `f`, of course. + // + // Furthermore, putting `forget` here at the end ensures the above + // destructuring never moves by value, so its behavior on drop remains intact. + mem::forget(self); ret } - #[inline] + #[inline(always)] fn size_hint(&self) -> (usize, Option) { let len = self.len(); (len, Some(len)) } - #[inline] + #[inline(always)] fn count(self) -> usize { self.len() } fn nth(&mut self, n: usize) -> Option { // First consume values prior to the nth. - let ndrop = cmp::min(n, self.len()); + let next_index = self.index + cmp::min(n, self.len()); - for p in &mut self.array[self.index..self.index + ndrop] { - self.index += 1; - - unsafe { - ptr::drop_in_place(p); - } + unsafe { + ptr::drop_in_place(self.array.get_unchecked_mut(self.index..next_index)); } + self.index = next_index; + self.next() } @@ -194,10 +178,8 @@ where } } -impl DoubleEndedIterator for GenericArrayIter -where - N: ArrayLength, -{ +impl DoubleEndedIterator for GenericArrayIter { + #[inline] fn next_back(&mut self) -> Option { if self.index < self.index_back { self.index_back -= 1; @@ -208,6 +190,7 @@ where } } + #[inline] fn rfold(mut self, init: B, mut f: F) -> B where F: FnMut(B, Self::Item) -> B, @@ -219,7 +202,7 @@ where ref mut index_back, } = self; - let remaining = &array[index..*index_back]; + let remaining = array.get_unchecked(index..*index_back); remaining.iter().rfold(init, |acc, src| { let value = ptr::read(src); @@ -230,22 +213,32 @@ where }) }; - // ensure the drop happens here after iteration - drop(self); + // Same as `fold` + mem::forget(self); ret } + + fn nth_back(&mut self, n: usize) -> Option { + let next_back = self.index_back - cmp::min(n, self.len()); + + unsafe { + ptr::drop_in_place(self.array.get_unchecked_mut(next_back..self.index_back)); + } + + self.index_back = next_back; + + self.next_back() + } } -impl ExactSizeIterator for GenericArrayIter -where - N: ArrayLength, -{ +impl ExactSizeIterator for GenericArrayIter { + #[inline] fn len(&self) -> usize { self.index_back - self.index } } -impl FusedIterator for GenericArrayIter where N: ArrayLength {} +impl FusedIterator for GenericArrayIter {} // TODO: Implement `TrustedLen` when stabilized diff --git a/src/lib.rs b/src/lib.rs index 662fa519b0..f1109ed3e4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,67 +1,86 @@ //! This crate implements a structure that can be used as a generic array type. -//! Core Rust array types `[T; N]` can't be used generically with -//! respect to `N`, so for example this: +//! +//! Before Rust 1.51, arrays `[T; N]` were problematic in that they couldn't be generic with respect to the length `N`, so this wouldn't work: //! //! ```rust{compile_fail} -//! struct Foo { -//! data: [T; N] +//! struct Foo { +//! data: [i32; N], //! } //! ``` //! -//! won't work. -//! -//! **generic-array** exports a `GenericArray` type, which lets -//! the above be implemented as: +//! Since 1.51, the below syntax is valid: //! //! ```rust -//! use generic_array::{ArrayLength, GenericArray}; +//! struct Foo { +//! data: [i32; N], +//! } +//! ``` +//! +//! However, the const-generics we have as of writing this are still the minimum-viable product (`min_const_generics`), so many situations still result in errors, such as this example: //! -//! struct Foo> { -//! data: GenericArray +//! ```compile_fail +//! # struct Foo { +//! # data: [i32; N], +//! # } +//! trait Bar { +//! const LEN: usize; +//! +//! // Error: cannot perform const operation using `Self` +//! fn bar(&self) -> Foo<{ Self::LEN }>; //! } //! ``` //! -//! The `ArrayLength` trait is implemented by default for -//! [unsigned integer types](../typenum/uint/index.html) from -//! [typenum](../typenum/index.html): +//! **generic-array** defines a new trait [`ArrayLength`] and a struct [`GenericArray`](GenericArray), +//! which lets the above be implemented as: //! //! ```rust -//! # use generic_array::{ArrayLength, GenericArray}; -//! use generic_array::typenum::U5; +//! use generic_array::{GenericArray, ArrayLength}; //! -//! struct Foo> { +//! struct Foo { //! data: GenericArray //! } //! -//! # fn main() { -//! let foo = Foo::{data: GenericArray::default()}; -//! # } +//! trait Bar { +//! type LEN: ArrayLength; +//! fn bar(&self) -> Foo; +//! } //! ``` //! -//! For example, `GenericArray` would work almost like `[T; 5]`: +//! The [`ArrayLength`] trait is implemented for +//! [unsigned integer types](typenum::Unsigned) from +//! [typenum]. For example, [`GenericArray`] would work almost like `[T; 5]`: //! //! ```rust //! # use generic_array::{ArrayLength, GenericArray}; //! use generic_array::typenum::U5; //! -//! struct Foo> { +//! struct Foo { //! data: GenericArray //! } //! -//! # fn main() { -//! let foo = Foo::{data: GenericArray::default()}; -//! # } +//! let foo = Foo:: { data: GenericArray::default() }; //! ``` //! -//! For ease of use, an `arr!` macro is provided - example below: +//! The `arr!` macro is provided to allow easier creation of literal arrays, as shown below: //! -//! ``` -//! use generic_array::arr; -//! use generic_array::typenum; -//! # fn main() { -//! let array = arr![u32; 1, 2, 3]; +//! ```rust +//! # use generic_array::arr; +//! let array = arr![1, 2, 3]; +//! // array: GenericArray //! assert_eq!(array[2], 3); -//! # } +//! ``` +//! ## Feature flags +//! +//! ```toml +//! [dependencies.generic-array] +//! features = [ +//! "more_lengths", # Expands From/Into implementation for more array lengths +//! "serde", # Serialize/Deserialize implementation +//! "zeroize", # Zeroize implementation for setting array elements to zero +//! "const-default", # Compile-time const default value support via trait +//! "alloc". # Enables From/TryFrom implementations between GenericArray and Vec/Box<[T]> +//! "faster-hex" # Enables internal use of the `faster-hex` crate for faster hex encoding via SIMD +//! ] //! ``` #![deny(missing_docs)] @@ -69,22 +88,18 @@ #![no_std] #![cfg_attr(docsrs, feature(doc_auto_cfg))] -#[cfg(feature = "const-default")] -extern crate const_default; - -#[cfg(feature = "serde")] -extern crate serde; - -#[cfg(feature = "zeroize")] -extern crate zeroize; - -#[cfg(test)] -extern crate bincode; - pub extern crate typenum; +#[doc(hidden)] +#[cfg(feature = "alloc")] +pub extern crate alloc; + mod hex; mod impls; +mod iter; + +#[cfg(feature = "alloc")] +mod impl_alloc; #[cfg(feature = "const-default")] mod impl_const_default; @@ -101,29 +116,147 @@ use core::mem::{ManuallyDrop, MaybeUninit}; use core::ops::{Deref, DerefMut}; use core::{mem, ptr, slice}; use typenum::bit::{B0, B1}; +use typenum::generic_const_mappings::{Const, ToUInt}; use typenum::uint::{UInt, UTerm, Unsigned}; +#[doc(hidden)] #[cfg_attr(test, macro_use)] pub mod arr; + pub mod functional; -pub mod iter; pub mod sequence; +mod internal; +use internal::{ArrayBuilder, ArrayConsumer, Sealed}; + +// re-export to allow doc_auto_cfg to handle it +#[cfg(feature = "internals")] +pub mod internals { + //! Very unsafe internal functionality. + //! + //! These are used internally for building and consuming generic arrays. WHen used correctly, + //! they can ensure elements are correctly dropped if something panics while using them. + + pub use crate::internal::{ArrayBuilder, ArrayConsumer}; +} + use self::functional::*; -pub use self::iter::GenericArrayIter; use self::sequence::*; -/// Trait making `GenericArray` work, marking types to be used as length of an array -pub unsafe trait ArrayLength: Unsigned { - /// Associated type representing the array type for the number - type ArrayType; +pub use self::iter::GenericArrayIter; + +/// Trait used to define the number of elements in a [`GenericArray`]. +/// +/// `ArrayLength` is a superset of [`typenum::Unsigned`]. +/// +/// Consider `N: ArrayLength` to be equivalent to `const N: usize` +/// +/// ``` +/// # use generic_array::{GenericArray, ArrayLength}; +/// fn foo(arr: GenericArray) -> i32 { +/// arr.iter().sum() +/// } +/// ``` +/// is equivalent to: +/// ``` +/// fn foo(arr: [i32; N]) -> i32 { +/// arr.iter().sum() +/// } +/// ``` +/// +/// # Safety +/// +/// This trait is effectively sealed due to only being allowed on [`Unsigned`] types, +/// and therefore cannot be implemented in user code. +pub unsafe trait ArrayLength: Unsigned + 'static { + /// Associated type representing the underlying contiguous memory + /// that constitutes an array with the given number of elements. + /// + /// This is an implementation detail, but is required to be public in cases where certain attributes + /// of the inner type of [`GenericArray`] cannot be proven, such as [`Copy`] bounds. + /// + /// [`Copy`] example: + /// ``` + /// # use generic_array::{GenericArray, ArrayLength}; + /// struct MyType { + /// data: GenericArray, + /// } + /// + /// impl Clone for MyType where N::ArrayType: Copy { + /// fn clone(&self) -> Self { MyType { ..*self } } + /// } + /// + /// impl Copy for MyType where N::ArrayType: Copy {} + /// ``` + /// + /// Alternatively, using the entire `GenericArray` type as the bounds works: + /// ```ignore + /// where GenericArray: Copy + /// ``` + type ArrayType: Sealed; } -unsafe impl ArrayLength for UTerm { +unsafe impl ArrayLength for UTerm { #[doc(hidden)] - type ArrayType = [T; 0]; + type ArrayType = [T; 0]; +} + +/// Implemented for types which can have an associated [`ArrayLength`], +/// such as [`Const`] for use with const-generics. +/// +/// ``` +/// use generic_array::{GenericArray, IntoArrayLength, ConstArrayLength, typenum::Const}; +/// +/// fn some_array_interopt(value: [u32; N]) -> GenericArray> +/// where +/// Const: IntoArrayLength, +/// { +/// let ga = GenericArray::from(value); +/// // do stuff +/// ga +/// } +/// ``` +/// +/// This is mostly to simplify the `where` bounds, equivalent to: +/// +/// ``` +/// use generic_array::{GenericArray, ArrayLength, typenum::{Const, U, ToUInt}}; +/// +/// fn some_array_interopt(value: [u32; N]) -> GenericArray> +/// where +/// Const: ToUInt, +/// U: ArrayLength, +/// { +/// let ga = GenericArray::from(value); +/// // do stuff +/// ga +/// } +/// ``` +pub trait IntoArrayLength { + /// The associated `ArrayLength` + type ArrayLength: ArrayLength; } +impl IntoArrayLength for Const +where + Const: ToUInt, + typenum::U: ArrayLength, +{ + type ArrayLength = typenum::U; +} + +impl IntoArrayLength for N +where + N: ArrayLength, +{ + type ArrayLength = Self; +} + +/// Associated [`ArrayLength`] for one [`Const`] +/// +/// See [`IntoArrayLength`] for more information. +pub type ConstArrayLength = as IntoArrayLength>::ArrayLength; + /// Internal type used to generate a struct of appropriate size #[allow(dead_code)] #[repr(C)] @@ -134,18 +267,6 @@ pub struct GenericArrayImplEven { _marker: PhantomData, } -impl Clone for GenericArrayImplEven { - fn clone(&self) -> GenericArrayImplEven { - GenericArrayImplEven { - parent1: self.parent1.clone(), - parent2: self.parent2.clone(), - _marker: PhantomData, - } - } -} - -impl Copy for GenericArrayImplEven {} - /// Internal type used to generate a struct of appropriate size #[allow(dead_code)] #[repr(C)] @@ -156,166 +277,179 @@ pub struct GenericArrayImplOdd { data: T, } +impl Clone for GenericArrayImplEven { + #[inline(always)] + fn clone(&self) -> GenericArrayImplEven { + // Clone is never called on the GenericArrayImpl types, + // as we use `self.map(clone)` elsewhere. This helps avoid + // extra codegen for recursive clones when they are never used. + unsafe { core::hint::unreachable_unchecked() } + } +} + impl Clone for GenericArrayImplOdd { + #[inline(always)] fn clone(&self) -> GenericArrayImplOdd { - GenericArrayImplOdd { - parent1: self.parent1.clone(), - parent2: self.parent2.clone(), - data: self.data.clone(), - } + unsafe { core::hint::unreachable_unchecked() } } } +// Even if Clone is never used, they can still be byte-copyable. +impl Copy for GenericArrayImplEven {} impl Copy for GenericArrayImplOdd {} -unsafe impl> ArrayLength for UInt { +impl Sealed for GenericArrayImplEven {} +impl Sealed for GenericArrayImplOdd {} + +unsafe impl ArrayLength for UInt { #[doc(hidden)] - type ArrayType = GenericArrayImplEven; + type ArrayType = GenericArrayImplEven>; } -unsafe impl> ArrayLength for UInt { +unsafe impl ArrayLength for UInt { #[doc(hidden)] - type ArrayType = GenericArrayImplOdd; + type ArrayType = GenericArrayImplOdd>; } -/// Struct representing a generic array - `GenericArray` works like [T; N] -#[allow(dead_code)] +/// Struct representing a generic array - `GenericArray` works like `[T; N]` +/// +/// For how to implement [`Copy`] on structs using a generic-length `GenericArray` internally, see +/// the docs for [`ArrayLength::ArrayType`]. +/// +/// # Usage Notes +/// +/// ### Intialization +/// +/// Initialization of known-length `GenericArray`s can be done via the [`arr![]`](arr!) macro, +/// or [`from_array`](GenericArray::from_array)/[`from_slice`](GenericArray::from_slice). +/// +/// For generic arrays of unknown/generic length, several safe methods are included to initialize +/// them, such as the [`GenericSequence::generate`] method: +/// +/// ```rust +/// use generic_array::{GenericArray, sequence::GenericSequence, typenum, arr}; +/// +/// let evens: GenericArray = +/// GenericArray::generate(|i: usize| i as i32 * 2); +/// +/// assert_eq!(evens, arr![0, 2, 4, 6]); +/// ``` +/// +/// Furthermore, [`FromIterator`] and [`try_from_iter`](GenericArray::try_from_iter) exist to construct them +/// from iterators, but will panic/fail if not given exactly the correct number of elements. +/// +/// ### Utilities +/// +/// The [`GenericSequence`], [`FunctionalSequence`], [`Lengthen`], [`Shorten`], [`Split`], and [`Concat`] traits implement +/// some common operations on generic arrays. +/// +/// ### Optimizations +/// +/// Prefer to use the slice iterators like `.iter()`/`.iter_mut()` rather than by-value [`IntoIterator`]/[`GenericArrayIter`] if you can. +/// Slices optimize better. Using the [`FunctionalSequence`] methods also optimize well. +/// +/// # How it works +/// +/// The `typenum` crate uses Rust's type system to define binary integers as nested types, +/// and allows for operations which can be applied to those type-numbers, such as `Add`, `Sub`, etc. +/// +/// e.g. `6` would be `UInt, B1>, B0>` +/// +/// `generic-array` uses this nested type to recursively allocate contiguous elements, statically. +/// The [`ArrayLength`] trait is implemented on `UInt`, `UInt` and `UTerm`, +/// which correspond to even, odd and zero numeric values, respectively. +/// Together, these three cover all cases of `Unsigned` integers from `typenum`. +/// For `UInt` and `UInt`, it peels away the highest binary digit and +/// builds up a recursive structure that looks almost like a binary tree. +/// Then, within `GenericArray`, the recursive structure is reinterpreted as a contiguous +/// chunk of memory and allowing access to it as a slice. +/// +///
+/// Expand for internal structure demonstration +/// +/// For example, `GenericArray` more or less expands to (at compile time): +/// +/// ```ignore +/// GenericArray { +/// // 6 = UInt, B1>, B0> +/// data: EvenData { +/// // 3 = UInt, B1> +/// left: OddData { +/// // 1 = UInt +/// left: OddData { +/// left: (), // UTerm +/// right: (), // UTerm +/// data: T, // Element 0 +/// }, +/// // 1 = UInt +/// right: OddData { +/// left: (), // UTerm +/// right: (), // UTerm +/// data: T, // Element 1 +/// }, +/// data: T // Element 2 +/// }, +/// // 3 = UInt, B1> +/// right: OddData { +/// // 1 = UInt +/// left: OddData { +/// left: (), // UTerm +/// right: (), // UTerm +/// data: T, // Element 3 +/// }, +/// // 1 = UInt +/// right: OddData { +/// left: (), // UTerm +/// right: (), // UTerm +/// data: T, // Element 4 +/// }, +/// data: T // Element 5 +/// } +/// } +/// } +/// ``` +/// +/// This has the added benefit of only being `log2(N)` deep, which is important for things like `Drop` +/// to avoid stack overflows, since we can't implement `Drop` manually. +/// +/// Then, we take the contiguous block of data and cast it to `*const T` or `*mut T` and use it as a slice: +/// +/// ```ignore +/// unsafe { +/// slice::from_raw_parts( +/// self as *const GenericArray as *const T, +/// ::USIZE +/// ) +/// } +/// ``` +/// +///
#[repr(transparent)] -pub struct GenericArray> { - data: U::ArrayType, +pub struct GenericArray { + #[allow(dead_code)] // data is never accessed directly + data: N::ArrayType, } -unsafe impl> Send for GenericArray {} -unsafe impl> Sync for GenericArray {} +unsafe impl Send for GenericArray {} +unsafe impl Sync for GenericArray {} -impl Deref for GenericArray -where - N: ArrayLength, -{ +impl Deref for GenericArray { type Target = [T]; #[inline(always)] fn deref(&self) -> &[T] { - unsafe { slice::from_raw_parts(self as *const Self as *const T, N::USIZE) } + GenericArray::as_slice(self) } } -impl DerefMut for GenericArray -where - N: ArrayLength, -{ +impl DerefMut for GenericArray { #[inline(always)] fn deref_mut(&mut self) -> &mut [T] { - unsafe { slice::from_raw_parts_mut(self as *mut Self as *mut T, N::USIZE) } + GenericArray::as_mut_slice(self) } } -/// Creates an array one element at a time using a mutable iterator -/// you can write to with `ptr::write`. -/// -/// Increment the position while iterating to mark off created elements, -/// which will be dropped if `into_inner` is not called. -#[doc(hidden)] -pub struct ArrayBuilder> { - array: MaybeUninit>, - position: usize, -} - -impl> ArrayBuilder { - #[doc(hidden)] - #[inline] - pub unsafe fn new() -> ArrayBuilder { - ArrayBuilder { - array: MaybeUninit::uninit(), - position: 0, - } - } - - /// Creates a mutable iterator for writing to the array using `ptr::write`. - /// - /// Increment the position value given as a mutable reference as you iterate - /// to mark how many elements have been created. - #[doc(hidden)] - #[inline] - pub unsafe fn iter_position(&mut self) -> (slice::IterMut, &mut usize) { - ( - (&mut *self.array.as_mut_ptr()).iter_mut(), - &mut self.position, - ) - } - - /// When done writing (assuming all elements have been written to), - /// get the inner array. - #[doc(hidden)] - #[inline] - pub unsafe fn into_inner(self) -> GenericArray { - let array = ptr::read(&self.array); - - mem::forget(self); - - array.assume_init() - } -} - -impl> Drop for ArrayBuilder { - fn drop(&mut self) { - if mem::needs_drop::() { - unsafe { - for value in &mut (&mut *self.array.as_mut_ptr())[..self.position] { - ptr::drop_in_place(value); - } - } - } - } -} - -/// Consumes an array. -/// -/// Increment the position while iterating and any leftover elements -/// will be dropped if position does not go to N -#[doc(hidden)] -pub struct ArrayConsumer> { - array: ManuallyDrop>, - position: usize, -} - -impl> ArrayConsumer { - #[doc(hidden)] - #[inline] - pub unsafe fn new(array: GenericArray) -> ArrayConsumer { - ArrayConsumer { - array: ManuallyDrop::new(array), - position: 0, - } - } - - /// Creates an iterator and mutable reference to the internal position - /// to keep track of consumed elements. - /// - /// Increment the position as you iterate to mark off consumed elements - #[doc(hidden)] - #[inline] - pub unsafe fn iter_position(&mut self) -> (slice::Iter, &mut usize) { - (self.array.iter(), &mut self.position) - } -} - -impl> Drop for ArrayConsumer { - fn drop(&mut self) { - if mem::needs_drop::() { - for value in &mut self.array[self.position..N::USIZE] { - unsafe { - ptr::drop_in_place(value); - } - } - } - } -} - -impl<'a, T: 'a, N> IntoIterator for &'a GenericArray -where - N: ArrayLength, -{ +impl<'a, T: 'a, N: ArrayLength> IntoIterator for &'a GenericArray { type IntoIter = slice::Iter<'a, T>; type Item = &'a T; @@ -324,10 +458,7 @@ where } } -impl<'a, T: 'a, N> IntoIterator for &'a mut GenericArray -where - N: ArrayLength, -{ +impl<'a, T: 'a, N: ArrayLength> IntoIterator for &'a mut GenericArray { type IntoIter = slice::IterMut<'a, T>; type Item = &'a mut T; @@ -336,55 +467,37 @@ where } } -impl FromIterator for GenericArray -where - N: ArrayLength, -{ +impl FromIterator for GenericArray { + /// Create a `GenericArray` from an iterator. + /// + /// Will panic if the number of elements is not exactly the array length. + /// + /// See [`GenericArray::try_from_iter]` for a fallible alternative. fn from_iter(iter: I) -> GenericArray where I: IntoIterator, { - unsafe { - let mut destination = ArrayBuilder::new(); - - { - let (destination_iter, position) = destination.iter_position(); - - iter.into_iter() - .zip(destination_iter) - .for_each(|(src, dst)| { - ptr::write(dst, src); - - *position += 1; - }); - } - - if destination.position < N::USIZE { - from_iter_length_fail(destination.position, N::USIZE); - } - - destination.into_inner() + match Self::try_from_iter(iter) { + Ok(res) => res, + Err(_) => from_iter_length_fail(N::USIZE), } } } #[inline(never)] #[cold] -fn from_iter_length_fail(length: usize, expected: usize) -> ! { - panic!( - "GenericArray::from_iter received {} elements but expected {}", - length, expected - ); +pub(crate) fn from_iter_length_fail(length: usize) -> ! { + panic!("GenericArray::from_iter expected {length} items"); } -unsafe impl GenericSequence for GenericArray +unsafe impl GenericSequence for GenericArray where - N: ArrayLength, Self: IntoIterator, { type Length = N; type Sequence = Self; + #[inline(always)] fn generate(mut f: F) -> GenericArray where F: FnMut(usize) -> T, @@ -396,17 +509,16 @@ where let (destination_iter, position) = destination.iter_position(); destination_iter.enumerate().for_each(|(i, dst)| { - ptr::write(dst, f(i)); - + dst.write(f(i)); *position += 1; }); } - destination.into_inner() + destination.assume_init() } } - #[doc(hidden)] + #[inline(always)] fn inverted_zip( self, lhs: GenericArray, @@ -416,72 +528,97 @@ where GenericArray: GenericSequence + MappedGenericSequence, Self: MappedGenericSequence, - Self::Length: ArrayLength + ArrayLength, F: FnMut(B, Self::Item) -> U, { unsafe { - let mut left = ArrayConsumer::new(lhs); - let mut right = ArrayConsumer::new(self); - - let (left_array_iter, left_position) = left.iter_position(); - let (right_array_iter, right_position) = right.iter_position(); - - FromIterator::from_iter(left_array_iter.zip(right_array_iter).map(|(l, r)| { - let left_value = ptr::read(l); - let right_value = ptr::read(r); - - *left_position += 1; - *right_position += 1; - - f(left_value, right_value) - })) + if mem::needs_drop::() || mem::needs_drop::() { + let mut left = ArrayConsumer::new(lhs); + let mut right = ArrayConsumer::new(self); + + let (left_array_iter, left_position) = left.iter_position(); + let (right_array_iter, right_position) = right.iter_position(); + + FromIterator::from_iter(left_array_iter.zip(right_array_iter).map(|(l, r)| { + let left_value = ptr::read(l); + let right_value = ptr::read(r); + + *left_position += 1; + *right_position = *left_position; + + f(left_value, right_value) + })) + } else { + // Despite neither needing `Drop`, they may not be `Copy`, so be paranoid + // and avoid anything related to drop anyway. Assume it's moved out on each read. + let left = ManuallyDrop::new(lhs); + let right = ManuallyDrop::new(self); + + // Neither right nor left require `Drop` be called, so choose an iterator that's easily optimized + // + // Note that because ArrayConsumer checks for `needs_drop` itself, if `f` panics then nothing + // would have been done about it anyway. Only the other branch needs `ArrayConsumer` + let res = FromIterator::from_iter(left.iter().zip(right.iter()).map(|(l, r)| { + let left_value = ptr::read(l); + let right_value = ptr::read(r); + + f(left_value, right_value) + })); + + res + } } } - #[doc(hidden)] + #[inline(always)] fn inverted_zip2(self, lhs: Lhs, mut f: F) -> MappedSequence where Lhs: GenericSequence + MappedGenericSequence, Self: MappedGenericSequence, - Self::Length: ArrayLength + ArrayLength, F: FnMut(Lhs::Item, Self::Item) -> U, { unsafe { - let mut right = ArrayConsumer::new(self); + if mem::needs_drop::() { + let mut right = ArrayConsumer::new(self); - let (right_array_iter, right_position) = right.iter_position(); + let (right_array_iter, right_position) = right.iter_position(); - FromIterator::from_iter( - lhs.into_iter() - .zip(right_array_iter) - .map(|(left_value, r)| { - let right_value = ptr::read(r); + FromIterator::from_iter(right_array_iter.zip(lhs).map(|(r, left_value)| { + let right_value = ptr::read(r); - *right_position += 1; + *right_position += 1; - f(left_value, right_value) - }), - ) + f(left_value, right_value) + })) + } else { + let right = ManuallyDrop::new(self); + + // Similar logic to `inverted_zip`'s no-drop branch + let res = FromIterator::from_iter(right.iter().zip(lhs).map(|(r, left_value)| { + let right_value = ptr::read(r); + + f(left_value, right_value) + })); + + res + } } } } -unsafe impl MappedGenericSequence for GenericArray +impl MappedGenericSequence for GenericArray where - N: ArrayLength + ArrayLength, GenericArray: GenericSequence, { type Mapped = GenericArray; } -unsafe impl FunctionalSequence for GenericArray +impl FunctionalSequence for GenericArray where - N: ArrayLength, Self: GenericSequence, { + #[inline(always)] fn map(self, mut f: F) -> MappedSequence where - Self::Length: ArrayLength, Self: MappedGenericSequence, F: FnMut(T) -> U, { @@ -500,18 +637,18 @@ where } } - #[inline] + #[inline(always)] fn zip(self, rhs: Rhs, f: F) -> MappedSequence where Self: MappedGenericSequence, Rhs: MappedGenericSequence>, - Self::Length: ArrayLength + ArrayLength, Rhs: GenericSequence, F: FnMut(T, Rhs::Item) -> U, { rhs.inverted_zip(self, f) } + #[inline(always)] fn fold(self, init: U, mut f: F) -> U where F: FnMut(U, T) -> U, @@ -523,142 +660,341 @@ where array_iter.fold(init, |acc, src| { let value = ptr::read(src); - *position += 1; - f(acc, value) }) } } } -impl GenericArray -where - N: ArrayLength, -{ +impl GenericArray { + /// Returns the number of elements in the array. + /// + /// Equivalent to [`::USIZE`](typenum::Unsigned) where `N` is the array length. + /// + /// Useful for when only a type alias is available. + pub const fn len() -> usize { + N::USIZE + } + /// Extracts a slice containing the entire array. - #[inline] - pub fn as_slice(&self) -> &[T] { - self.deref() + #[inline(always)] + pub const fn as_slice(&self) -> &[T] { + unsafe { slice::from_raw_parts(self as *const Self as *const T, N::USIZE) } } /// Extracts a mutable slice containing the entire array. - #[inline] + #[inline(always)] pub fn as_mut_slice(&mut self) -> &mut [T] { - self.deref_mut() + unsafe { slice::from_raw_parts_mut(self as *mut Self as *mut T, N::USIZE) } } - /// Converts slice to a generic array reference with inferred length; + /// Converts a slice to a generic array reference with inferred length. /// /// # Panics /// /// Panics if the slice is not equal to the length of the array. - #[inline] - pub fn from_slice(slice: &[T]) -> &GenericArray { - slice.into() + /// + /// Consider [`TryFrom`]/[`TryInto`] for a fallible conversion, + /// or [`try_from_slice`](GenericArray::try_from_slice) for use in const expressions. + #[inline(always)] + pub const fn from_slice(slice: &[T]) -> &GenericArray { + if slice.len() != N::USIZE { + panic!("slice.len() != N in GenericArray::from_slice"); + } + + unsafe { &*(slice.as_ptr() as *const GenericArray) } } - /// Converts mutable slice to a mutable generic array reference + /// Converts a slice to a generic array reference with inferred length. + /// + /// This is a fallible alternative to [`from_slice`](GenericArray::from_slice), and can be used in const expressions, + /// but [`TryFrom`]/[`TryInto`] are also available to do the same thing. + #[inline(always)] + pub const fn try_from_slice(slice: &[T]) -> Result<&GenericArray, LengthError> { + if slice.len() != N::USIZE { + return Err(LengthError); + } + + Ok(unsafe { &*(slice.as_ptr() as *const GenericArray) }) + } + + /// Converts a mutable slice to a mutable generic array reference with inferred length. /// /// # Panics /// /// Panics if the slice is not equal to the length of the array. - #[inline] + /// + /// Consider [`TryFrom`]/[`TryInto`] for a fallible conversion. + #[inline(always)] pub fn from_mut_slice(slice: &mut [T]) -> &mut GenericArray { - slice.into() + assert_eq!( + slice.len(), + N::USIZE, + "slice.len() != N in GenericArray::from_mut_slice" + ); + + unsafe { &mut *(slice.as_mut_ptr() as *mut GenericArray) } + } + + /// Converts a mutable slice to a mutable generic array reference with inferred length. + /// + /// This is a fallible alternative to [`from_mut_slice`](GenericArray::from_mut_slice), + /// and current just calls [`TryFrom`] internally, but is provided for + /// future compatibility when we can make it const. + #[inline(always)] + pub fn try_from_mut_slice(slice: &mut [T]) -> Result<&mut GenericArray, LengthError> { + TryFrom::try_from(slice) } -} -impl<'a, T, N: ArrayLength> From<&'a [T]> for &'a GenericArray { - /// Converts slice to a generic array reference with inferred length; + /// Converts a slice of `T` elements into a slice of `GenericArray` chunks. + /// + /// Any remaining elements that do not fill the array will be returned as a second slice. /// /// # Panics /// - /// Panics if the slice is not equal to the length of the array. - #[inline] - fn from(slice: &[T]) -> &GenericArray { - assert_eq!(slice.len(), N::USIZE); + /// Panics if `N` is `U0` _AND_ the input slice is not empty. + pub const fn chunks_from_slice(slice: &[T]) -> (&[GenericArray], &[T]) { + if N::USIZE == 0 { + assert!(slice.is_empty(), "GenericArray length N must be non-zero"); + return (&[], &[]); + } - unsafe { &*(slice.as_ptr() as *const GenericArray) } + // NOTE: Using `slice.split_at` adds an unnecessary assert + let num_chunks = slice.len() / N::USIZE; // integer division + let num_in_chunks = num_chunks * N::USIZE; + let num_remainder = slice.len() - num_in_chunks; + + unsafe { + ( + slice::from_raw_parts(slice.as_ptr() as *const GenericArray, num_chunks), + slice::from_raw_parts(slice.as_ptr().add(num_in_chunks), num_remainder), + ) + } } -} -impl<'a, T, N: ArrayLength> From<&'a mut [T]> for &'a mut GenericArray { - /// Converts mutable slice to a mutable generic array reference + /// Converts a mutable slice of `T` elements into a mutable slice `GenericArray` chunks. + /// + /// Any remaining elements that do not fill the array will be returned as a second slice. /// /// # Panics /// - /// Panics if the slice is not equal to the length of the array. - #[inline] - fn from(slice: &mut [T]) -> &mut GenericArray { - assert_eq!(slice.len(), N::USIZE); + /// Panics if `N` is `U0` _AND_ the input slice is not empty. + pub fn chunks_from_slice_mut(slice: &mut [T]) -> (&mut [GenericArray], &mut [T]) { + if N::USIZE == 0 { + assert!(slice.is_empty(), "GenericArray length N must be non-zero"); + return (&mut [], &mut []); + } - unsafe { &mut *(slice.as_mut_ptr() as *mut GenericArray) } + // NOTE: Using `slice.split_at_mut` adds an unnecessary assert + let num_chunks = slice.len() / N::USIZE; // integer division + let num_in_chunks = num_chunks * N::USIZE; + let num_remainder = slice.len() - num_in_chunks; + + unsafe { + ( + slice::from_raw_parts_mut( + slice.as_mut_ptr() as *mut GenericArray, + num_chunks, + ), + slice::from_raw_parts_mut(slice.as_mut_ptr().add(num_in_chunks), num_remainder), + ) + } + } + + /// Convert a slice of `GenericArray` into a slice of `T`, effectively flattening the arrays. + #[inline(always)] + pub const fn slice_from_chunks(slice: &[GenericArray]) -> &[T] { + unsafe { slice::from_raw_parts(slice.as_ptr() as *const T, slice.len() * N::USIZE) } + } + + /// Convert a slice of `GenericArray` into a slice of `T`, effectively flattening the arrays. + #[inline(always)] + pub fn slice_from_chunks_mut(slice: &mut [GenericArray]) -> &mut [T] { + unsafe { slice::from_raw_parts_mut(slice.as_mut_ptr() as *mut T, slice.len() * N::USIZE) } + } + + /// Convert a native array into `GenericArray` of the same length and type. + /// + /// This is the `const` equivalent of using the standard [`From`]/[`Into`] traits methods. + #[inline(always)] + pub const fn from_array(value: [T; U]) -> Self + where + Const: IntoArrayLength, + { + unsafe { crate::const_transmute(value) } + } + + /// Convert the `GenericArray` into a native array of the same length and type. + /// + /// This is the `const` equivalent of using the standard [`From`]/[`Into`] traits methods. + #[inline(always)] + pub const fn into_array(self) -> [T; U] + where + Const: IntoArrayLength, + { + unsafe { crate::const_transmute(self) } + } + + /// Convert a slice of native arrays into a slice of `GenericArray`s. + #[inline(always)] + pub const fn from_chunks(chunks: &[[T; U]]) -> &[GenericArray] + where + Const: IntoArrayLength, + { + unsafe { mem::transmute(chunks) } + } + + /// Convert a mutable slice of native arrays into a mutable slice of `GenericArray`s. + #[inline(always)] + pub fn from_chunks_mut(chunks: &mut [[T; U]]) -> &mut [GenericArray] + where + Const: IntoArrayLength, + { + unsafe { mem::transmute(chunks) } + } + + /// Converts a slice `GenericArray` into a slice of `[T; N]` + #[inline(always)] + pub const fn into_chunks(chunks: &[GenericArray]) -> &[[T; U]] + where + Const: IntoArrayLength, + { + unsafe { mem::transmute(chunks) } + } + + /// Converts a mutable slice `GenericArray` into a mutable slice of `[T; N]` + #[inline(always)] + pub fn into_chunks_mut(chunks: &mut [GenericArray]) -> &mut [[T; U]] + where + Const: IntoArrayLength, + { + unsafe { mem::transmute(chunks) } } } -impl GenericArray -where - N: ArrayLength, -{ - /// Construct a `GenericArray` from a slice by cloning its content +impl GenericArray { + /// Create a new array of `MaybeUninit` items, in an uninitialized state. /// - /// # Panics + /// See [`GenericArray::assume_init`] for a full example. + #[inline(always)] + #[allow(clippy::uninit_assumed_init)] + pub const fn uninit() -> GenericArray, N> { + unsafe { + // SAFETY: An uninitialized `[MaybeUninit<_>; N]` is valid, same as regular array + MaybeUninit::, N>>::uninit().assume_init() + } + } + + /// Extracts the values from a generic array of `MaybeUninit` containers. /// - /// Panics if the slice is not equal to the length of the array. - #[inline] - pub fn clone_from_slice(list: &[T]) -> GenericArray { - Self::from_exact_iter(list.iter().cloned()) - .expect("Slice must be the same length as the array") + /// # Safety + /// + /// It is up to the caller to guarantee that all elements of the array are in an initialized state. + /// + /// # Example + /// + /// ``` + /// # use core::mem::MaybeUninit; + /// # use generic_array::{GenericArray, typenum::U3, arr}; + /// let mut array: GenericArray, U3> = GenericArray::uninit(); + /// array[0].write(0); + /// array[1].write(1); + /// array[2].write(2); + /// + /// // SAFETY: Now safe as we initialised all elements + /// let array = unsafe { + /// GenericArray::assume_init(array) + /// }; + /// + /// assert_eq!(array, arr![0, 1, 2]); + /// ``` + #[inline(always)] + pub const unsafe fn assume_init(array: GenericArray, N>) -> Self { + const_transmute::<_, MaybeUninit>>(array).assume_init() } } -impl GenericArray -where - N: ArrayLength, -{ - /// Creates a new `GenericArray` instance from an iterator with a specific size. +/// Error for [`TryFrom`] and [`try_from_iter`](GenericArray::try_from_iter) +#[derive(Debug, Clone, Copy)] +pub struct LengthError; + +// TODO: Impl core::error::Error when when https://github.com/rust-lang/rust/issues/103765 is finished + +impl core::fmt::Display for LengthError { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.write_str("LengthError: Slice or iterator does not match GenericArray length") + } +} + +impl<'a, T, N: ArrayLength> TryFrom<&'a [T]> for &'a GenericArray { + type Error = LengthError; + + #[inline(always)] + fn try_from(slice: &'a [T]) -> Result { + GenericArray::try_from_slice(slice) + } +} + +impl<'a, T, N: ArrayLength> TryFrom<&'a mut [T]> for &'a mut GenericArray { + type Error = LengthError; + + #[inline(always)] + fn try_from(slice: &'a mut [T]) -> Result { + match slice.len() == N::USIZE { + true => Ok(GenericArray::from_mut_slice(slice)), + false => Err(LengthError), + } + } +} + +impl GenericArray { + /// Fallible equivalent of [`FromIterator::from_iter`] /// - /// Returns `None` if the size is not equal to the number of elements in the `GenericArray`. - pub fn from_exact_iter(iter: I) -> Option + /// Given iterator must yield exactly `N` elements or an error will be returned. Using [`.take(N)`](Iterator::take) + /// with an iterator longer than the array may be helpful. + #[inline] + pub fn try_from_iter(iter: I) -> Result where I: IntoIterator, { let mut iter = iter.into_iter(); + // pre-checks + match iter.size_hint() { + // if the lower bound is greater than N, array will overflow + (n, _) if n > N::USIZE => return Err(LengthError), + // if the upper bound is smaller than N, array cannot be filled + (_, Some(n)) if n < N::USIZE => return Err(LengthError), + _ => {} + } + unsafe { let mut destination = ArrayBuilder::new(); - { - let (destination_iter, position) = destination.iter_position(); - - destination_iter.zip(&mut iter).for_each(|(dst, src)| { - ptr::write(dst, src); - - *position += 1; - }); + destination.extend(&mut iter); - // The iterator produced fewer than `N` elements. - if *position != N::USIZE { - return None; - } - - // The iterator produced more than `N` elements. - if iter.next().is_some() { - return None; - } + if !destination.is_full() || iter.next().is_some() { + return Err(LengthError); } - Some(destination.into_inner()) + Ok(destination.assume_init()) } } } -/// A reimplementation of the `transmute` function, avoiding problems -/// when the compiler can't prove equal sizes. -#[inline] -#[doc(hidden)] -pub const unsafe fn transmute(a: A) -> B { +/// A const reimplementation of the [`transmute`](core::mem::transmute) function, +/// avoiding problems when the compiler can't prove equal sizes. +/// +/// # Safety +/// Treat this the same as [`transmute`](core::mem::transmute), or (preferably) don't use it at all. +#[inline(always)] +#[cfg_attr(not(feature = "internals"), doc(hidden))] +pub const unsafe fn const_transmute(a: A) -> B { + if mem::size_of::
() != mem::size_of::() { + panic!("Size mismatch for generic_array::const_transmute"); + } + #[repr(C)] union Union { a: ManuallyDrop, @@ -690,14 +1026,14 @@ mod test { fn test_assembly() { use crate::functional::*; - let a = black_box(arr![i32; 1, 3, 5, 7]); - let b = black_box(arr![i32; 2, 4, 6, 8]); + let a = black_box(arr![1, 3, 5, 7]); + let b = black_box(arr![2, 4, 6, 8]); let c = (&a).zip(b, |l, r| l + r); let d = a.fold(0, |a, x| a + x); - assert_eq!(c, arr![i32; 3, 7, 11, 15]); + assert_eq!(c, arr![3, 7, 11, 15]); assert_eq!(d, 16); } diff --git a/src/sequence.rs b/src/sequence.rs index 5c51862be6..de04f7e8e9 100644 --- a/src/sequence.rs +++ b/src/sequence.rs @@ -9,11 +9,16 @@ use typenum::operator_aliases::*; /// Defines some sequence with an associated length and iteration capabilities. /// /// This is useful for passing N-length generic arrays as generics. +/// +/// # Safety +/// Care must be taken when implementing such that methods are safe. +/// +/// Lengths must match, and element drop on panic must be handled. pub unsafe trait GenericSequence: Sized + IntoIterator { /// `GenericArray` associated length - type Length: ArrayLength; + type Length: ArrayLength; - /// Concrete sequence type used in conjuction with reference implementations of `GenericSequence` + /// Owned sequence type used in conjuction with reference implementations of `GenericSequence` type Sequence: GenericSequence + FromIterator; /// Initializes a new sequence instance using the given function. @@ -24,7 +29,11 @@ pub unsafe trait GenericSequence: Sized + IntoIterator { where F: FnMut(usize) -> T; - #[doc(hidden)] + /// Treats `self` as the right-hand operand in a zip operation + /// + /// This is optimized for stack-allocated `GenericArray`s + #[cfg_attr(not(feature = "internals"), doc(hidden))] + #[inline(always)] fn inverted_zip( self, lhs: GenericArray, @@ -34,7 +43,6 @@ pub unsafe trait GenericSequence: Sized + IntoIterator { GenericArray: GenericSequence + MappedGenericSequence, Self: MappedGenericSequence, - Self::Length: ArrayLength + ArrayLength, F: FnMut(B, Self::Item) -> U, { unsafe { @@ -42,27 +50,26 @@ pub unsafe trait GenericSequence: Sized + IntoIterator { let (left_array_iter, left_position) = left.iter_position(); - FromIterator::from_iter(left_array_iter.zip(self.into_iter()).map( - |(l, right_value)| { - let left_value = ptr::read(l); + FromIterator::from_iter(left_array_iter.zip(self).map(|(l, right_value)| { + let left_value = ptr::read(l); - *left_position += 1; + *left_position += 1; - f(left_value, right_value) - }, - )) + f(left_value, right_value) + })) } } - #[doc(hidden)] + /// Treats `self` as the right-hand operand in a zip operation + #[cfg_attr(not(feature = "internals"), doc(hidden))] + #[inline(always)] fn inverted_zip2(self, lhs: Lhs, mut f: F) -> MappedSequence where Lhs: GenericSequence + MappedGenericSequence, Self: MappedGenericSequence, - Self::Length: ArrayLength + ArrayLength, F: FnMut(Lhs::Item, Self::Item) -> U, { - FromIterator::from_iter(lhs.into_iter().zip(self.into_iter()).map(|(l, r)| f(l, r))) + FromIterator::from_iter(lhs.into_iter().zip(self).map(|(l, r)| f(l, r))) } } @@ -79,7 +86,7 @@ where type Length = S::Length; type Sequence = S::Sequence; - #[inline] + #[inline(always)] fn generate(f: F) -> Self::Sequence where F: FnMut(usize) -> T, @@ -95,7 +102,7 @@ where type Length = S::Length; type Sequence = S::Sequence; - #[inline] + #[inline(always)] fn generate(f: F) -> Self::Sequence where F: FnMut(usize) -> T, @@ -108,6 +115,10 @@ where /// or prepending an element to it. /// /// Any lengthened sequence can be shortened back to the original using `pop_front` or `pop_back` +/// +/// # Safety +/// While the [`append`](Lengthen::append) and [`prepend`](Lengthen::prepend) +/// methods are marked safe, care must be taken when implementing them. pub unsafe trait Lengthen: Sized + GenericSequence { /// `GenericSequence` that has one more element than `Self` type Longer: Shorten; @@ -118,13 +129,12 @@ pub unsafe trait Lengthen: Sized + GenericSequence { /// /// ```rust /// # use generic_array::{arr, sequence::Lengthen}; - /// # fn main() { - /// let a = arr![i32; 1, 2, 3]; + /// + /// let a = arr![1, 2, 3]; /// /// let b = a.append(4); /// - /// assert_eq!(b, arr![i32; 1, 2, 3, 4]); - /// # } + /// assert_eq!(b, arr![1, 2, 3, 4]); /// ``` fn append(self, last: T) -> Self::Longer; @@ -134,13 +144,12 @@ pub unsafe trait Lengthen: Sized + GenericSequence { /// /// ```rust /// # use generic_array::{arr, sequence::Lengthen}; - /// # fn main() { - /// let a = arr![i32; 1, 2, 3]; + /// + /// let a = arr![1, 2, 3]; /// /// let b = a.prepend(4); /// - /// assert_eq!(b, arr![i32; 4, 1, 2, 3]); - /// # } + /// assert_eq!(b, arr![4, 1, 2, 3]); /// ``` fn prepend(self, first: T) -> Self::Longer; } @@ -149,6 +158,10 @@ pub unsafe trait Lengthen: Sized + GenericSequence { /// /// Additionally, any shortened sequence can be lengthened by /// appending or prepending an element to it. +/// +/// # Safety +/// While the [`pop_back`](Shorten::pop_back) and [`pop_front`](Shorten::pop_front) +/// methods are marked safe, care must be taken when implementing them. pub unsafe trait Shorten: Sized + GenericSequence { /// `GenericSequence` that has one less element than `Self` type Shorter: Lengthen; @@ -159,14 +172,13 @@ pub unsafe trait Shorten: Sized + GenericSequence { /// /// ```rust /// # use generic_array::{arr, sequence::Shorten}; - /// # fn main() { - /// let a = arr![i32; 1, 2, 3, 4]; + /// + /// let a = arr![1, 2, 3, 4]; /// /// let (init, last) = a.pop_back(); /// - /// assert_eq!(init, arr![i32; 1, 2, 3]); + /// assert_eq!(init, arr![1, 2, 3]); /// assert_eq!(last, 4); - /// # } /// ``` fn pop_back(self) -> (Self::Shorter, T); @@ -175,27 +187,27 @@ pub unsafe trait Shorten: Sized + GenericSequence { /// /// ```rust /// # use generic_array::{arr, sequence::Shorten}; - /// # fn main() { - /// let a = arr![i32; 1, 2, 3, 4]; + /// + /// let a = arr![1, 2, 3, 4]; /// /// let (head, tail) = a.pop_front(); /// /// assert_eq!(head, 1); - /// assert_eq!(tail, arr![i32; 2, 3, 4]); - /// # } + /// assert_eq!(tail, arr![2, 3, 4]); /// ``` fn pop_front(self) -> (T, Self::Shorter); } -unsafe impl> Lengthen for GenericArray +unsafe impl Lengthen for GenericArray where N: Add, - Add1: ArrayLength, + Add1: ArrayLength, Add1: Sub, - Sub1>: ArrayLength, + Sub1>: ArrayLength, { type Longer = GenericArray>; + #[inline] fn append(self, last: T) -> Self::Longer { let mut longer: MaybeUninit = MaybeUninit::uninit(); @@ -212,6 +224,7 @@ where } } + #[inline] fn prepend(self, first: T) -> Self::Longer { let mut longer: MaybeUninit = MaybeUninit::uninit(); @@ -229,15 +242,16 @@ where } } -unsafe impl> Shorten for GenericArray +unsafe impl Shorten for GenericArray where N: Sub, - Sub1: ArrayLength, + Sub1: ArrayLength, Sub1: Add, - Add1>: ArrayLength, + Add1>: ArrayLength, { type Shorter = GenericArray>; + #[inline] fn pop_back(self) -> (Self::Shorter, T) { let whole = ManuallyDrop::new(self); @@ -249,6 +263,7 @@ where } } + #[inline] fn pop_front(self) -> (T, Self::Shorter) { // ensure this doesn't get dropped let whole = ManuallyDrop::new(self); @@ -263,10 +278,11 @@ where } /// Defines a `GenericSequence` that can be split into two parts at a given pivot index. -pub unsafe trait Split: GenericSequence -where - K: ArrayLength, -{ +/// +/// # Safety +/// While the [`split`](Split::split) method is marked safe, +/// care must be taken when implementing it. +pub unsafe trait Split: GenericSequence { /// First part of the resulting split array type First: GenericSequence; /// Second part of the resulting split array @@ -278,14 +294,15 @@ where unsafe impl Split for GenericArray where - N: ArrayLength, - K: ArrayLength, + N: ArrayLength, + K: ArrayLength, N: Sub, - Diff: ArrayLength, + Diff: ArrayLength, { type First = GenericArray; type Second = GenericArray>; + #[inline] fn split(self) -> (Self::First, Self::Second) { unsafe { // ensure this doesn't get dropped @@ -301,14 +318,15 @@ where unsafe impl<'a, T, N, K> Split for &'a GenericArray where - N: ArrayLength, - K: ArrayLength + 'static, + N: ArrayLength, + K: ArrayLength, N: Sub, - Diff: ArrayLength, + Diff: ArrayLength, { type First = &'a GenericArray; type Second = &'a GenericArray>; + #[inline] fn split(self) -> (Self::First, Self::Second) { unsafe { let ptr_to_first: *const T = self.as_ptr(); @@ -321,14 +339,15 @@ where unsafe impl<'a, T, N, K> Split for &'a mut GenericArray where - N: ArrayLength, - K: ArrayLength + 'static, + N: ArrayLength, + K: ArrayLength, N: Sub, - Diff: ArrayLength, + Diff: ArrayLength, { type First = &'a mut GenericArray; type Second = &'a mut GenericArray>; + #[inline] fn split(self) -> (Self::First, Self::Second) { unsafe { let ptr_to_first: *mut T = self.as_mut_ptr(); @@ -340,10 +359,11 @@ where } /// Defines `GenericSequence`s which can be joined together, forming a larger array. -pub unsafe trait Concat: GenericSequence -where - M: ArrayLength, -{ +/// +/// # Safety +/// While the [`concat`](Concat::concat) method is marked safe, +/// care must be taken when implementing it. +pub unsafe trait Concat: GenericSequence { /// Sequence to be concatenated with `self` type Rest: GenericSequence; @@ -356,13 +376,14 @@ where unsafe impl Concat for GenericArray where - N: ArrayLength + Add, - M: ArrayLength, - Sum: ArrayLength, + N: ArrayLength + Add, + M: ArrayLength, + Sum: ArrayLength, { type Rest = GenericArray; type Output = GenericArray>; + #[inline] fn concat(self, rest: Self::Rest) -> Self::Output { let mut output: MaybeUninit = MaybeUninit::uninit(); diff --git a/tests/arr.rs b/tests/arr.rs index 3a3300dead..624d8b2d19 100644 --- a/tests/arr.rs +++ b/tests/arr.rs @@ -1,37 +1,59 @@ -use generic_array::arr; +use generic_array::{arr, GenericArray}; #[test] fn empty_without_trailing_comma() { - let ar = arr![u8; ]; + let ar: GenericArray = arr![]; assert_eq!(format!("{:x}", ar), ""); } #[test] fn empty_with_trailing_comma() { - let ar = arr![u8; , ]; + let ar: GenericArray = arr![, ]; assert_eq!(format!("{:x}", ar), ""); } #[test] fn without_trailing_comma() { - let ar = arr![u8; 10, 20, 30]; + let ar = arr![10, 20, 30]; assert_eq!(format!("{:x}", ar), "0a141e"); } #[test] fn with_trailing_comma() { - let ar = arr![u8; 10, 20, 30, ]; + let ar = arr![10u8, 20, 30,]; assert_eq!(format!("{:x}", ar), "0a141e"); } #[test] fn const_context() { - const AR: generic_array::GenericArray = arr![u8; 10, 20, 30]; + const AR: GenericArray = arr![10, 20, 30]; assert_eq!(format!("{:x}", AR), "0a141e"); } #[test] fn repeat_expression() { - let ar = arr![u8; 0xc0; typenum::U4]; + let ar = arr![0xc0u8; typenum::U4]; assert_eq!(format!("{:x}", ar), "c0c0c0c0"); + + _ = arr![0; 12]; + _ = arr![1; 't' as usize]; +} + +#[cfg(feature = "alloc")] +#[test] +fn alloc_arr() { + use generic_array::box_arr; + + let ar = box_arr![0xc0u8; typenum::U4]; + assert_eq!(format!("{:x}", &*ar), "c0c0c0c0"); + + _ = box_arr![0; 12]; + _ = box_arr![1; 't' as usize]; + + // ZSTs + _ = box_arr![0f32; 0].into_boxed_slice(); + let z = box_arr![(); 5].into_boxed_slice(); + + #[allow(clippy::let_unit_value)] + let _ = z[0]; } diff --git a/tests/generics.rs b/tests/generics.rs index e080c1311d..3eb86e33a5 100644 --- a/tests/generics.rs +++ b/tests/generics.rs @@ -1,4 +1,3 @@ -#![recursion_limit = "128"] use generic_array::arr; use generic_array::typenum::consts::U4; @@ -56,7 +55,7 @@ pub fn generic_array_variable_length_zip_sum( b: GenericArray, ) -> i32 where - N: ArrayLength, + N: ArrayLength, { a.zip(b, |l, r| l + r).map(|x| x + 1).fold(0, |a, x| x + a) } @@ -66,7 +65,7 @@ pub fn generic_array_same_type_variable_length_zip_sum( b: GenericArray, ) -> i32 where - N: ArrayLength + ArrayLength<>::Output>, + N: ArrayLength, T: Add, { a.zip(b, |l, r| l + r).map(|x| x + 1).fold(0, |a, x| x + a) @@ -75,14 +74,12 @@ where /// Complex example using fully generic `GenericArray`s with the same length. /// /// It's mostly just the repeated `Add` traits, which would be present in other systems anyway. -pub fn generic_array_zip_sum + ArrayLength>( +pub fn generic_array_zip_sum( a: GenericArray, b: GenericArray, ) -> i32 where A: Add, - N: ArrayLength<>::Output> - + ArrayLength<<>::Output as Add>::Output>, >::Output: Add, <>::Output as Add>::Output: Add, { @@ -91,33 +88,30 @@ where #[test] fn test_generics() { - generic_map(arr![i32; 1, 2, 3, 4]); + generic_map(arr![1, 2, 3, 4]); assert_eq!( - generic_sequence_zip_sum(arr![i32; 1, 2, 3, 4], arr![i32; 2, 3, 4, 5]), + generic_sequence_zip_sum(arr![1, 2, 3, 4], arr![2, 3, 4, 5]), 28 ); assert_eq!( - generic_array_plain_zip_sum(arr![i32; 1, 2, 3, 4], arr![i32; 2, 3, 4, 5]), + generic_array_plain_zip_sum(arr![1, 2, 3, 4], arr![2, 3, 4, 5]), 28 ); assert_eq!( - generic_array_variable_length_zip_sum(arr![i32; 1, 2, 3, 4], arr![i32; 2, 3, 4, 5]), + generic_array_variable_length_zip_sum(arr![1, 2, 3, 4], arr![2, 3, 4, 5]), 28 ); assert_eq!( - generic_array_same_type_variable_length_zip_sum( - arr![i32; 1, 2, 3, 4], - arr![i32; 2, 3, 4, 5] - ), + generic_array_same_type_variable_length_zip_sum(arr![1, 2, 3, 4], arr![2, 3, 4, 5]), 28 ); assert_eq!( - generic_array_zip_sum(arr![i32; 1, 2, 3, 4], arr![i32; 2, 3, 4, 5]), + generic_array_zip_sum(arr![1, 2, 3, 4], arr![2, 3, 4, 5]), 28 ); } diff --git a/tests/hex.rs b/tests/hex.rs index 479d1c4f43..e77577e77a 100644 --- a/tests/hex.rs +++ b/tests/hex.rs @@ -6,13 +6,13 @@ use typenum::U2048; #[test] fn short_lower_hex() { - let ar = arr![u8; 10, 20, 30]; + let ar = arr![10u8, 20, 30]; assert_eq!(format!("{:x}", ar), "0a141e"); } #[test] fn short_upper_hex() { - let ar = arr![u8; 30, 20, 10]; + let ar = arr![30u8, 20, 10]; assert_eq!(format!("{:X}", ar), "1E140A"); } @@ -42,7 +42,7 @@ fn long_upper_hex_truncated() { #[test] fn truncated_lower_hex() { - let ar = arr![u8; 10, 20, 30, 40, 50]; + let ar = arr![10u8, 20, 30, 40, 50]; assert_eq!(format!("{:.2x}", ar), "0a"); assert_eq!(format!("{:.3x}", ar), "0a1"); assert_eq!(format!("{:.4x}", ar), "0a14"); @@ -50,7 +50,7 @@ fn truncated_lower_hex() { #[test] fn truncated_upper_hex() { - let ar = arr![u8; 30, 20, 10, 17, 0]; + let ar = arr![30u8, 20, 10, 17, 0]; assert_eq!(format!("{:.4X}", ar), "1E14"); assert_eq!(format!("{:.5X}", ar), "1E140"); assert_eq!(format!("{:.6X}", ar), "1E140A"); diff --git a/tests/import_name.rs b/tests/import_name.rs index dbc1334c2c..04e533ead7 100644 --- a/tests/import_name.rs +++ b/tests/import_name.rs @@ -1,10 +1,15 @@ use generic_array as gen_arr; -use gen_arr::arr; -use gen_arr::typenum; - #[test] fn test_different_crate_name() { - let _: gen_arr::GenericArray = arr![u32; 0, 1, 2, 3]; - let _: gen_arr::GenericArray = arr![u32;]; + use gen_arr::arr; + use gen_arr::typenum; + + let _: gen_arr::GenericArray = arr![0, 1, 2, 3]; + let _: gen_arr::GenericArray = arr![]; +} + +#[test] +fn test_crate_usage() { + let _: gen_arr::GenericArray = gen_arr::arr![]; } diff --git a/tests/iter.rs b/tests/iter.rs index b27c5fc536..1af78ff4ee 100644 --- a/tests/iter.rs +++ b/tests/iter.rs @@ -25,12 +25,12 @@ fn test_from_iterator() { self.0 } } - assert!(GenericArray::::from_exact_iter(BadExact(5)).is_none()); + assert!(GenericArray::::try_from_iter(BadExact(5)).is_err()); } #[test] fn test_into_iter_as_slice() { - let array = arr![char; 'a', 'b', 'c']; + let array = arr!['a', 'b', 'c']; let mut into_iter = array.into_iter(); assert_eq!(into_iter.as_slice(), &['a', 'b', 'c']); let _ = into_iter.next().unwrap(); @@ -42,7 +42,7 @@ fn test_into_iter_as_slice() { #[test] fn test_into_iter_as_mut_slice() { - let array = arr![char; 'a', 'b', 'c']; + let array = arr!['a', 'b', 'c']; let mut into_iter = array.into_iter(); assert_eq!(into_iter.as_slice(), &['a', 'b', 'c']); into_iter.as_mut_slice()[0] = 'x'; @@ -53,7 +53,7 @@ fn test_into_iter_as_mut_slice() { #[test] fn test_into_iter_debug() { - let array = arr![char; 'a', 'b', 'c']; + let array = arr!['a', 'b', 'c']; let into_iter = array.into_iter(); let debug = format!("{:?}", into_iter); assert_eq!(debug, "GenericArrayIter(['a', 'b', 'c'])"); @@ -65,7 +65,7 @@ fn test_into_iter_clone() { let v: Vec = it.collect(); assert_eq!(&v[..], slice); } - let mut it = arr![i32; 1, 2, 3].into_iter(); + let mut it = arr![1, 2, 3].into_iter(); iter_equal(it.clone(), &[1, 2, 3]); assert_eq!(it.next(), Some(1)); let mut it = it.rev(); @@ -79,7 +79,7 @@ fn test_into_iter_clone() { #[test] fn test_into_iter_nth() { - let v = arr![i32; 0, 1, 2, 3, 4]; + let v = arr![0, 1, 2, 3, 4]; for i in 0..v.len() { assert_eq!(v.into_iter().nth(i).unwrap(), v[i]); } @@ -90,16 +90,30 @@ fn test_into_iter_nth() { assert_eq!(iter.nth(1).unwrap(), v[4]); } +#[test] +fn test_into_iter_nth_back() { + let v = arr![0, 1, 2, 3, 4]; + + for i in 0..v.len() { + assert_eq!(v.into_iter().nth_back(i).unwrap(), v[v.len() - i - 1]); + } + assert_eq!(v.into_iter().nth_back(v.len()), None); + + let mut iter = v.into_iter(); + assert_eq!(iter.nth_back(2).unwrap(), v[2]); + assert_eq!(iter.nth_back(1).unwrap(), v[0]); +} + #[test] fn test_into_iter_last() { - let v = arr![i32; 0, 1, 2, 3, 4]; + let v = arr![0, 1, 2, 3, 4]; assert_eq!(v.into_iter().last().unwrap(), 4); - assert_eq!(arr![i32; 0].into_iter().last().unwrap(), 0); + assert_eq!(arr![0].into_iter().last().unwrap(), 0); } #[test] fn test_into_iter_count() { - let v = arr![i32; 0, 1, 2, 3, 4]; + let v = arr![0, 1, 2, 3, 4]; assert_eq!(v.into_iter().count(), 5); let mut iter2 = v.into_iter(); @@ -110,17 +124,14 @@ fn test_into_iter_count() { #[test] fn test_into_iter_flat_map() { - assert!((0..5).flat_map(|i| arr![i32; 2 * i, 2 * i + 1]).eq(0..10)); + assert!((0..5).flat_map(|i| arr![2 * i, 2 * i + 1]).eq(0..10)); } #[test] fn test_into_iter_fold() { - assert_eq!( - arr![i32; 1, 2, 3, 4].into_iter().fold(0, |sum, x| sum + x), - 10 - ); + assert_eq!(arr![1, 2, 3, 4].into_iter().fold(0, |sum, x| sum + x), 10); - let mut iter = arr![i32; 0, 1, 2, 3, 4, 5].into_iter(); + let mut iter = arr![0, 1, 2, 3, 4, 5].into_iter(); iter.next(); iter.next_back(); @@ -147,7 +158,7 @@ fn test_into_iter_drops() { } fn v(i: &Cell) -> GenericArray { - arr![R; r(i), r(i), r(i), r(i), r(i)] + arr![r(i), r(i), r(i), r(i), r(i)] } let i = Cell::new(0); @@ -176,6 +187,16 @@ fn test_into_iter_drops() { } assert_eq!(i.get(), 5); + let i = Cell::new(0); + { + let mut iter = v(&i).into_iter(); + let _x = iter.nth_back(2); + assert_eq!(i.get(), 2); + let _y = iter.last(); + assert_eq!(i.get(), 3); + } + assert_eq!(i.get(), 5); + let i = Cell::new(0); for (index, _x) in v(&i).into_iter().enumerate() { assert_eq!(i.get(), index); diff --git a/tests/mod.rs b/tests/mod.rs index ebc0aab0f6..bf18fe96bc 100644 --- a/tests/mod.rs +++ b/tests/mod.rs @@ -1,4 +1,3 @@ -#![recursion_limit = "128"] #![no_std] use core::cell::Cell; use core::ops::{Add, Drop}; @@ -8,13 +7,16 @@ use generic_array::sequence::*; use generic_array::typenum::{U0, U3, U4, U97}; use generic_array::GenericArray; +#[cfg(feature = "alloc")] +extern crate alloc; + #[test] fn test() { let mut list97 = [0; 97]; for (i, elem) in list97.iter_mut().enumerate() { *elem = i as i32; } - let l: GenericArray = GenericArray::clone_from_slice(&list97); + let l: GenericArray = *GenericArray::from_slice(&list97); assert_eq!(l[0], 0); assert_eq!(l[1], 1); assert_eq!(l[32], 32); @@ -34,22 +36,24 @@ fn test_drop() { let drop_counter = Cell::new(0); { - let _: GenericArray = arr![TestDrop; TestDrop(&drop_counter), - TestDrop(&drop_counter), - TestDrop(&drop_counter)]; + let _: GenericArray = arr![ + TestDrop(&drop_counter), + TestDrop(&drop_counter), + TestDrop(&drop_counter) + ]; } assert_eq!(drop_counter.get(), 3); } #[test] fn test_arr() { - let test: GenericArray = arr![u32; 1, 2, 3]; + let test: GenericArray = arr![1, 2, 3]; assert_eq!(test[1], 2); } #[test] fn test_copy() { - let test = arr![u32; 1, 2, 3]; + let test = arr![1, 2, 3]; let test2 = test; // if GenericArray is not copy, this should fail as a use of a moved value assert_eq!(test[1], 2); @@ -100,18 +104,18 @@ fn test_from() { #[test] fn test_unit_macro() { - let arr = arr![f32; 3.14]; - assert_eq!(arr[0], 3.14); + let arr = arr![5.81]; + assert_eq!(arr[0], 5.81); } #[test] fn test_empty_macro() { - let _arr = arr![f32;]; + let _arr: GenericArray<(), _> = arr![]; } #[test] fn test_cmp() { - let _ = arr![u8; 0x00].cmp(&arr![u8; 0x00]); + let _ = arr![0x00u8].cmp(&arr![0x00]); } /// This test should cause a helpful compile error if uncommented. @@ -129,7 +133,7 @@ mod impl_serde { #[test] fn test_serde_implementation() { - let array: GenericArray = arr![f64; 0.0, 5.0, 3.0, 7.07192, 76.0, -9.0]; + let array: GenericArray = arr![0.0, 5.0, 3.0, 7.07192, 76.0, -9.0]; let string = serde_json::to_string(&array).unwrap(); assert_eq!(string, "[0.0,5.0,3.0,7.07192,76.0,-9.0]"); @@ -142,7 +146,7 @@ mod impl_serde { fn test_map() { let b: GenericArray = GenericArray::generate(|i| i as i32 * 4).map(|x| x - 3); - assert_eq!(b, arr![i32; -3, 1, 5, 9]); + assert_eq!(b, arr![-3, 1, 5, 9]); } #[test] @@ -153,7 +157,7 @@ fn test_zip() { // Uses reference and non-reference arguments let c = (&a).zip(b, |r, l| *r as i32 + l); - assert_eq!(c, arr![i32; 1, 6, 11, 16]); + assert_eq!(c, arr![1, 6, 11, 16]); } #[test] @@ -163,7 +167,7 @@ fn test_from_iter_short() { let a: GenericArray<_, U4> = repeat(11).take(3).collect(); - assert_eq!(a, arr![i32; 11, 11, 11, 0]); + assert_eq!(a, arr![11, 11, 11, 0]); } #[test] @@ -172,7 +176,7 @@ fn test_from_iter() { let a: GenericArray<_, U4> = repeat(11).take(3).chain(once(0)).collect(); - assert_eq!(a, arr![i32; 11, 11, 11, 0]); + assert_eq!(a, arr![11, 11, 11, 0]); } #[allow(unused)] @@ -210,9 +214,9 @@ fn test_sizes() { assert_eq!(size_of::(), 25 + size_of::() * 2); - assert_eq!(size_of_val(&arr![u8; 1, 2, 3]), size_of::() * 3); - assert_eq!(size_of_val(&arr![u32; 1]), size_of::() * 1); - assert_eq!(size_of_val(&arr![u64; 1, 2, 3, 4]), size_of::() * 4); + assert_eq!(size_of_val(&arr![1u8, 2, 3]), size_of::() * 3); + assert_eq!(size_of_val(&arr![1u32]), size_of::() * 1); + assert_eq!(size_of_val(&arr![1u64, 2, 3, 4]), size_of::() * 4); assert_eq!(size_of::>(), size_of::() * 97); } @@ -237,102 +241,102 @@ fn test_alignment() { #[test] fn test_append() { - let a = arr![i32; 1, 2, 3]; + let a = arr![1, 2, 3]; let b = a.append(4); - assert_eq!(b, arr![i32; 1, 2, 3, 4]); + assert_eq!(b, arr![1, 2, 3, 4]); } #[test] fn test_prepend() { - let a = arr![i32; 1, 2, 3]; + let a = arr![1, 2, 3]; let b = a.prepend(4); - assert_eq!(b, arr![i32; 4, 1, 2, 3]); + assert_eq!(b, arr![4, 1, 2, 3]); } #[test] fn test_pop() { - let a = arr![i32; 1, 2, 3, 4]; + let a = arr![1, 2, 3, 4]; let (init, last) = a.pop_back(); - assert_eq!(init, arr![i32; 1, 2, 3]); + assert_eq!(init, arr![1, 2, 3]); assert_eq!(last, 4); let (head, tail) = a.pop_front(); assert_eq!(head, 1); - assert_eq!(tail, arr![i32; 2, 3, 4]); + assert_eq!(tail, arr![2, 3, 4]); } #[test] fn test_split() { - let a = arr![i32; 1, 2, 3, 4]; + let a = arr![1, 2, 3, 4]; let (b, c) = a.split(); - assert_eq!(b, arr![i32; 1]); - assert_eq!(c, arr![i32; 2, 3, 4]); + assert_eq!(b, arr![1]); + assert_eq!(c, arr![2, 3, 4]); let (e, f) = a.split(); - assert_eq!(e, arr![i32; 1, 2]); - assert_eq!(f, arr![i32; 3, 4]); + assert_eq!(e, arr![1, 2]); + assert_eq!(f, arr![3, 4]); } #[test] fn test_split_ref() { - let a = arr![i32; 1, 2, 3, 4]; + let a = arr![1, 2, 3, 4]; let a_ref = &a; let (b_ref, c_ref) = a_ref.split(); - assert_eq!(b_ref, &arr![i32; 1]); - assert_eq!(c_ref, &arr![i32; 2, 3, 4]); + assert_eq!(b_ref, &arr![1]); + assert_eq!(c_ref, &arr![2, 3, 4]); let (e_ref, f_ref) = a_ref.split(); - assert_eq!(e_ref, &arr![i32; 1, 2]); - assert_eq!(f_ref, &arr![i32; 3, 4]); + assert_eq!(e_ref, &arr![1, 2]); + assert_eq!(f_ref, &arr![3, 4]); } #[test] fn test_split_mut() { - let mut a = arr![i32; 1, 2, 3, 4]; + let mut a = arr![1, 2, 3, 4]; let a_ref = &mut a; let (b_ref, c_ref) = a_ref.split(); - assert_eq!(b_ref, &mut arr![i32; 1]); - assert_eq!(c_ref, &mut arr![i32; 2, 3, 4]); + assert_eq!(b_ref, &mut arr![1]); + assert_eq!(c_ref, &mut arr![2, 3, 4]); let (e_ref, f_ref) = a_ref.split(); - assert_eq!(e_ref, &mut arr![i32; 1, 2]); - assert_eq!(f_ref, &mut arr![i32; 3, 4]); + assert_eq!(e_ref, &mut arr![1, 2]); + assert_eq!(f_ref, &mut arr![3, 4]); } #[test] fn test_concat() { - let a = arr![i32; 1, 2]; - let b = arr![i32; 3, 4, 5]; + let a = arr![1, 2]; + let b = arr![3, 4, 5]; let c = a.concat(b); - assert_eq!(c, arr![i32; 1, 2, 3, 4, 5]); + assert_eq!(c, arr![1, 2, 3, 4, 5]); let (d, e) = c.split(); - assert_eq!(d, arr![i32; 1, 2]); - assert_eq!(e, arr![i32; 3, 4, 5]); + assert_eq!(d, arr![1, 2]); + assert_eq!(e, arr![3, 4, 5]); } #[test] fn test_fold() { - let a = arr![i32; 1, 2, 3, 4]; + let a = arr![1, 2, 3, 4]; assert_eq!(10, a.fold(0, |a, x| a + x)); } @@ -348,31 +352,31 @@ where #[test] fn test_sum() { - let a = sum_generic(arr![i32; 1, 2, 3, 4]); + let a = sum_generic(arr![1, 2, 3, 4]); assert_eq!(a, 10); } #[test] fn test_as_ref() { - let a = arr![i32; 1, 2, 3, 4]; + let a = arr![1, 2, 3, 4]; let a_ref: &[i32; 4] = a.as_ref(); assert_eq!(a_ref, &[1, 2, 3, 4]); } #[test] fn test_as_mut() { - let mut a = arr![i32; 1, 2, 3, 4]; + let mut a = arr![1, 2, 3, 4]; let a_mut: &mut [i32; 4] = a.as_mut(); assert_eq!(a_mut, &mut [1, 2, 3, 4]); a_mut[2] = 0; assert_eq!(a_mut, &mut [1, 2, 0, 4]); - assert_eq!(a, arr![i32; 1, 2, 0, 4]); + assert_eq!(a, arr![1, 2, 0, 4]); } #[test] fn test_from_array_ref() { - let a = arr![i32; 1, 2, 3, 4]; + let a = arr![1, 2, 3, 4]; let a_ref: &[i32; 4] = a.as_ref(); let a_from: &GenericArray = a_ref.into(); assert_eq!(&a, a_from); @@ -380,9 +384,92 @@ fn test_from_array_ref() { #[test] fn test_from_array_mut() { - let mut a = arr![i32; 1, 2, 3, 4]; + let mut a = arr![1, 2, 3, 4]; let mut a_copy = a; let a_mut: &mut [i32; 4] = a.as_mut(); let a_from: &mut GenericArray = a_mut.into(); assert_eq!(&mut a_copy, a_from); } + +#[cfg(feature = "alloc")] +#[test] +fn test_try_from_vec() { + let a = alloc::vec![1, 2, 3, 4]; + let _ = GenericArray::<_, U4>::try_from(a).unwrap(); +} + +#[cfg(feature = "alloc")] +#[test] +fn test_alloc() { + use alloc::{boxed::Box, vec::Vec}; + use generic_array::box_arr; + + let x: Box<[i32]> = arr![1, 2, 3, 4, 5].into(); + assert_eq!(x.len(), 5); + let y: GenericArray = x.clone().try_into().unwrap(); + assert_eq!(&x[..], &y[..]); + + let x: Vec = arr![1, 2, 3, 4, 5].into(); + assert_eq!(x.len(), 5); + let y: GenericArray = x.clone().try_into().unwrap(); + assert_eq!(&x[..], &y[..]); + + let x: Vec = Box::new(arr![1, 2, 3, 4, 5]).into_vec(); + assert_eq!(x.len(), 5); + let y: Box> = GenericArray::try_from_vec(x.clone()).unwrap(); + assert_eq!(&x[..], &y[..]); + + let z = + Box::>::from_iter(y.into_iter() as alloc::vec::IntoIter<_>); + + let _: Box<_> = z.clone().zip(Box::new(arr![1, 2, 3, 4, 5]), |a, b| a + b); + + let _ = z.map(|x| x + 1); + + let _ = arr![1, 2, 3, 4].zip(*box_arr![1, 2, 3, 4], |a, b| a + b); + + let _ = box_arr!(1, 2, 3, 4, 5); + + #[cfg(not(miri))] + { + // 128-bit * 10^6 = 16MB, large enough to overflow the stack, but not this + let _ = box_arr![1u128; typenum::Exp]; + + let _ = GenericArray::>::default_boxed(); + } +} + +#[test] +fn test_chunks() { + // intended usage + let (chunks, rem) = GenericArray::::chunks_from_slice(&[1, 2, 3, 4, 5, 6, 7]); + + assert_eq!(chunks[0], arr![1, 2, 3]); + assert_eq!(chunks[1], arr![4, 5, 6]); + assert_eq!(rem, &[7]); + + // zero-length input + let (chunks, rem) = GenericArray::::chunks_from_slice(&[]); + assert!(chunks.is_empty()); + assert!(rem.is_empty()); + + // zero-length output with zero-length input + let (chunks, rem) = GenericArray::::chunks_from_slice(&[]); + assert!(chunks.is_empty()); + assert!(rem.is_empty()); + + // only remainder + let (chunks, rem) = GenericArray::::chunks_from_slice(&[1, 2]); + + assert!(chunks.is_empty()); + assert_eq!(rem, &[1, 2]); +} + +#[test] +#[should_panic] +fn test_chunks_fail() { + // zero-length output with input + let (chunks, rem) = GenericArray::::chunks_from_slice(&[1, 2, 3]); + assert!(chunks.is_empty()); + assert!(rem.is_empty()); +}