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")]
|
#[cfg(feature = "std")]
|
||||||
extern crate std;
|
extern crate std;
|
||||||
|
|
||||||
|
pub mod array_vec;
|
||||||
|
pub mod const_tools;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
pub mod macros;
|
pub mod macros;
|
||||||
mod parse;
|
mod parse;
|
||||||
|
|
Loading…
Reference in New Issue