Merge rust-bitcoin/rust-bitcoin#2287: Implement custom `ArrayVec` that is `Copy`
7c3b198127
Add a simple `Copy`-able `ArrayVec` (Martin Habovstiak)f17c0402b7
Add tools that help with making code `const` (Martin Habovstiak) Pull request description: While we decided to use `arrayvec::ArrayVec`, unfortunately it currently isn't `Copy` even if `T` is because of unfortunate interactions with the `Drop` trait. So this adds a super-simple custom version that is `Copy` and may be extended if we need to or replaced with `arrayvec` if they start supporting `Copy`. ACKs for top commit: tcharding: ACK7c3b198127
apoelstra: ACK7c3b198127
Tree-SHA512: 545cb99c910f9455185a78a77a1110374e45a11e374c698316a222e599737b82ec2ac5c8ef1a8fe7037f20d938b1a4061685b6c673061b706dc9df9478de2b48
This commit is contained in:
commit
12e11a089c
|
@ -0,0 +1,206 @@
|
|||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
//! A simplified `Copy` version of `arrayvec::ArrayVec`.
|
||||
|
||||
use core::fmt;
|
||||
|
||||
pub use safety_boundary::ArrayVec;
|
||||
|
||||
/// Limits the scope of `unsafe` auditing.
|
||||
// New trait impls and fns that don't need to access internals should go below the module, not
|
||||
// inside it!
|
||||
mod safety_boundary {
|
||||
use core::mem::MaybeUninit;
|
||||
use crate::const_tools::cond_const;
|
||||
|
||||
/// A growable contiguous collection backed by array.
|
||||
#[derive(Copy)]
|
||||
pub struct ArrayVec<T: Copy, const CAP: usize> {
|
||||
len: usize,
|
||||
data: [MaybeUninit<T>; CAP],
|
||||
}
|
||||
|
||||
impl<T: Copy, const CAP: usize> ArrayVec<T, CAP> {
|
||||
// The bounds are const-unstable until 1.61
|
||||
cond_const! {
|
||||
/// Creates an empty `ArrayVec`.
|
||||
pub const(in rust_v_1_61 = "1.61") fn new() -> Self {
|
||||
Self {
|
||||
len: 0,
|
||||
data: [MaybeUninit::uninit(); CAP],
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates an `ArrayVec` initialized with the contets of `slice`.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// If the slice is longer than `CAP`.
|
||||
pub const(in rust_v_1_61 = "1.61") fn from_slice(slice: &[T]) -> Self {
|
||||
assert!(slice.len() <= CAP);
|
||||
let mut data = [MaybeUninit::uninit(); CAP];
|
||||
let mut i = 0;
|
||||
// can't use mutable references and operators in const
|
||||
while i < slice.len() {
|
||||
data[i] = MaybeUninit::new(slice[i]);
|
||||
i += 1;
|
||||
}
|
||||
|
||||
Self {
|
||||
len: slice.len(),
|
||||
data,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// from_raw_parts is const-unstable until 1.64
|
||||
cond_const! {
|
||||
/// Returns a reference to the underlying data.
|
||||
pub const(in rust_v_1_64 = "1.64") fn as_slice(&self) -> &[T] {
|
||||
let ptr = &self.data as *const _ as *const T;
|
||||
unsafe { core::slice::from_raw_parts(ptr, self.len) }
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the underlying data.
|
||||
pub fn as_mut_slice(&mut self) -> &mut [T] {
|
||||
unsafe { &mut *(&mut self.data[..self.len] as *mut _ as *mut [T]) }
|
||||
}
|
||||
|
||||
/// Adds an element into `self`.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// If the length would increase past CAP.
|
||||
pub fn push(&mut self, element: T) {
|
||||
assert!(self.len < CAP);
|
||||
self.data[self.len] = MaybeUninit::new(element);
|
||||
self.len += 1;
|
||||
}
|
||||
|
||||
/// Copies and appends all elements from `slice` into `self`.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// If the length would increase past CAP.
|
||||
pub fn extend_from_slice(&mut self, slice: &[T]) {
|
||||
let new_len = self.len.checked_add(slice.len()).expect("integer/buffer overflow");
|
||||
assert!(new_len <= CAP, "buffer overflow");
|
||||
// SAFETY: MaybeUninit<T> has the same layout as T
|
||||
let slice = unsafe { &*(slice as *const _ as *const [MaybeUninit<T>]) };
|
||||
self.data[self.len..].copy_from_slice(slice);
|
||||
self.len = new_len;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Clones the value *faster* than using `Copy`.
|
||||
///
|
||||
/// Because we avoid copying the uninitialized part of the array this copies the value faster than
|
||||
/// memcpy.
|
||||
#[allow(clippy::non_canonical_clone_impl)]
|
||||
impl<T: Copy, const CAP: usize> Clone for ArrayVec<T, CAP> {
|
||||
fn clone(&self) -> Self {
|
||||
Self::from_slice(self)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl<T: Copy, const CAP: usize> core::ops::Deref for ArrayVec<T, CAP> {
|
||||
type Target = [T];
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.as_slice()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy, const CAP: usize> core::ops::DerefMut for ArrayVec<T, CAP> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
self.as_mut_slice()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Eq, const CAP: usize> Eq for ArrayVec<T, CAP> {}
|
||||
|
||||
impl<T: Copy + PartialEq, const CAP1: usize, const CAP2: usize> PartialEq<ArrayVec<T, CAP2>> for ArrayVec<T, CAP1> {
|
||||
fn eq(&self, other: &ArrayVec<T, CAP2>) -> bool {
|
||||
**self == **other
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + PartialEq, const CAP: usize> PartialEq<[T]> for ArrayVec<T, CAP> {
|
||||
fn eq(&self, other: &[T]) -> bool {
|
||||
**self == *other
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + PartialEq, const CAP: usize> PartialEq<ArrayVec<T, CAP>> for [T] {
|
||||
fn eq(&self, other: &ArrayVec<T, CAP>) -> bool {
|
||||
*self == **other
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + PartialEq, const CAP: usize, const LEN: usize> PartialEq<[T; LEN]> for ArrayVec<T, CAP> {
|
||||
fn eq(&self, other: &[T; LEN]) -> bool {
|
||||
**self == *other
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + PartialEq, const CAP: usize, const LEN: usize> PartialEq<ArrayVec<T, CAP>> for [T; LEN] {
|
||||
fn eq(&self, other: &ArrayVec<T, CAP>) -> bool {
|
||||
*self == **other
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Ord, const CAP: usize> Ord for ArrayVec<T, CAP> {
|
||||
fn cmp(&self, other: &ArrayVec<T, CAP>) -> core::cmp::Ordering {
|
||||
(**self).cmp(&**other)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + PartialOrd, const CAP1: usize, const CAP2: usize> PartialOrd<ArrayVec<T, CAP2>> for ArrayVec<T, CAP1> {
|
||||
fn partial_cmp(&self, other: &ArrayVec<T, CAP2>) -> Option<core::cmp::Ordering> {
|
||||
(**self).partial_cmp(&**other)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + fmt::Debug, const CAP: usize> fmt::Debug for ArrayVec<T, CAP> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
fmt::Debug::fmt(&**self, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + core::hash::Hash, const CAP: usize> core::hash::Hash for ArrayVec<T, CAP> {
|
||||
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
|
||||
core::hash::Hash::hash(&**self, state)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::ArrayVec;
|
||||
|
||||
#[test]
|
||||
fn arrayvec_ops() {
|
||||
let mut av = ArrayVec::<_, 1>::new();
|
||||
assert!(av.is_empty());
|
||||
av.push(42);
|
||||
assert_eq!(av.len(), 1);
|
||||
assert_eq!(av, [42]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn overflow_push() {
|
||||
let mut av = ArrayVec::<_, 0>::new();
|
||||
av.push(42);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn overflow_extend() {
|
||||
let mut av = ArrayVec::<_, 0>::new();
|
||||
av.extend_from_slice(&[42]);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,92 @@
|
|||
//! Contains tools (workarounds) to make implementing `const fn`s easier.
|
||||
|
||||
/// Copies first `$len` bytes from `$slice` and returns them as an array.
|
||||
///
|
||||
/// Returns `None` if `$len > $slice.len()`. `$len` must be (obviously) statically known.
|
||||
/// Calling from non-const context doesn't affect performance.
|
||||
#[macro_export]
|
||||
macro_rules! copy_byte_array_from_slice {
|
||||
($slice:expr, $len:expr) => {
|
||||
if $len > $slice.len() {
|
||||
None
|
||||
} else {
|
||||
let mut array = [0u8; $len];
|
||||
// Note: produces same assemble as copy_from_slice
|
||||
let mut i = 0;
|
||||
while i < $len {
|
||||
array[i] = $slice[i];
|
||||
i += 1;
|
||||
}
|
||||
Some(array)
|
||||
}
|
||||
};
|
||||
}
|
||||
pub use copy_byte_array_from_slice;
|
||||
|
||||
/// Concatenates two byte slices or byte arrays (or combination) to a single array.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This macro panics if `$len` is not equal to the sum of `$a.len()` and `$b.len()`.
|
||||
#[macro_export]
|
||||
macro_rules! concat_bytes_to_arr {
|
||||
($a:expr, $b:expr, $len:expr) => {{
|
||||
// avoid repeated eval
|
||||
let a = $a;
|
||||
let b = $b;
|
||||
|
||||
#[allow(unconditional_panic)]
|
||||
let _ = [(); 1][($len != a.len() + b.len()) as usize];
|
||||
|
||||
let mut output = [0u8; $len];
|
||||
let mut i = 0;
|
||||
while i < a.len() {
|
||||
output[i] = $a[i];
|
||||
i += 1;
|
||||
}
|
||||
while i < a.len() + b.len() {
|
||||
output[i] = b[i - a.len()];
|
||||
i += 1;
|
||||
}
|
||||
output
|
||||
}};
|
||||
}
|
||||
pub use concat_bytes_to_arr;
|
||||
|
||||
#[macro_export]
|
||||
/// Enables const fn in specified Rust version
|
||||
macro_rules! cond_const {
|
||||
($($(#[$attr:meta])* $vis:vis const(in $ver:ident $(= $human_ver:literal)?) fn $name:ident$(<$($gen:tt)*>)?($($args:tt)*) $(-> $ret:ty)? $body:block)+ ) => {
|
||||
$(
|
||||
#[cfg($ver)]
|
||||
$(#[$attr])*
|
||||
$(
|
||||
#[doc = "\nNote: the function is only `const` in Rust "]
|
||||
#[doc = $human_ver]
|
||||
#[doc = "."]
|
||||
)?
|
||||
$vis const fn $name$(<$($gen)*>)?($($args)*) $(-> $ret)? $body
|
||||
|
||||
#[cfg(not($ver))]
|
||||
$(#[$attr])*
|
||||
$vis fn $name$(<$($gen)*>)?($($args)*) $(-> $ret)? $body
|
||||
)+
|
||||
};
|
||||
($($(#[$attr:meta])* $vis:vis const(in $ver:ident $(= $human_ver:literal)?) unsafe fn $name:ident$(<$($gen:tt)*>)?($($args:tt)*) $(-> $ret:ty)? $body:block)+ ) => {
|
||||
$(
|
||||
#[cfg($ver)]
|
||||
$(#[$attr])*
|
||||
$(
|
||||
#[doc = "\nNote: the function is only `const` in Rust "]
|
||||
#[doc = $human_ver]
|
||||
#[doc = " and newer."]
|
||||
)?
|
||||
$vis const unsafe fn $name$(<$($gen)*>)?($($args)*) $(-> $ret)? $body
|
||||
|
||||
#[cfg(not($ver))]
|
||||
$(#[$attr])*
|
||||
$vis unsafe fn $name$(<$($gen)*>)?($($args)*) $(-> $ret)? $body
|
||||
)+
|
||||
};
|
||||
}
|
||||
pub use cond_const;
|
|
@ -20,6 +20,8 @@ extern crate alloc;
|
|||
#[cfg(feature = "std")]
|
||||
extern crate std;
|
||||
|
||||
pub mod array_vec;
|
||||
pub mod const_tools;
|
||||
pub mod error;
|
||||
pub mod macros;
|
||||
mod parse;
|
||||
|
|
Loading…
Reference in New Issue