Replace StaticVec with SmallVec.
This commit is contained in:
parent
40850e6b1e
commit
afbcd0fc0b
@ -18,6 +18,7 @@ categories = [ "no-std", "embedded", "wasm", "parser-implementations" ]
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
num-traits = { version = "0.2.11", default-features = false }
|
num-traits = { version = "0.2.11", default-features = false }
|
||||||
|
smallvec = { version = "1.4.1", default-features = false }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
#default = ["unchecked", "sync", "no_optimize", "no_float", "only_i32", "no_index", "no_object", "no_function", "no_module"]
|
#default = ["unchecked", "sync", "no_optimize", "no_float", "only_i32", "no_index", "no_object", "no_function", "no_module"]
|
||||||
|
@ -14,7 +14,7 @@ Easy
|
|||||||
|
|
||||||
* Easily [call a script-defined function]({{rootUrl}}/engine/call-fn.md) from Rust.
|
* Easily [call a script-defined function]({{rootUrl}}/engine/call-fn.md) from Rust.
|
||||||
|
|
||||||
* Very few additional dependencies (right now only [`num-traits`](https://crates.io/crates/num-traits/) to do checked arithmetic operations);
|
* Very few additional dependencies (right now only [`num-traits`](https://crates.io/crates/num-traits/) to do checked arithmetic operations, and [`smallvec`](https://crates.io/crates/smallvec/));
|
||||||
for [`no-std`] builds, a number of additional dependencies are pulled in to provide for functionalities that used to be in `std`.
|
for [`no-std`] builds, a number of additional dependencies are pulled in to provide for functionalities that used to be in `std`.
|
||||||
|
|
||||||
Fast
|
Fast
|
||||||
|
@ -445,7 +445,7 @@ pub fn search_imports<'s>(
|
|||||||
state: &mut State,
|
state: &mut State,
|
||||||
modules: &Box<ModuleRef>,
|
modules: &Box<ModuleRef>,
|
||||||
) -> Result<&'s Module, Box<EvalAltResult>> {
|
) -> Result<&'s Module, Box<EvalAltResult>> {
|
||||||
let (root, root_pos) = modules.get(0);
|
let (root, root_pos) = &modules[0];
|
||||||
|
|
||||||
// Qualified - check if the root module is directly indexed
|
// Qualified - check if the root module is directly indexed
|
||||||
let index = if state.always_search {
|
let index = if state.always_search {
|
||||||
@ -478,7 +478,7 @@ pub fn search_imports_mut<'s>(
|
|||||||
state: &mut State,
|
state: &mut State,
|
||||||
modules: &Box<ModuleRef>,
|
modules: &Box<ModuleRef>,
|
||||||
) -> Result<&'s mut Module, Box<EvalAltResult>> {
|
) -> Result<&'s mut Module, Box<EvalAltResult>> {
|
||||||
let (root, root_pos) = modules.get(0);
|
let (root, root_pos) = &modules[0];
|
||||||
|
|
||||||
// Qualified - check if the root module is directly indexed
|
// Qualified - check if the root module is directly indexed
|
||||||
let index = if state.always_search {
|
let index = if state.always_search {
|
||||||
@ -652,7 +652,7 @@ impl Engine {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Pop the last index value
|
// Pop the last index value
|
||||||
let idx_val = idx_values.pop();
|
let idx_val = idx_values.pop().unwrap();
|
||||||
|
|
||||||
match chain_type {
|
match chain_type {
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
@ -1007,7 +1007,7 @@ impl Engine {
|
|||||||
idx_values.push(Dynamic::from(arg_values));
|
idx_values.push(Dynamic::from(arg_values));
|
||||||
}
|
}
|
||||||
Expr::FnCall(_) => unreachable!(),
|
Expr::FnCall(_) => unreachable!(),
|
||||||
Expr::Property(_) => idx_values.push(()), // Store a placeholder - no need to copy the property name
|
Expr::Property(_) => idx_values.push(().into()), // Store a placeholder - no need to copy the property name
|
||||||
Expr::Index(x) | Expr::Dot(x) => {
|
Expr::Index(x) | Expr::Dot(x) => {
|
||||||
let (lhs, rhs, _) = x.as_ref();
|
let (lhs, rhs, _) = x.as_ref();
|
||||||
|
|
||||||
|
@ -20,6 +20,7 @@ use crate::parser::CustomExpr;
|
|||||||
use crate::stdlib::{
|
use crate::stdlib::{
|
||||||
boxed::Box,
|
boxed::Box,
|
||||||
iter::empty,
|
iter::empty,
|
||||||
|
mem,
|
||||||
string::{String, ToString},
|
string::{String, ToString},
|
||||||
vec,
|
vec,
|
||||||
vec::Vec,
|
vec::Vec,
|
||||||
@ -436,7 +437,7 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr {
|
|||||||
// Array literal where everything is pure - promote the indexed item.
|
// Array literal where everything is pure - promote the indexed item.
|
||||||
// All other items can be thrown away.
|
// All other items can be thrown away.
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
let mut expr = a.0.take(i.0 as usize);
|
let mut expr = a.0.remove(i.0 as usize);
|
||||||
expr.set_position(a.1);
|
expr.set_position(a.1);
|
||||||
expr
|
expr
|
||||||
}
|
}
|
||||||
|
@ -1103,7 +1103,7 @@ fn parse_fn_call(
|
|||||||
eat_token(input, Token::RightParen);
|
eat_token(input, Token::RightParen);
|
||||||
|
|
||||||
let hash_script = if let Some(modules) = modules.as_mut() {
|
let hash_script = if let Some(modules) = modules.as_mut() {
|
||||||
modules.set_index(state.find_module(&modules.get(0).0));
|
modules.set_index(state.find_module(&modules[0].0));
|
||||||
|
|
||||||
// Rust functions are indexed in two steps:
|
// Rust functions are indexed in two steps:
|
||||||
// 1) Calculate a hash in a similar manner to script-defined functions,
|
// 1) Calculate a hash in a similar manner to script-defined functions,
|
||||||
@ -1145,7 +1145,7 @@ fn parse_fn_call(
|
|||||||
eat_token(input, Token::RightParen);
|
eat_token(input, Token::RightParen);
|
||||||
|
|
||||||
let hash_script = if let Some(modules) = modules.as_mut() {
|
let hash_script = if let Some(modules) = modules.as_mut() {
|
||||||
modules.set_index(state.find_module(&modules.get(0).0));
|
modules.set_index(state.find_module(&modules[0].0));
|
||||||
|
|
||||||
// Rust functions are indexed in two steps:
|
// Rust functions are indexed in two steps:
|
||||||
// 1) Calculate a hash in a similar manner to script-defined functions,
|
// 1) Calculate a hash in a similar manner to script-defined functions,
|
||||||
@ -1678,7 +1678,7 @@ fn parse_primary(
|
|||||||
|
|
||||||
// Qualifiers + variable name
|
// Qualifiers + variable name
|
||||||
*hash = calc_fn_hash(modules.iter().map(|(v, _)| v.as_str()), name, 0, empty());
|
*hash = calc_fn_hash(modules.iter().map(|(v, _)| v.as_str()), name, 0, empty());
|
||||||
modules.set_index(state.find_module(&modules.get(0).0));
|
modules.set_index(state.find_module(&modules[0].0));
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
@ -1936,7 +1936,7 @@ fn make_dot_expr(lhs: Expr, rhs: Expr, op_pos: Position) -> Result<Expr, ParseEr
|
|||||||
}
|
}
|
||||||
// lhs.module::id - syntax error
|
// lhs.module::id - syntax error
|
||||||
(_, Expr::Variable(x)) if x.1.is_some() => {
|
(_, Expr::Variable(x)) if x.1.is_some() => {
|
||||||
return Err(PERR::PropertyExpected.into_err(x.1.unwrap().get(0).1));
|
return Err(PERR::PropertyExpected.into_err(x.1.unwrap()[0].1));
|
||||||
}
|
}
|
||||||
// lhs.prop
|
// lhs.prop
|
||||||
(lhs, prop @ Expr::Property(_)) => Expr::Dot(Box::new((lhs, prop, op_pos))),
|
(lhs, prop @ Expr::Property(_)) => Expr::Dot(Box::new((lhs, prop, op_pos))),
|
||||||
@ -2197,25 +2197,25 @@ fn parse_binary_op(
|
|||||||
| Token::GreaterThanEqualsTo => Expr::FnCall(Box::new((op, None, hash, args, cmp_def))),
|
| Token::GreaterThanEqualsTo => Expr::FnCall(Box::new((op, None, hash, args, cmp_def))),
|
||||||
|
|
||||||
Token::Or => {
|
Token::Or => {
|
||||||
let rhs = args.pop();
|
let rhs = args.pop().unwrap();
|
||||||
let current_lhs = args.pop();
|
let current_lhs = args.pop().unwrap();
|
||||||
Expr::Or(Box::new((current_lhs, rhs, pos)))
|
Expr::Or(Box::new((current_lhs, rhs, pos)))
|
||||||
}
|
}
|
||||||
Token::And => {
|
Token::And => {
|
||||||
let rhs = args.pop();
|
let rhs = args.pop().unwrap();
|
||||||
let current_lhs = args.pop();
|
let current_lhs = args.pop().unwrap();
|
||||||
Expr::And(Box::new((current_lhs, rhs, pos)))
|
Expr::And(Box::new((current_lhs, rhs, pos)))
|
||||||
}
|
}
|
||||||
Token::In => {
|
Token::In => {
|
||||||
let rhs = args.pop();
|
let rhs = args.pop().unwrap();
|
||||||
let current_lhs = args.pop();
|
let current_lhs = args.pop().unwrap();
|
||||||
make_in_expr(current_lhs, rhs, pos)?
|
make_in_expr(current_lhs, rhs, pos)?
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Token::Period => {
|
Token::Period => {
|
||||||
let rhs = args.pop();
|
let rhs = args.pop().unwrap();
|
||||||
let current_lhs = args.pop();
|
let current_lhs = args.pop().unwrap();
|
||||||
make_dot_expr(current_lhs, rhs, pos)?
|
make_dot_expr(current_lhs, rhs, pos)?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
48
src/token.rs
48
src/token.rs
@ -21,7 +21,6 @@ use crate::stdlib::{
|
|||||||
iter::Peekable,
|
iter::Peekable,
|
||||||
str::{Chars, FromStr},
|
str::{Chars, FromStr},
|
||||||
string::{String, ToString},
|
string::{String, ToString},
|
||||||
vec::Vec,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
type LERR = LexError;
|
type LERR = LexError;
|
||||||
@ -747,8 +746,8 @@ pub fn parse_string_literal(
|
|||||||
pos: &mut Position,
|
pos: &mut Position,
|
||||||
enclosing_char: char,
|
enclosing_char: char,
|
||||||
) -> Result<String, (LexError, Position)> {
|
) -> Result<String, (LexError, Position)> {
|
||||||
let mut result = Vec::new();
|
let mut result: StaticVec<char> = Default::default();
|
||||||
let mut escape = String::with_capacity(12);
|
let mut escape: StaticVec<char> = Default::default();
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let next_char = stream.get_next().ok_or((LERR::UnterminatedString, *pos))?;
|
let next_char = stream.get_next().ok_or((LERR::UnterminatedString, *pos))?;
|
||||||
@ -787,8 +786,8 @@ pub fn parse_string_literal(
|
|||||||
// \x??, \u????, \U????????
|
// \x??, \u????, \U????????
|
||||||
ch @ 'x' | ch @ 'u' | ch @ 'U' if !escape.is_empty() => {
|
ch @ 'x' | ch @ 'u' | ch @ 'U' if !escape.is_empty() => {
|
||||||
let mut seq = escape.clone();
|
let mut seq = escape.clone();
|
||||||
seq.push(ch);
|
|
||||||
escape.clear();
|
escape.clear();
|
||||||
|
seq.push(ch);
|
||||||
|
|
||||||
let mut out_val: u32 = 0;
|
let mut out_val: u32 = 0;
|
||||||
let len = match ch {
|
let len = match ch {
|
||||||
@ -799,23 +798,31 @@ pub fn parse_string_literal(
|
|||||||
};
|
};
|
||||||
|
|
||||||
for _ in 0..len {
|
for _ in 0..len {
|
||||||
let c = stream
|
let c = stream.get_next().ok_or_else(|| {
|
||||||
.get_next()
|
(
|
||||||
.ok_or_else(|| (LERR::MalformedEscapeSequence(seq.to_string()), *pos))?;
|
LERR::MalformedEscapeSequence(seq.iter().cloned().collect()),
|
||||||
|
*pos,
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
seq.push(c);
|
seq.push(c);
|
||||||
pos.advance();
|
pos.advance();
|
||||||
|
|
||||||
out_val *= 16;
|
out_val *= 16;
|
||||||
out_val += c
|
out_val += c.to_digit(16).ok_or_else(|| {
|
||||||
.to_digit(16)
|
(
|
||||||
.ok_or_else(|| (LERR::MalformedEscapeSequence(seq.to_string()), *pos))?;
|
LERR::MalformedEscapeSequence(seq.iter().cloned().collect()),
|
||||||
|
*pos,
|
||||||
|
)
|
||||||
|
})?;
|
||||||
}
|
}
|
||||||
|
|
||||||
result.push(
|
result.push(char::from_u32(out_val).ok_or_else(|| {
|
||||||
char::from_u32(out_val)
|
(
|
||||||
.ok_or_else(|| (LERR::MalformedEscapeSequence(seq), *pos))?,
|
LERR::MalformedEscapeSequence(seq.into_iter().collect()),
|
||||||
);
|
*pos,
|
||||||
|
)
|
||||||
|
})?);
|
||||||
}
|
}
|
||||||
|
|
||||||
// \{enclosing_char} - escaped
|
// \{enclosing_char} - escaped
|
||||||
@ -828,7 +835,12 @@ pub fn parse_string_literal(
|
|||||||
ch if enclosing_char == ch && escape.is_empty() => break,
|
ch if enclosing_char == ch && escape.is_empty() => break,
|
||||||
|
|
||||||
// Unknown escape sequence
|
// Unknown escape sequence
|
||||||
_ if !escape.is_empty() => return Err((LERR::MalformedEscapeSequence(escape), *pos)),
|
_ if !escape.is_empty() => {
|
||||||
|
return Err((
|
||||||
|
LERR::MalformedEscapeSequence(escape.into_iter().collect()),
|
||||||
|
*pos,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
// Cannot have new-lines inside string literals
|
// Cannot have new-lines inside string literals
|
||||||
'\n' => {
|
'\n' => {
|
||||||
@ -983,7 +995,7 @@ fn get_next_token_inner(
|
|||||||
|
|
||||||
// digit ...
|
// digit ...
|
||||||
('0'..='9', _) => {
|
('0'..='9', _) => {
|
||||||
let mut result = Vec::new();
|
let mut result: StaticVec<char> = Default::default();
|
||||||
let mut radix_base: Option<u32> = None;
|
let mut radix_base: Option<u32> = None;
|
||||||
result.push(c);
|
result.push(c);
|
||||||
|
|
||||||
@ -1385,7 +1397,7 @@ fn get_identifier(
|
|||||||
start_pos: Position,
|
start_pos: Position,
|
||||||
first_char: char,
|
first_char: char,
|
||||||
) -> Option<(Token, Position)> {
|
) -> Option<(Token, Position)> {
|
||||||
let mut result = Vec::new();
|
let mut result: StaticVec<_> = Default::default();
|
||||||
result.push(first_char);
|
result.push(first_char);
|
||||||
|
|
||||||
while let Some(next_char) = stream.peek_next() {
|
while let Some(next_char) = stream.peek_next() {
|
||||||
@ -1400,7 +1412,7 @@ fn get_identifier(
|
|||||||
|
|
||||||
let is_valid_identifier = is_valid_identifier(result.iter().cloned());
|
let is_valid_identifier = is_valid_identifier(result.iter().cloned());
|
||||||
|
|
||||||
let identifier: String = result.into_iter().collect();
|
let identifier = result.into_iter().collect();
|
||||||
|
|
||||||
if !is_valid_identifier {
|
if !is_valid_identifier {
|
||||||
return Some((
|
return Some((
|
||||||
|
555
src/utils.rs
555
src/utils.rs
@ -1,8 +1,4 @@
|
|||||||
//! Module containing various utility types and functions.
|
//! Module containing various utility types and functions.
|
||||||
//!
|
|
||||||
//! # Safety
|
|
||||||
//!
|
|
||||||
//! The `StaticVec` type has some `unsafe` blocks to handle conversions between `MaybeUninit` and regular types.
|
|
||||||
|
|
||||||
use crate::fn_native::{shared_make_mut, shared_take, Shared};
|
use crate::fn_native::{shared_make_mut, shared_take, Shared};
|
||||||
|
|
||||||
@ -13,12 +9,9 @@ use crate::stdlib::{
|
|||||||
fmt,
|
fmt,
|
||||||
hash::{BuildHasher, Hash, Hasher},
|
hash::{BuildHasher, Hash, Hasher},
|
||||||
iter::FromIterator,
|
iter::FromIterator,
|
||||||
mem,
|
ops::{Add, AddAssign, Deref},
|
||||||
mem::MaybeUninit,
|
|
||||||
ops::{Add, AddAssign, Deref, DerefMut, Drop, Index, IndexMut},
|
|
||||||
str::FromStr,
|
str::FromStr,
|
||||||
string::{String, ToString},
|
string::{String, ToString},
|
||||||
vec::Vec,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(not(feature = "no_std"))]
|
#[cfg(not(feature = "no_std"))]
|
||||||
@ -27,6 +20,8 @@ use crate::stdlib::collections::hash_map::DefaultHasher;
|
|||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
use ahash::AHasher;
|
use ahash::AHasher;
|
||||||
|
|
||||||
|
use smallvec::SmallVec;
|
||||||
|
|
||||||
/// A hasher that only takes one single `u64` and returns it as a hash key.
|
/// A hasher that only takes one single `u64` and returns it as a hash key.
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panics
|
||||||
@ -92,549 +87,7 @@ pub fn calc_fn_spec<'a>(
|
|||||||
s.finish()
|
s.finish()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// [INTERNALS] An array-like type that holds a number of values in static storage for no-allocation, quick access.
|
pub type StaticVec<T> = SmallVec<[T; 4]>;
|
||||||
/// Exported under the `internals` feature only.
|
|
||||||
///
|
|
||||||
/// If too many items are stored, it converts into using a `Vec`.
|
|
||||||
///
|
|
||||||
///
|
|
||||||
/// This is essentially a knock-off of the [`staticvec`](https://crates.io/crates/staticvec) crate.
|
|
||||||
/// This simplified implementation here is to avoid pulling in another crate.
|
|
||||||
///
|
|
||||||
/// # Implementation
|
|
||||||
///
|
|
||||||
/// A `StaticVec` holds data in _either one_ of two storages: 1) a fixed-size array of `MAX_STATIC_VEC`
|
|
||||||
/// items, and 2) a dynamic `Vec`. At any time, either one of them (or both) must be empty, depending on the
|
|
||||||
/// total number of items.
|
|
||||||
///
|
|
||||||
/// There is a `len` field containing the total number of items held by the `StaticVec`.
|
|
||||||
///
|
|
||||||
/// The fixed-size array (`list`) is not initialized (i.e. initialized with `MaybeUninit::uninit()`).
|
|
||||||
///
|
|
||||||
/// When `len <= MAX_STATIC_VEC`, all elements are stored in the fixed-size array.
|
|
||||||
/// Array slots `>= len` are `MaybeUninit::uninit()` while slots `< len` are considered actual data.
|
|
||||||
/// In this scenario, the `Vec` (`more`) is empty.
|
|
||||||
///
|
|
||||||
/// As soon as we try to push a new item into the `StaticVec` that makes the total number exceed
|
|
||||||
/// `MAX_STATIC_VEC`, all the items in the fixed-sized array are taken out, replaced with
|
|
||||||
/// `MaybeUninit::uninit()` (via `mem::replace`) and pushed into the `Vec`.
|
|
||||||
/// Then the new item is added to the `Vec`.
|
|
||||||
///
|
|
||||||
/// Therefore, if `len > MAX_STATIC_VEC`, then the fixed-size array (`list`) is considered
|
|
||||||
/// empty and uninitialized while all data resides in the `Vec` (`more`).
|
|
||||||
///
|
|
||||||
/// When popping an item off of the `StaticVec`, the reverse is true. When `len = MAX_STATIC_VEC + 1`,
|
|
||||||
/// after popping the item, all the items residing in the `Vec` are moved back to the fixed-size array (`list`).
|
|
||||||
/// The `Vec` will then be empty.
|
|
||||||
///
|
|
||||||
/// Therefore, if `len <= MAX_STATIC_VEC`, data is in the fixed-size array (`list`).
|
|
||||||
/// Otherwise, data is in the `Vec` (`more`).
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// This type uses some unsafe code (mainly for uninitialized/unused array slots) for efficiency.
|
|
||||||
///
|
|
||||||
/// ## WARNING
|
|
||||||
///
|
|
||||||
/// This type is volatile and may change.
|
|
||||||
//
|
|
||||||
// TODO - remove unsafe code
|
|
||||||
pub struct StaticVec<T> {
|
|
||||||
/// Total number of values held.
|
|
||||||
len: usize,
|
|
||||||
/// Fixed-size storage for fast, no-allocation access.
|
|
||||||
list: [MaybeUninit<T>; MAX_STATIC_VEC],
|
|
||||||
/// Dynamic storage. For spill-overs.
|
|
||||||
more: Vec<T>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Maximum slots of fixed-size storage for a `StaticVec`.
|
|
||||||
/// 4 slots should be enough for most cases.
|
|
||||||
const MAX_STATIC_VEC: usize = 4;
|
|
||||||
|
|
||||||
impl<T> Drop for StaticVec<T> {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
self.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Hash> Hash for StaticVec<T> {
|
|
||||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
|
||||||
self.iter().for_each(|x| x.hash(state));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Default for StaticVec<T> {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
len: 0,
|
|
||||||
list: unsafe { mem::MaybeUninit::uninit().assume_init() },
|
|
||||||
more: Vec::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: PartialEq> PartialEq for StaticVec<T> {
|
|
||||||
fn eq(&self, other: &Self) -> bool {
|
|
||||||
if self.len != other.len || self.more != other.more {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.len > MAX_STATIC_VEC {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
mem::transmute::<_, &[T; MAX_STATIC_VEC]>(&self.list)
|
|
||||||
== mem::transmute::<_, &[T; MAX_STATIC_VEC]>(&other.list)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Clone> Clone for StaticVec<T> {
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
let mut value: Self = Default::default();
|
|
||||||
value.len = self.len;
|
|
||||||
|
|
||||||
if self.is_fixed_storage() {
|
|
||||||
for x in 0..self.len {
|
|
||||||
let item = self.list.get(x).unwrap();
|
|
||||||
let item_value = unsafe { mem::transmute::<_, &T>(item) };
|
|
||||||
value.list[x] = MaybeUninit::new(item_value.clone());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
value.more = self.more.clone();
|
|
||||||
}
|
|
||||||
|
|
||||||
value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Eq> Eq for StaticVec<T> {}
|
|
||||||
|
|
||||||
impl<T> FromIterator<T> for StaticVec<T> {
|
|
||||||
fn from_iter<X: IntoIterator<Item = T>>(iter: X) -> Self {
|
|
||||||
let mut vec = StaticVec::new();
|
|
||||||
|
|
||||||
for x in iter {
|
|
||||||
vec.push(x);
|
|
||||||
}
|
|
||||||
|
|
||||||
vec
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: 'static> IntoIterator for StaticVec<T> {
|
|
||||||
type Item = T;
|
|
||||||
type IntoIter = Box<dyn Iterator<Item = T>>;
|
|
||||||
|
|
||||||
fn into_iter(self) -> Self::IntoIter {
|
|
||||||
self.into_iter()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> StaticVec<T> {
|
|
||||||
/// Create a new `StaticVec`.
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Default::default()
|
|
||||||
}
|
|
||||||
/// Empty the `StaticVec`.
|
|
||||||
pub fn clear(&mut self) {
|
|
||||||
if self.is_fixed_storage() {
|
|
||||||
for x in 0..self.len {
|
|
||||||
self.extract_from_list(x);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
self.more.clear();
|
|
||||||
}
|
|
||||||
self.len = 0;
|
|
||||||
}
|
|
||||||
/// Extract a `MaybeUninit` into a concrete initialized type.
|
|
||||||
fn extract(value: MaybeUninit<T>) -> T {
|
|
||||||
unsafe { value.assume_init() }
|
|
||||||
}
|
|
||||||
/// Extract an item from the fixed-size array, replacing it with `MaybeUninit::uninit()`.
|
|
||||||
///
|
|
||||||
/// # Panics
|
|
||||||
///
|
|
||||||
/// Panics if fixed-size storage is not used, or if the `index` is out of bounds.
|
|
||||||
fn extract_from_list(&mut self, index: usize) -> T {
|
|
||||||
if !self.is_fixed_storage() {
|
|
||||||
panic!("not fixed storage in StaticVec");
|
|
||||||
}
|
|
||||||
if index >= self.len {
|
|
||||||
panic!("index OOB in StaticVec");
|
|
||||||
}
|
|
||||||
Self::extract(mem::replace(
|
|
||||||
self.list.get_mut(index).unwrap(),
|
|
||||||
MaybeUninit::uninit(),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
/// Set an item into the fixed-size array.
|
|
||||||
/// If `drop` is `true`, the original value is extracted then automatically dropped.
|
|
||||||
///
|
|
||||||
/// # Panics
|
|
||||||
///
|
|
||||||
/// Panics if fixed-size storage is not used, or if the `index` is out of bounds.
|
|
||||||
fn set_into_list(&mut self, index: usize, value: T, drop: bool) {
|
|
||||||
if !self.is_fixed_storage() {
|
|
||||||
panic!("not fixed storage in StaticVec");
|
|
||||||
}
|
|
||||||
// Allow setting at most one slot to the right
|
|
||||||
if index > self.len {
|
|
||||||
panic!("index OOB in StaticVec");
|
|
||||||
}
|
|
||||||
let temp = mem::replace(self.list.get_mut(index).unwrap(), MaybeUninit::new(value));
|
|
||||||
if drop {
|
|
||||||
// Extract the original value - which will drop it automatically
|
|
||||||
Self::extract(temp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// Move item in the fixed-size array into the `Vec`.
|
|
||||||
///
|
|
||||||
/// # Panics
|
|
||||||
///
|
|
||||||
/// Panics if fixed-size storage is not used, or if the fixed-size storage is not full.
|
|
||||||
fn move_fixed_into_vec(&mut self, num: usize) {
|
|
||||||
if !self.is_fixed_storage() {
|
|
||||||
panic!("not fixed storage in StaticVec");
|
|
||||||
}
|
|
||||||
if self.len != num {
|
|
||||||
panic!("fixed storage is not full in StaticVec");
|
|
||||||
}
|
|
||||||
self.more.extend(
|
|
||||||
self.list
|
|
||||||
.iter_mut()
|
|
||||||
.take(num)
|
|
||||||
.map(|v| mem::replace(v, MaybeUninit::uninit()))
|
|
||||||
.map(Self::extract),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
/// Is data stored in fixed-size storage?
|
|
||||||
fn is_fixed_storage(&self) -> bool {
|
|
||||||
self.len <= MAX_STATIC_VEC
|
|
||||||
}
|
|
||||||
/// Push a new value to the end of this `StaticVec`.
|
|
||||||
pub fn push<X: Into<T>>(&mut self, value: X) {
|
|
||||||
if self.len == MAX_STATIC_VEC {
|
|
||||||
self.move_fixed_into_vec(MAX_STATIC_VEC);
|
|
||||||
self.more.push(value.into());
|
|
||||||
} else if self.is_fixed_storage() {
|
|
||||||
self.set_into_list(self.len, value.into(), false);
|
|
||||||
} else {
|
|
||||||
self.more.push(value.into());
|
|
||||||
}
|
|
||||||
self.len += 1;
|
|
||||||
}
|
|
||||||
/// Insert a new value to this `StaticVec` at a particular position.
|
|
||||||
///
|
|
||||||
/// # Panics
|
|
||||||
///
|
|
||||||
/// Panics if `index` is out of bounds.
|
|
||||||
pub fn insert<X: Into<T>>(&mut self, index: usize, value: X) {
|
|
||||||
if index > self.len {
|
|
||||||
panic!("index OOB in StaticVec");
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.len == MAX_STATIC_VEC {
|
|
||||||
self.move_fixed_into_vec(MAX_STATIC_VEC);
|
|
||||||
self.more.insert(index, value.into());
|
|
||||||
} else if self.is_fixed_storage() {
|
|
||||||
// Move all items one slot to the right
|
|
||||||
for x in (index..self.len).rev() {
|
|
||||||
let orig_value = self.extract_from_list(x);
|
|
||||||
self.set_into_list(x + 1, orig_value, false);
|
|
||||||
}
|
|
||||||
self.set_into_list(index, value.into(), false);
|
|
||||||
} else {
|
|
||||||
self.more.insert(index, value.into());
|
|
||||||
}
|
|
||||||
self.len += 1;
|
|
||||||
}
|
|
||||||
/// Pop a value from the end of this `StaticVec`.
|
|
||||||
///
|
|
||||||
/// # Panics
|
|
||||||
///
|
|
||||||
/// Panics if the `StaticVec` is empty.
|
|
||||||
pub fn pop(&mut self) -> T {
|
|
||||||
if self.is_empty() {
|
|
||||||
panic!("nothing to pop!");
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.is_fixed_storage() {
|
|
||||||
let value = self.extract_from_list(self.len - 1);
|
|
||||||
self.len -= 1;
|
|
||||||
value
|
|
||||||
} else {
|
|
||||||
let value = self.more.pop().unwrap();
|
|
||||||
self.len -= 1;
|
|
||||||
|
|
||||||
// Move back to the fixed list
|
|
||||||
if self.more.len() == MAX_STATIC_VEC {
|
|
||||||
for index in (0..MAX_STATIC_VEC).rev() {
|
|
||||||
let item = self.more.pop().unwrap();
|
|
||||||
self.set_into_list(index, item, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// Remove a value from this `StaticVec` at a particular position.
|
|
||||||
///
|
|
||||||
/// # Panics
|
|
||||||
///
|
|
||||||
/// Panics if `index` is out of bounds.
|
|
||||||
pub fn remove(&mut self, index: usize) -> T {
|
|
||||||
if index >= self.len {
|
|
||||||
panic!("index OOB in StaticVec");
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.is_fixed_storage() {
|
|
||||||
let value = self.extract_from_list(index);
|
|
||||||
|
|
||||||
// Move all items one slot to the left
|
|
||||||
for x in index + 1..self.len {
|
|
||||||
let orig_value = self.extract_from_list(x);
|
|
||||||
self.set_into_list(x - 1, orig_value, false);
|
|
||||||
}
|
|
||||||
self.len -= 1;
|
|
||||||
|
|
||||||
value
|
|
||||||
} else {
|
|
||||||
let value = self.more.remove(index);
|
|
||||||
self.len -= 1;
|
|
||||||
|
|
||||||
// Move back to the fixed list
|
|
||||||
if self.more.len() == MAX_STATIC_VEC {
|
|
||||||
for index in (0..MAX_STATIC_VEC).rev() {
|
|
||||||
let item = self.more.pop().unwrap();
|
|
||||||
self.set_into_list(index, item, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// Get the number of items in this `StaticVec`.
|
|
||||||
pub fn len(&self) -> usize {
|
|
||||||
self.len
|
|
||||||
}
|
|
||||||
/// Is this `StaticVec` empty?
|
|
||||||
pub fn is_empty(&self) -> bool {
|
|
||||||
self.len == 0
|
|
||||||
}
|
|
||||||
/// Get a reference to the item at a particular index.
|
|
||||||
///
|
|
||||||
/// # Panics
|
|
||||||
///
|
|
||||||
/// Panics if `index` is out of bounds.
|
|
||||||
pub fn get(&self, index: usize) -> &T {
|
|
||||||
if index >= self.len {
|
|
||||||
panic!("index OOB in StaticVec");
|
|
||||||
}
|
|
||||||
|
|
||||||
let list = unsafe { mem::transmute::<_, &[T; MAX_STATIC_VEC]>(&self.list) };
|
|
||||||
|
|
||||||
if self.is_fixed_storage() {
|
|
||||||
list.get(index).unwrap()
|
|
||||||
} else {
|
|
||||||
self.more.get(index).unwrap()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// Get a mutable reference to the item at a particular index.
|
|
||||||
///
|
|
||||||
/// # Panics
|
|
||||||
///
|
|
||||||
/// Panics if `index` is out of bounds.
|
|
||||||
pub fn get_mut(&mut self, index: usize) -> &mut T {
|
|
||||||
if index >= self.len {
|
|
||||||
panic!("index OOB in StaticVec");
|
|
||||||
}
|
|
||||||
|
|
||||||
let list = unsafe { mem::transmute::<_, &mut [T; MAX_STATIC_VEC]>(&mut self.list) };
|
|
||||||
|
|
||||||
if self.is_fixed_storage() {
|
|
||||||
list.get_mut(index).unwrap()
|
|
||||||
} else {
|
|
||||||
self.more.get_mut(index).unwrap()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// Get an iterator to entries in the `StaticVec`.
|
|
||||||
pub fn iter(&self) -> impl Iterator<Item = &T> {
|
|
||||||
let list = unsafe { mem::transmute::<_, &[T; MAX_STATIC_VEC]>(&self.list) };
|
|
||||||
|
|
||||||
if self.is_fixed_storage() {
|
|
||||||
list[..self.len].iter()
|
|
||||||
} else {
|
|
||||||
self.more.iter()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// Get a mutable iterator to entries in the `StaticVec`.
|
|
||||||
pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut T> {
|
|
||||||
let list = unsafe { mem::transmute::<_, &mut [T; MAX_STATIC_VEC]>(&mut self.list) };
|
|
||||||
|
|
||||||
if self.is_fixed_storage() {
|
|
||||||
list[..self.len].iter_mut()
|
|
||||||
} else {
|
|
||||||
self.more.iter_mut()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: 'static> StaticVec<T> {
|
|
||||||
/// Get a mutable iterator to entries in the `StaticVec`.
|
|
||||||
pub fn into_iter(mut self) -> Box<dyn Iterator<Item = T>> {
|
|
||||||
if self.is_fixed_storage() {
|
|
||||||
let mut it = FixedStorageIterator {
|
|
||||||
data: unsafe { mem::MaybeUninit::uninit().assume_init() },
|
|
||||||
index: 0,
|
|
||||||
limit: self.len,
|
|
||||||
};
|
|
||||||
|
|
||||||
for x in 0..self.len {
|
|
||||||
it.data[x] = mem::replace(self.list.get_mut(x).unwrap(), MaybeUninit::uninit());
|
|
||||||
}
|
|
||||||
self.len = 0;
|
|
||||||
|
|
||||||
Box::new(it)
|
|
||||||
} else {
|
|
||||||
Box::new(Vec::from(self).into_iter())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// An iterator that takes control of the fixed-size storage of a `StaticVec` and returns its values.
|
|
||||||
struct FixedStorageIterator<T> {
|
|
||||||
data: [MaybeUninit<T>; MAX_STATIC_VEC],
|
|
||||||
index: usize,
|
|
||||||
limit: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Iterator for FixedStorageIterator<T> {
|
|
||||||
type Item = T;
|
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
|
||||||
if self.index >= self.limit {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
self.index += 1;
|
|
||||||
|
|
||||||
let value = mem::replace(
|
|
||||||
self.data.get_mut(self.index - 1).unwrap(),
|
|
||||||
MaybeUninit::uninit(),
|
|
||||||
);
|
|
||||||
|
|
||||||
unsafe { Some(value.assume_init()) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Default> StaticVec<T> {
|
|
||||||
/// Get the item at a particular index, replacing it with the default.
|
|
||||||
///
|
|
||||||
/// # Panics
|
|
||||||
///
|
|
||||||
/// Panics if `index` is out of bounds.
|
|
||||||
pub fn take(&mut self, index: usize) -> T {
|
|
||||||
if index >= self.len {
|
|
||||||
panic!("index OOB in StaticVec");
|
|
||||||
}
|
|
||||||
|
|
||||||
mem::take(if self.is_fixed_storage() {
|
|
||||||
unsafe { mem::transmute(self.list.get_mut(index).unwrap()) }
|
|
||||||
} else {
|
|
||||||
self.more.get_mut(index).unwrap()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: fmt::Debug> fmt::Debug for StaticVec<T> {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
fmt::Debug::fmt(&self.iter().collect::<Vec<_>>(), f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> AsRef<[T]> for StaticVec<T> {
|
|
||||||
fn as_ref(&self) -> &[T] {
|
|
||||||
let list = unsafe { mem::transmute::<_, &[T; MAX_STATIC_VEC]>(&self.list) };
|
|
||||||
|
|
||||||
if self.is_fixed_storage() {
|
|
||||||
&list[..self.len]
|
|
||||||
} else {
|
|
||||||
&self.more[..]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> AsMut<[T]> for StaticVec<T> {
|
|
||||||
fn as_mut(&mut self) -> &mut [T] {
|
|
||||||
let list = unsafe { mem::transmute::<_, &mut [T; MAX_STATIC_VEC]>(&mut self.list) };
|
|
||||||
|
|
||||||
if self.is_fixed_storage() {
|
|
||||||
&mut list[..self.len]
|
|
||||||
} else {
|
|
||||||
&mut self.more[..]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Deref for StaticVec<T> {
|
|
||||||
type Target = [T];
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
self.as_ref()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> DerefMut for StaticVec<T> {
|
|
||||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
|
||||||
self.as_mut()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Index<usize> for StaticVec<T> {
|
|
||||||
type Output = T;
|
|
||||||
|
|
||||||
fn index(&self, index: usize) -> &Self::Output {
|
|
||||||
self.get(index)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> IndexMut<usize> for StaticVec<T> {
|
|
||||||
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
|
|
||||||
self.get_mut(index)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> From<StaticVec<T>> for Vec<T> {
|
|
||||||
fn from(mut value: StaticVec<T>) -> Self {
|
|
||||||
if value.len <= MAX_STATIC_VEC {
|
|
||||||
value.move_fixed_into_vec(value.len);
|
|
||||||
}
|
|
||||||
value.len = 0;
|
|
||||||
|
|
||||||
let mut arr = Self::new();
|
|
||||||
arr.append(&mut value.more);
|
|
||||||
arr
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> From<Vec<T>> for StaticVec<T> {
|
|
||||||
fn from(mut value: Vec<T>) -> Self {
|
|
||||||
let mut arr: Self = Default::default();
|
|
||||||
arr.len = value.len();
|
|
||||||
|
|
||||||
if arr.len <= MAX_STATIC_VEC {
|
|
||||||
for x in (0..arr.len).rev() {
|
|
||||||
arr.set_into_list(x, value.pop().unwrap(), false);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
arr.more = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
arr
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The system immutable string type.
|
/// The system immutable string type.
|
||||||
///
|
///
|
||||||
|
Loading…
x
Reference in New Issue
Block a user