Skip to content

Commit

Permalink
Substituted stackvector with arrayvec.
Browse files Browse the repository at this point in the history
  • Loading branch information
Alex Huszagh committed Sep 2, 2019
1 parent e651824 commit cc19e24
Show file tree
Hide file tree
Showing 8 changed files with 130 additions and 58 deletions.
6 changes: 3 additions & 3 deletions lexical-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ travis-ci = { repository = "Alexhuszagh/rust-lexical" }
[dependencies]
cfg-if = "0.1"
static_assertions = "0.3.3"
# Use stack-vector for the correct parser.
stackvector = { version = "^1.0.5", optional = true }
# Use arrayvec for the correct parser.
arrayvec = { version = "^0.4.0", optional = true, features = ["array-sizes-33-128"] }

This comment has been minimized.

Copy link
@matklad

matklad Sep 2, 2019

Cargo treats “^0.4.0” exactly like “0.4.0”, so there’s no need for circumflex.

This comment has been minimized.

Copy link
@Alexhuszagh

Alexhuszagh Sep 2, 2019

Owner

@matklad Thank you for catching that typo, that should be ^0.4. I'm going to run a few more eyes over the diffs, I'm making more mistakes that usual.

This comment has been minimized.

Copy link
@matklad

matklad Sep 2, 2019

^0.4, in Cargo, is equivalent to 0.4 as well.

This comment has been minimized.

Copy link
@Alexhuszagh

Alexhuszagh Sep 2, 2019

Owner

Insert Gob voice, "I've made a huge tiny mistake." Luckily, the behaviour I wanted was exactly what was being done...

# Optimized Grisu3 implementation, a well-tested, correct algorithm.
dtoa = { version = "0.4", optional = true }
# Optimized Ryu implementation, the fastest correct algorithm.
Expand All @@ -41,7 +41,7 @@ rustc_version = "0.2"
[features]
default = ["correct", "ryu", "std"]
# Use the correct atof parser.
correct = ["stackvector", "table"]
correct = ["arrayvec", "table"]
# Use the optimized Grisu3 implementation from dtoa (not recommended).
grisu3 = ["dtoa"]
# Add support for parsing non-decimal float and integer strings.
Expand Down
28 changes: 14 additions & 14 deletions lexical-core/src/atof/algorithm/bigcomp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ perftools_inline!{
pub fn scaling_factor(radix: u32, sci_exponent: u32)
-> Bigfloat
{
let mut factor = Bigfloat { data: stackvec![1], exp: 0 };
let mut factor = Bigfloat { data: arrvec![1], exp: 0 };
factor.imul_power(radix, sci_exponent);
factor
}}
Expand All @@ -146,7 +146,7 @@ pub(super) fn make_ratio<F: Float>(radix: u32, sci_exponent: i32, f: F, kind: Ro
// if it's the denominator, we need to multiply it into the numerator.
num = factor;
num.imul_large(&theor);
den = Bigfloat { data: stackvec![1], exp: -theor.exp };
den = Bigfloat { data: arrvec![1], exp: -theor.exp };
} else {
num = theor;
den = factor;
Expand Down Expand Up @@ -295,25 +295,25 @@ mod tests {
let (num3, den3) = make_ratio(10, 307, 8.98846567431158e+307f64, RoundingKind::NearestTieEven);

#[cfg(limb_width_32)] {
assert_eq!(num1, Bigfloat { data: stackvec![1725370368, 1252154597, 1017462556, 675087593, 2805901938, 1401824593, 1124332496, 2380663002, 1612846757, 4128923878, 1492915356, 437569744, 2975325085, 3331531962, 3367627909, 730662168, 2699172281, 1440714968, 2778340312, 690527038, 1297115354, 763425880, 1453089653, 331561842], exp: 312 });
assert_eq!(den1, Bigfloat { data: stackvec![0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 134217728], exp: 312 });
assert_eq!(num1, Bigfloat { data: arrvec![1725370368, 1252154597, 1017462556, 675087593, 2805901938, 1401824593, 1124332496, 2380663002, 1612846757, 4128923878, 1492915356, 437569744, 2975325085, 3331531962, 3367627909, 730662168, 2699172281, 1440714968, 2778340312, 690527038, 1297115354, 763425880, 1453089653, 331561842], exp: 312 });
assert_eq!(den1, Bigfloat { data: arrvec![0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 134217728], exp: 312 });

assert_eq!(num2, Bigfloat { data: stackvec![881143808, 3756463792, 3052387668, 2025262779, 4122738518, 4205473780, 3372997488, 2847021710, 543572976, 3796837043, 183778774, 1312709233, 336040663, 1404661296, 1512949137, 2191986506, 3802549547, 27177609, 4040053641, 2071581115, 3891346062, 2290277640, 64301663, 994685527], exp: 312 });
assert_eq!(den2, Bigfloat { data: stackvec![0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 134217728], exp: 312 });
assert_eq!(num2, Bigfloat { data: arrvec![881143808, 3756463792, 3052387668, 2025262779, 4122738518, 4205473780, 3372997488, 2847021710, 543572976, 3796837043, 183778774, 1312709233, 336040663, 1404661296, 1512949137, 2191986506, 3802549547, 27177609, 4040053641, 2071581115, 3891346062, 2290277640, 64301663, 994685527], exp: 312 });
assert_eq!(den2, Bigfloat { data: arrvec![0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 134217728], exp: 312 });

assert_eq!(num3, Bigfloat { data: stackvec![0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1024, 2147483648], exp: 288 });
assert_eq!(den3, Bigfloat { data: stackvec![1978138624, 2671552565, 2938166866, 3588566204, 1860064291, 2104472219, 2014975858, 2797301608, 462262832, 318515330, 1101517094, 1738264167, 3721375114, 414401884, 1406861075, 3053102637, 387329537, 2051556775, 1867945454, 3717689914, 1434550525, 1446648206, 238915486], exp: 288 });
assert_eq!(num3, Bigfloat { data: arrvec![0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1024, 2147483648], exp: 288 });
assert_eq!(den3, Bigfloat { data: arrvec![1978138624, 2671552565, 2938166866, 3588566204, 1860064291, 2104472219, 2014975858, 2797301608, 462262832, 318515330, 1101517094, 1738264167, 3721375114, 414401884, 1406861075, 3053102637, 387329537, 2051556775, 1867945454, 3717689914, 1434550525, 1446648206, 238915486], exp: 288 });
}

#[cfg(limb_width_64)] {
assert_eq!(num1, Bigfloat { data: stackvec![7410409304047484928, 4369968404176723173, 12051257060168107241, 4828971301551875409, 6927124077155322074, 6412022633845121254, 12778923935480989904, 14463851737583396026, 11592856673895384344, 11932880778639151320, 5571068025259989822, 6240972538554414168, 331561842], exp: 280 });
assert_eq!(den1, Bigfloat { data: stackvec![0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 134217728], exp: 280 });
assert_eq!(num1, Bigfloat { data: arrvec![7410409304047484928, 4369968404176723173, 12051257060168107241, 4828971301551875409, 6927124077155322074, 6412022633845121254, 12778923935480989904, 14463851737583396026, 11592856673895384344, 11932880778639151320, 5571068025259989822, 6240972538554414168, 331561842], exp: 280 });
assert_eq!(den1, Bigfloat { data: arrvec![0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 134217728], exp: 280 });

assert_eq!(num2, Bigfloat { data: stackvec![3784483838432903168, 13109905212530169520, 17707027106794770107, 14486913904655626228, 2334628157756414606, 789323827825812147, 1443283659023866481, 6498067065331084848, 16331825947976601418, 17351898262207902345, 16713204075779969467, 276173541953690888, 994685527], exp: 280 });
assert_eq!(den2, Bigfloat { data: stackvec![0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 134217728], exp: 280 });
assert_eq!(num2, Bigfloat { data: arrvec![3784483838432903168, 13109905212530169520, 17707027106794770107, 14486913904655626228, 2334628157756414606, 789323827825812147, 1443283659023866481, 6498067065331084848, 16331825947976601418, 17351898262207902345, 16713204075779969467, 276173541953690888, 994685527], exp: 280 });
assert_eq!(den2, Bigfloat { data: arrvec![0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 134217728], exp: 280 });

assert_eq!(num3, Bigfloat { data: stackvec![0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4398046511104, 2147483648], exp: 288 });
assert_eq!(den3, Bigfloat { data: stackvec![11474230898198052864, 15412774488649031250, 9038639357805614115, 12014318925423187826, 1368012926086910512, 7465787750175199526, 1779842542902160778, 13112975978653220627, 8811369254899559937, 15967356599166997998, 6213306735021621501, 238915486], exp: 288 });
assert_eq!(num3, Bigfloat { data: arrvec![0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4398046511104, 2147483648], exp: 288 });
assert_eq!(den3, Bigfloat { data: arrvec![11474230898198052864, 15412774488649031250, 9038639357805614115, 12014318925423187826, 1368012926086910512, 7465787750175199526, 1779842542902160778, 13112975978653220627, 8811369254899559937, 15967356599166997998, 6213306735021621501, 238915486], exp: 288 });
}
}

Expand Down
10 changes: 5 additions & 5 deletions lexical-core/src/atof/algorithm/bignum.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,13 @@ if #[cfg(feature = "radix")] {
} else {
// Maximum denominator is 767 mantissa digits + 324 exponent,
// or 1091 digits, or approximately 3600 bits (round up to 4k).
use stackvector;
use arrayvec;

#[cfg(limb_width_32)]
type IntStorageType = stackvector::StackVec<[Limb; 128]>;
type IntStorageType = arrayvec::ArrayVec<[Limb; 128]>;

#[cfg(limb_width_64)]
type IntStorageType = stackvector::StackVec<[Limb; 64]>;
type IntStorageType = arrayvec::ArrayVec<[Limb; 64]>;
}} // cfg_if

// BIGINT
Expand Down Expand Up @@ -65,9 +65,9 @@ impl LargeOps for Bigint {
// Adjust the storage capacity for the underlying array.
cfg_if! {
if #[cfg(limb_width_64)] {
type FloatStorageType = stackvector::StackVec<[Limb; 20]>;
type FloatStorageType = arrayvec::ArrayVec<[Limb; 20]>;
} else {
type FloatStorageType = stackvector::StackVec<[Limb; 36]>;
type FloatStorageType = arrayvec::ArrayVec<[Limb; 36]>;
}} // cfg_if

/// Storage for a big floating-point type.
Expand Down
12 changes: 6 additions & 6 deletions lexical-core/src/atof/algorithm/math.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2923,7 +2923,7 @@ mod tests {
impl Bigint {
#[inline]
pub fn new() -> Bigint {
Bigint { data: stackvec![] }
Bigint { data: arrvec![] }
}
}

Expand Down Expand Up @@ -3128,9 +3128,9 @@ mod tests {
fn trailing_zero_limbs_test() {
assert_eq!(Bigint::new().trailing_zero_limbs(), 0);

assert_eq!(Bigint { data: stackvec![0xFF] }.trailing_zero_limbs(), 0);
assert_eq!(Bigint { data: stackvec![0, 0xFF000] }.trailing_zero_limbs(), 1);
assert_eq!(Bigint { data: stackvec![0, 0, 0, 0xFF000] }.trailing_zero_limbs(), 3);
assert_eq!(Bigint { data: arrvec![0xFF] }.trailing_zero_limbs(), 0);
assert_eq!(Bigint { data: arrvec![0, 0xFF000] }.trailing_zero_limbs(), 1);
assert_eq!(Bigint { data: arrvec![0, 0, 0, 0xFF000] }.trailing_zero_limbs(), 3);
}

#[test]
Expand Down Expand Up @@ -3200,11 +3200,11 @@ mod tests {

#[test]
fn pad_zero_digits_test() {
let mut x = Bigint { data: stackvec![0, 0, 0, 1] };
let mut x = Bigint { data: arrvec![0, 0, 0, 1] };
x.pad_zero_digits(3);
assert_eq!(x.data.as_slice(), &[0, 0, 0, 0, 0, 0, 1]);

let mut x = Bigint { data: stackvec![1] };
let mut x = Bigint { data: arrvec![1] };
x.pad_zero_digits(1);
assert_eq!(x.data.as_slice(), &[0, 1]);
}
Expand Down
5 changes: 2 additions & 3 deletions lexical-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,10 +149,9 @@ extern crate proptest;
#[cfg_attr(test, macro_use)]
extern crate alloc;

// Use stackvector for atof.
// Use arrayvec for atof.
#[cfg(feature = "correct")]
#[macro_use]
extern crate stackvector;
extern crate arrayvec;

// Ensure only one back-end is enabled.
#[cfg(all(feature = "grisu3", feature = "ryu"))]
Expand Down
2 changes: 2 additions & 0 deletions lexical-core/src/util/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ if #[cfg(feature = "correct")] {
mod bound;
mod range_bounds;
mod slice_index;

#[macro_use]
mod sequence;
} else {
mod wrapped;
Expand Down
119 changes: 95 additions & 24 deletions lexical-core/src/util/sequence.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
#![allow(dead_code)]

use lib::{cmp, iter, marker, ops, ptr, slice};
use stackvector;
use arrayvec;
use super::bound::Bound;
use super::pointer_methods::PointerMethods;
use super::range_bounds::RangeBounds;
Expand All @@ -12,6 +12,69 @@ use super::slice_index::SliceIndex;
#[cfg(all(feature = "correct", feature = "radix"))]
use lib::Vec;

// ARRVEC

/// Macro to automate simplify the creation of an ArrayVec.
#[macro_export]
macro_rules! arrvec {
// This only works if the ArrayVec is the same size as the input array.
($elem:expr; $n:expr) => ({
$crate::arrayvec::ArrayVec::from([$elem; $n])
});
// This just repeatedly calls `push`. I don't believe there's a concise way to count the number of expressions.
($($x:expr),*$(,)*) => ({
// Allow an unused mut variable, since if the sequence is empty,
// the vec will never be mutated.
#[allow(unused_mut)] {
let mut vec = $crate::arrayvec::ArrayVec::new();
$(vec.push($x);)*
vec
}
});
}

// INSERT MANY

/// Insert multiple elements at position `index`.
///
/// Shifts all following elements toward the
/// back.
pub fn insert_many<V, T, I>(vec: &mut V, index: usize, iterable: I)
where V: VecLike<T>,
I: iter::IntoIterator<Item=T>
{
assert!(index <= vec.len());

let iter = iterable.into_iter();
let (lower_bound, upper_bound) = iter.size_hint();
let upper_bound = upper_bound.expect("iterable must provide upper bound.");
assert!(vec.len() + upper_bound <= vec.capacity());

This comment has been minimized.

Copy link
@matklad

matklad Sep 2, 2019

Note that if size_hint implementation is wrong, this code can lead to buffer overrun in safe code. Unsafe code can’t trust that safe traits are implemented correctly. See: https://doc.rust-lang.org/nomicon/safe-unsafe-meaning.html.

This comment has been minimized.

Copy link
@Alexhuszagh

Alexhuszagh Sep 2, 2019

Owner

This is true, @matklad, however, the only time this is actually implemented is using iter::repeat(n).take(i), so the size_hint cannot be implemented incorrectly. But, this is a great point, and shouldn't be relied upon.

I can take the lower_hint and then reserve that much capacity, and add a condition during insertion to ensure we never overrun the buffer? Thanks for catching this.

This comment has been minimized.

Copy link
@matklad

matklad Sep 2, 2019

What about this:

  • bound I: iter::ExactSizeIterator (just to avoid dealing with size_hint API)
  • call I.take(I.len()) internally: I think two takes should basically cancel each other anyway, and Take is TrustedLen, and it's OK to trust TrustedLen. We can't directly bound by : TrustedLen b/c that's unstable though.

This comment has been minimized.

Copy link
@Alexhuszagh

Alexhuszagh Sep 2, 2019

Owner

@matklad I looked carefully, and Rust-smallvec seems to have solved this fairly nicely. My issue (and TrustedLen, ExactSizeIterator, and size_hint all don't solve this) is if we cannot trust the upper bound, or lower bound, or even the .len() of the iterator, since it may not be accurate, then we shouldn't make any safety guarantees on its value.

A simple proof-of-concept shows it's easy to lie, which could happen by accident, breaking safety guarantees. Under-reporting the number of elements would likely be enough to cause a buffer over-run. However, smallvec has what seems to be a good solution, and it uses the same licensing as lexical.

This comment has been minimized.

Copy link
@matklad

matklad Sep 2, 2019

My issue (and TrustedLen, ExactSizeIterator, and size_hint all don't solve this)

TrustedLen does solve this issue, because it's an unsafe trait with a contract "size_hint is correct". So, with a bound like T: TrustedLen + ExtaxtSize, the impl which just blindly trusts .len() would be correct.

The problem is that we can't use TrustedLen directly, b/c its an unstable trait. However, if we call it.take(it.len()) inside insert_many, we'll get std::iter::Take, which actually implements TrustedLen. That is a little iffy, but should work: we trust that "concrete" stdlib type is implemented correctly, and not that arbitrary user-provided impl is implemented correctly.

small_vec's solution is also good of course!

This comment has been minimized.

Copy link
@Alexhuszagh

Alexhuszagh Sep 2, 2019

Owner

@matklad Yes, in theory, although checking the implementation, consumers are supposed to make guarantees of it based off the size_hint. However, the part I was missing, making it unsafe, trait seems to solve these issues, since if someone else breaks unsafe guarantees, that's not the library's fault. I'll be using smallvec's implementation in the meantime though.

This comment has been minimized.

Copy link
@matklad

matklad Sep 2, 2019

Yeah, and I actually miss the part that Take implements TrustedLen conditionally, so we can't trust its size in the end :( I think it's still valid to assume that .take(n) produces at most n elements, but that is completely orthogonal to TrustedLen: basically, we trust that stdlib impl is not stupid.


if index == vec.len() {
return vec.extend(iter);
}

unsafe {
let old_len = vec.len();
let ptr = vec.as_mut_ptr().padd(index);

// Move the trailing elements.
ptr::copy(ptr, ptr.padd(lower_bound), old_len - index);

This comment was marked as resolved.

Copy link
@matklad

matklad Sep 2, 2019

This must be copy_non_overlapping?

This comment was marked as resolved.

Copy link
@matklad

matklad Sep 2, 2019

Sorry, i’ve just mixed up the two, it must not be non_overlapping of course!


// In case the iterator panics, don't double-drop the items we just copied above.
vec.set_len(index);

let mut num_added = 0;
for element in iter {
let cur = ptr.padd(num_added);
ptr::write(cur, element);
num_added += 1;
}

vec.set_len(old_len + num_added);
}
}

// REMOVE_MANY

/// Remove many elements from a vec-like container.
Expand Down Expand Up @@ -217,17 +280,17 @@ impl<T> SliceLikeImpl<T> for Vec<T> {
}
}

impl<A: stackvector::Array> SliceLikeImpl<A::Item> for stackvector::StackVec<A> {
impl<A: arrayvec::Array> SliceLikeImpl<A::Item> for arrayvec::ArrayVec<A> {
// AS SLICE

#[inline]
fn as_slice(&self) -> &[A::Item] {
stackvector::StackVec::as_slice(self)
arrayvec::ArrayVec::as_slice(self)
}

#[inline]
fn as_mut_slice(&mut self) -> &mut [A::Item] {
stackvector::StackVec::as_mut_slice(self)
arrayvec::ArrayVec::as_mut_slice(self)
}
}

Expand Down Expand Up @@ -998,7 +1061,7 @@ impl<T> SliceLike<T> for Vec<T> {
}
}

impl<A: stackvector::Array> SliceLike<A::Item> for stackvector::StackVec<A> {
impl<A: arrayvec::Array> SliceLike<A::Item> for arrayvec::ArrayVec<A> {
// GET

/// Get an immutable reference to item at index.
Expand Down Expand Up @@ -1250,22 +1313,22 @@ impl<T> VecLike<T> for Vec<T> {
}
}

impl<A: stackvector::Array> VecLike<A::Item> for stackvector::StackVec<A> {
impl<A: arrayvec::Array> VecLike<A::Item> for arrayvec::ArrayVec<A> {
#[inline]
fn new() -> stackvector::StackVec<A> {
stackvector::StackVec::new()
fn new() -> arrayvec::ArrayVec<A> {
arrayvec::ArrayVec::new()
}

#[inline]
fn with_capacity(capacity: usize) -> stackvector::StackVec<A> {
let mut v = stackvector::StackVec::new();
fn with_capacity(capacity: usize) -> arrayvec::ArrayVec<A> {
let mut v = arrayvec::ArrayVec::new();
v.reserve(capacity);
v
}

#[inline]
fn capacity(&self) -> usize {
stackvector::StackVec::capacity(self)
arrayvec::ArrayVec::capacity(self)
}

#[inline]
Expand All @@ -1284,47 +1347,47 @@ impl<A: stackvector::Array> VecLike<A::Item> for stackvector::StackVec<A> {

#[inline]
fn truncate(&mut self, len: usize) {
stackvector::StackVec::truncate(self, len)
arrayvec::ArrayVec::truncate(self, len)
}

#[inline]
unsafe fn set_len(&mut self, new_len: usize) {
stackvector::StackVec::set_len(self, new_len);
arrayvec::ArrayVec::set_len(self, new_len);
}

#[inline]
fn swap_remove(&mut self, index: usize) -> A::Item {
stackvector::StackVec::swap_remove(self, index)
arrayvec::ArrayVec::swap_remove(self, index)
}

#[inline]
fn insert(&mut self, index: usize, element: A::Item) {
stackvector::StackVec::insert(self, index, element)
arrayvec::ArrayVec::insert(self, index, element)
}

#[inline]
fn remove(&mut self, index: usize) -> A::Item {
stackvector::StackVec::remove(self, index)
arrayvec::ArrayVec::remove(self, index)
}

#[inline]
fn push(&mut self, value: A::Item) {
stackvector::StackVec::push(self, value);
arrayvec::ArrayVec::push(self, value);
}

#[inline]
fn pop(&mut self) -> Option<A::Item> {
stackvector::StackVec::pop(self)
arrayvec::ArrayVec::pop(self)
}

#[inline]
fn clear(&mut self) {
stackvector::StackVec::clear(self);
arrayvec::ArrayVec::clear(self);
}

#[inline]
fn insert_many<I: iter::IntoIterator<Item=A::Item>>(&mut self, index: usize, iterable: I) {
stackvector::StackVec::insert_many(self, index, iterable)
insert_many(self, index, iterable)
}

#[inline]
Expand Down Expand Up @@ -1362,17 +1425,25 @@ impl<T> CloneableVecLike<T> for Vec<T>
}
}

impl<A: stackvector::Array> CloneableVecLike<A::Item> for stackvector::StackVec<A>
where A::Item: Clone + Copy + Send
impl<A: arrayvec::Array> CloneableVecLike<A::Item> for arrayvec::ArrayVec<A>
where A: Send,
A::Index: Send,
A::Item: Clone + Copy + Send
{
#[inline]
fn extend_from_slice(&mut self, other: &[A::Item]) {
stackvector::StackVec::extend_from_slice(self, other)
self.extend(other.iter().cloned())
}

#[inline]
fn resize(&mut self, len: usize, value: A::Item) {
stackvector::StackVec::resize(self, len, value)
assert!(len <= self.capacity());
let old_len = self.len();
if len > old_len {
self.extend(iter::repeat(value).take(len - old_len));
} else {
self.truncate(len);
}
}
}

Expand Down
Loading

0 comments on commit cc19e24

Please sign in to comment.