Extract index calculataion into functions.

This commit is contained in:
Stephen Chung
2022-01-13 18:13:27 +08:00
parent 4b4a6c944d
commit 0f4e8848f9
14 changed files with 328 additions and 558 deletions

View File

@@ -1,7 +1,7 @@
//! Types to support chaining operations (i.e. indexing and dotting).
#![cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
use super::{EvalState, GlobalRuntimeState, Target};
use super::{calc_index, EvalState, GlobalRuntimeState, Target};
use crate::ast::{Expr, OpAssignment};
use crate::types::dynamic::Union;
use crate::{Dynamic, Engine, Module, Position, RhaiResult, RhaiResultOf, Scope, StaticVec, ERR};
@@ -771,7 +771,7 @@ impl Engine {
lib: &[&Module],
target: &'t mut Dynamic,
idx: Dynamic,
idx_pos: Position,
pos: Position,
add_if_not_found: bool,
use_indexers: bool,
level: usize,
@@ -788,38 +788,12 @@ impl Engine {
// val_array[idx]
let index = idx
.as_int()
.map_err(|typ| self.make_type_mismatch_err::<crate::INT>(typ, idx_pos))?;
.map_err(|typ| self.make_type_mismatch_err::<crate::INT>(typ, pos))?;
let len = arr.len();
let arr_idx =
calc_index(len, index, true, || ERR::ErrorArrayBounds(len, index, pos))?;
let arr_len = arr.len();
#[cfg(not(feature = "unchecked"))]
let arr_idx = if index < 0 {
// Count from end if negative
arr_len
- index
.checked_abs()
.ok_or_else(|| ERR::ErrorArrayBounds(arr_len, index, idx_pos))
.and_then(|n| {
if n as usize > arr_len {
Err(ERR::ErrorArrayBounds(arr_len, index, idx_pos).into())
} else {
Ok(n as usize)
}
})?
} else {
index as usize
};
#[cfg(feature = "unchecked")]
let arr_idx = if index < 0 {
// Count from end if negative
arr_len - index.abs() as usize
} else {
index as usize
};
arr.get_mut(arr_idx)
.map(Target::from)
.ok_or_else(|| ERR::ErrorArrayBounds(arr_len, index, idx_pos).into())
Ok(arr.get_mut(arr_idx).map(Target::from).unwrap())
}
#[cfg(not(feature = "no_index"))]
@@ -827,39 +801,13 @@ impl Engine {
// val_blob[idx]
let index = idx
.as_int()
.map_err(|typ| self.make_type_mismatch_err::<crate::INT>(typ, idx_pos))?;
.map_err(|typ| self.make_type_mismatch_err::<crate::INT>(typ, pos))?;
let len = arr.len();
let arr_idx =
calc_index(len, index, true, || ERR::ErrorArrayBounds(len, index, pos))?;
let arr_len = arr.len();
let value = arr.get(arr_idx).map(|&v| (v as crate::INT).into()).unwrap();
#[cfg(not(feature = "unchecked"))]
let arr_idx = if index < 0 {
// Count from end if negative
arr_len
- index
.checked_abs()
.ok_or_else(|| ERR::ErrorArrayBounds(arr_len, index, idx_pos))
.and_then(|n| {
if n as usize > arr_len {
Err(ERR::ErrorArrayBounds(arr_len, index, idx_pos).into())
} else {
Ok(n as usize)
}
})?
} else {
index as usize
};
#[cfg(feature = "unchecked")]
let arr_idx = if index < 0 {
// Count from end if negative
arr_len - index.abs() as usize
} else {
index as usize
};
let value = arr
.get(arr_idx)
.map(|&v| (v as crate::INT).into())
.ok_or_else(|| Box::new(ERR::ErrorArrayBounds(arr_len, index, idx_pos)))?;
Ok(Target::BlobByte {
source: target,
value,
@@ -871,7 +819,7 @@ impl Engine {
Dynamic(Union::Map(map, _, _)) => {
// val_map[idx]
let index = idx.read_lock::<crate::ImmutableString>().ok_or_else(|| {
self.make_type_mismatch_err::<crate::ImmutableString>(idx.type_name(), idx_pos)
self.make_type_mismatch_err::<crate::ImmutableString>(idx.type_name(), pos)
})?;
if _add_if_not_found && !map.contains_key(index.as_str()) {
@@ -888,11 +836,6 @@ impl Engine {
Dynamic(Union::Int(value, _, _))
if idx.is::<crate::ExclusiveRange>() || idx.is::<crate::InclusiveRange>() =>
{
#[cfg(not(feature = "only_i32"))]
type BASE = u64;
#[cfg(feature = "only_i32")]
type BASE = u32;
// val_int[range]
const BITS: usize = std::mem::size_of::<crate::INT>() * 8;
@@ -900,40 +843,47 @@ impl Engine {
let start = range.start;
let end = range.end;
if start < 0 || start as usize >= BITS {
return Err(ERR::ErrorBitFieldBounds(BITS, start, idx_pos).into());
} else if end < 0 || end as usize >= BITS {
return Err(ERR::ErrorBitFieldBounds(BITS, end, idx_pos).into());
} else if end <= start {
let start = calc_index(BITS, start, false, || {
ERR::ErrorBitFieldBounds(BITS, start, pos)
})?;
let end = calc_index(BITS, end, false, || {
ERR::ErrorBitFieldBounds(BITS, end, pos)
})?;
if end <= start {
(0, 0)
} else if end as usize == BITS && start == 0 {
} else if end == BITS && start == 0 {
// -1 = all bits set
(0, -1)
} else {
(
start as u8,
// 2^bits - 1
(((2 as BASE).pow((end - start) as u32) - 1) as crate::INT) << start,
(((2 as crate::UINT).pow((end - start) as u32) - 1) as crate::INT)
<< start,
)
}
} else if let Some(range) = idx.read_lock::<crate::InclusiveRange>() {
let start = *range.start();
let end = *range.end();
if start < 0 || start as usize >= BITS {
return Err(ERR::ErrorBitFieldBounds(BITS, start, idx_pos).into());
} else if end < 0 || end as usize >= BITS {
return Err(ERR::ErrorBitFieldBounds(BITS, end, idx_pos).into());
} else if end < start {
let start = calc_index(BITS, start, false, || {
ERR::ErrorBitFieldBounds(BITS, start, pos)
})?;
let end = calc_index(BITS, end, false, || {
ERR::ErrorBitFieldBounds(BITS, end, pos)
})?;
if end < start {
(0, 0)
} else if end as usize == BITS - 1 && start == 0 {
} else if end == BITS - 1 && start == 0 {
// -1 = all bits set
(0, -1)
} else {
(
start as u8,
// 2^bits - 1
(((2 as BASE).pow((end - start + 1) as u32) - 1) as crate::INT)
(((2 as crate::UINT).pow((end - start + 1) as u32) - 1) as crate::INT)
<< start,
)
}
@@ -956,39 +906,20 @@ impl Engine {
// val_int[idx]
let index = idx
.as_int()
.map_err(|typ| self.make_type_mismatch_err::<crate::INT>(typ, idx_pos))?;
.map_err(|typ| self.make_type_mismatch_err::<crate::INT>(typ, pos))?;
const BITS: usize = std::mem::size_of::<crate::INT>() * 8;
let (bit_value, offset) = if index >= 0 {
let offset = index as usize;
(
if offset >= BITS {
return Err(ERR::ErrorBitFieldBounds(BITS, index, idx_pos).into());
} else {
(*value & (1 << offset)) != 0
},
offset as u8,
)
} else if let Some(abs_index) = index.checked_abs() {
let offset = abs_index as usize;
(
// Count from end if negative
if offset > BITS {
return Err(ERR::ErrorBitFieldBounds(BITS, index, idx_pos).into());
} else {
(*value & (1 << (BITS - offset))) != 0
},
offset as u8,
)
} else {
return Err(ERR::ErrorBitFieldBounds(BITS, index, idx_pos).into());
};
let bit = calc_index(BITS, index, true, || {
ERR::ErrorBitFieldBounds(BITS, index, pos)
})?;
let bit_value = (*value & (1 << bit)) != 0;
Ok(Target::Bit {
source: target,
value: bit_value.into(),
bit: offset,
bit: bit as u8,
})
}
@@ -997,14 +928,14 @@ impl Engine {
// val_string[idx]
let index = idx
.as_int()
.map_err(|typ| self.make_type_mismatch_err::<crate::INT>(typ, idx_pos))?;
.map_err(|typ| self.make_type_mismatch_err::<crate::INT>(typ, pos))?;
let (ch, offset) = if index >= 0 {
let offset = index as usize;
(
s.chars().nth(offset).ok_or_else(|| {
let chars_len = s.chars().count();
ERR::ErrorStringBounds(chars_len, index, idx_pos)
ERR::ErrorStringBounds(chars_len, index, pos)
})?,
offset,
)
@@ -1014,13 +945,13 @@ impl Engine {
// Count from end if negative
s.chars().rev().nth(offset - 1).ok_or_else(|| {
let chars_len = s.chars().count();
ERR::ErrorStringBounds(chars_len, index, idx_pos)
ERR::ErrorStringBounds(chars_len, index, pos)
})?,
offset,
)
} else {
let chars_len = s.chars().count();
return Err(ERR::ErrorStringBounds(chars_len, index, idx_pos).into());
return Err(ERR::ErrorStringBounds(chars_len, index, pos).into());
};
Ok(Target::StringChar {
@@ -1033,8 +964,6 @@ impl Engine {
_ if use_indexers => {
let args = &mut [target, &mut idx];
let hash_get = crate::ast::FnCallHashes::from_native(global.hash_idx_get());
let idx_pos = Position::NONE;
self.exec_fn_call(
global,
state,
@@ -1044,7 +973,7 @@ impl Engine {
args,
true,
true,
idx_pos,
Position::NONE,
None,
level,
)

View File

@@ -12,4 +12,4 @@ pub use chaining::{ChainArgument, ChainType};
pub use eval_context::EvalContext;
pub use eval_state::EvalState;
pub use global_state::GlobalRuntimeState;
pub use target::Target;
pub use target::{calc_index, calc_offset_len, Target};

View File

@@ -1,11 +1,77 @@
//! Type to hold a mutable reference to the target of an evaluation.
use crate::types::dynamic::Variant;
use crate::{Dynamic, RhaiResultOf};
use crate::{Dynamic, EvalAltResult, RhaiResultOf, INT};
use std::ops::{Deref, DerefMut};
#[cfg(feature = "no_std")]
use std::prelude::v1::*;
// Calculate an offset+len pair given an actual length of the underlying array.
//
// Negative starting positions count from the end.
//
// Values going over bounds are limited to the actual length.
#[inline(always)]
pub fn calc_offset_len(length: usize, start: INT, len: INT) -> (usize, usize) {
let start = if start < 0 {
start.checked_abs().map_or(0, |positive_start| {
length - usize::min(positive_start as usize, length)
})
} else if start as usize >= length {
return (length, 0);
} else {
start as usize
};
let len = if len <= 0 {
0
} else if len as usize > length - start {
length - start
} else {
len as usize
};
(start, len)
}
// Calculate an offset+len pair given an actual length of the underlying array.
//
// Negative starting positions count from the end.
//
// Values going over bounds call the provided closure to create an error.
#[inline(always)]
pub fn calc_index(
length: usize,
start: INT,
negative_count_from_end: bool,
err: impl Fn() -> EvalAltResult,
) -> RhaiResultOf<usize> {
if start < 0 {
if negative_count_from_end {
// Count from end if negative
#[cfg(not(feature = "unchecked"))]
return start
.checked_abs()
.ok_or_else(|| err().into())
.and_then(|positive_start| {
if (positive_start as usize) > length {
Err(err().into())
} else {
Ok(length - (positive_start as usize))
}
});
#[cfg(feature = "unchecked")]
return Ok(actual - (start.abs() as usize));
} else {
Err(err().into())
}
} else if start as usize >= length {
Err(err().into())
} else {
Ok(start as usize)
}
}
/// A type that encapsulates a mutation target for an expression with side effects.
#[derive(Debug)]
pub enum Target<'a> {