Implement bit-fields.
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
use crate::dynamic::Variant;
|
||||
use crate::{def_package, EvalAltResult, INT};
|
||||
use crate::{def_package, EvalAltResult, Position, INT};
|
||||
use std::ops::Range;
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
@@ -29,9 +29,9 @@ where
|
||||
Default::default(),
|
||||
Box::new(EvalAltResult::ErrorArithmetic(
|
||||
"step value cannot be zero".to_string(),
|
||||
crate::Position::NONE,
|
||||
Position::NONE,
|
||||
)),
|
||||
crate::Position::NONE,
|
||||
Position::NONE,
|
||||
)
|
||||
.into();
|
||||
}
|
||||
@@ -126,6 +126,68 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
// Register range function with step
|
||||
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
|
||||
struct BitRange(INT, INT, usize);
|
||||
|
||||
const BITS: usize = std::mem::size_of::<INT>() * 8;
|
||||
|
||||
impl BitRange {
|
||||
pub fn new(value: INT, from: INT, len: INT) -> Result<Self, Box<EvalAltResult>> {
|
||||
let from = if from >= 0 {
|
||||
let offset = from as usize;
|
||||
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
if offset >= BITS {
|
||||
return EvalAltResult::ErrorBitFieldBounds(BITS, from, Position::NONE).into();
|
||||
}
|
||||
offset
|
||||
} else {
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
if let Some(abs_from) = from.checked_abs() {
|
||||
if (abs_from as usize) > BITS {
|
||||
return EvalAltResult::ErrorBitFieldBounds(BITS, from, Position::NONE).into();
|
||||
}
|
||||
BITS - (abs_from as usize)
|
||||
} else {
|
||||
return EvalAltResult::ErrorBitFieldBounds(BITS, from, Position::NONE).into();
|
||||
}
|
||||
|
||||
#[cfg(feature = "unchecked")]
|
||||
{
|
||||
BITS - (from.abs() as usize)
|
||||
}
|
||||
};
|
||||
|
||||
let len = if len < 0 {
|
||||
0
|
||||
} else if from + (len as usize) > BITS {
|
||||
BITS - from
|
||||
} else {
|
||||
len as usize
|
||||
};
|
||||
|
||||
Ok(Self(value, 1 << from, len))
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for BitRange {
|
||||
type Item = bool;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let Self(value, mask, len) = *self;
|
||||
|
||||
if len == 0 {
|
||||
None
|
||||
} else {
|
||||
let r = (value & mask) != 0;
|
||||
self.1 <<= 1;
|
||||
self.2 -= 1;
|
||||
Some(r)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! reg_range {
|
||||
($lib:ident | $x:expr => $( $y:ty ),*) => {
|
||||
$(
|
||||
@@ -193,8 +255,8 @@ def_package!(crate:BasicIteratorPackage:"Basic range iterators.", lib, {
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
if step == 0.0 {
|
||||
return EvalAltResult::ErrorInFunctionCall("range".to_string(), "".to_string(),
|
||||
Box::new(EvalAltResult::ErrorArithmetic("step value cannot be zero".to_string(), crate::Position::NONE)),
|
||||
crate::Position::NONE,
|
||||
Box::new(EvalAltResult::ErrorArithmetic("step value cannot be zero".to_string(), Position::NONE)),
|
||||
Position::NONE,
|
||||
).into();
|
||||
}
|
||||
|
||||
@@ -255,8 +317,8 @@ def_package!(crate:BasicIteratorPackage:"Basic range iterators.", lib, {
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
if step.is_zero() {
|
||||
return EvalAltResult::ErrorInFunctionCall("range".to_string(), "".to_string(),
|
||||
Box::new(EvalAltResult::ErrorArithmetic("step value cannot be zero".to_string(), crate::Position::NONE)),
|
||||
crate::Position::NONE,
|
||||
Box::new(EvalAltResult::ErrorArithmetic("step value cannot be zero".to_string(), Position::NONE)),
|
||||
Position::NONE,
|
||||
).into();
|
||||
}
|
||||
|
||||
@@ -304,4 +366,18 @@ def_package!(crate:BasicIteratorPackage:"Basic range iterators.", lib, {
|
||||
#[cfg(feature = "metadata")]
|
||||
lib.update_fn_metadata(_hash, &["from: Decimal", "to: Decimal", "step: Decimal", "Iterator<Item=Decimal>"]);
|
||||
}
|
||||
|
||||
lib.set_iterator::<BitRange>();
|
||||
|
||||
let _hash = lib.set_native_fn("bits", |value, from, len| BitRange::new(value, from, len));
|
||||
#[cfg(feature = "metadata")]
|
||||
lib.update_fn_metadata(_hash, &["value: INT", "from: Decimal", "len: Decimal", "Iterator<Item=bool>"]);
|
||||
|
||||
let _hash = lib.set_native_fn("bits", |value, from| BitRange::new(value, from, INT::MAX));
|
||||
#[cfg(feature = "metadata")]
|
||||
lib.update_fn_metadata(_hash, &["value: INT", "from: Decimal", "Iterator<Item=bool>"]);
|
||||
|
||||
let _hash = lib.set_native_fn("bits", |value| BitRange::new(value, 0, INT::MAX));
|
||||
#[cfg(feature = "metadata")]
|
||||
lib.update_fn_metadata(_hash, &["value: INT", "Iterator<Item=bool>"]);
|
||||
});
|
||||
|
@@ -1,7 +1,7 @@
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
use crate::def_package;
|
||||
use crate::plugin::*;
|
||||
use crate::{def_package, EvalAltResult, INT};
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
|
||||
@@ -59,6 +59,8 @@ def_package!(crate:LogicPackage:"Logical operators.", lib, {
|
||||
}
|
||||
|
||||
set_exported_fn!(lib, "!", not);
|
||||
|
||||
combine_with_exported_module!(lib, "bit_field", bit_field_functions);
|
||||
});
|
||||
|
||||
// Logic operators
|
||||
@@ -87,8 +89,6 @@ gen_cmp_functions!(float => f64);
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
#[export_module]
|
||||
mod f32_functions {
|
||||
use crate::INT;
|
||||
|
||||
#[rhai_fn(name = "==")]
|
||||
pub fn eq_if(x: INT, y: f32) -> bool {
|
||||
(x as f32) == (y as f32)
|
||||
@@ -142,8 +142,6 @@ mod f32_functions {
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
#[export_module]
|
||||
mod f64_functions {
|
||||
use crate::INT;
|
||||
|
||||
#[rhai_fn(name = "==")]
|
||||
pub fn eq_if(x: INT, y: f64) -> bool {
|
||||
(x as f64) == (y as f64)
|
||||
@@ -193,3 +191,159 @@ mod f64_functions {
|
||||
(x as f64) <= (y as f64)
|
||||
}
|
||||
}
|
||||
|
||||
#[export_module]
|
||||
mod bit_field_functions {
|
||||
const BITS: usize = std::mem::size_of::<INT>() * 8;
|
||||
|
||||
#[rhai_fn(return_raw)]
|
||||
pub fn get_bit(value: INT, index: INT) -> Result<bool, Box<EvalAltResult>> {
|
||||
if index >= 0 {
|
||||
let offset = index as usize;
|
||||
|
||||
if offset >= BITS {
|
||||
EvalAltResult::ErrorBitFieldBounds(BITS, index, Position::NONE).into()
|
||||
} else {
|
||||
Ok((value & (1 << offset)) != 0)
|
||||
}
|
||||
} else if let Some(abs_index) = index.checked_abs() {
|
||||
let offset = abs_index as usize;
|
||||
|
||||
// Count from end if negative
|
||||
if offset > BITS {
|
||||
EvalAltResult::ErrorBitFieldBounds(BITS, index, Position::NONE).into()
|
||||
} else {
|
||||
Ok((value & (1 << (BITS - offset))) != 0)
|
||||
}
|
||||
} else {
|
||||
EvalAltResult::ErrorBitFieldBounds(BITS, index, Position::NONE).into()
|
||||
}
|
||||
}
|
||||
#[rhai_fn(return_raw)]
|
||||
pub fn set_bit(value: &mut INT, index: INT, new_value: bool) -> Result<(), Box<EvalAltResult>> {
|
||||
if index >= 0 {
|
||||
let offset = index as usize;
|
||||
|
||||
if offset >= BITS {
|
||||
EvalAltResult::ErrorBitFieldBounds(BITS, index, Position::NONE).into()
|
||||
} else {
|
||||
let mask = 1 << offset;
|
||||
if new_value {
|
||||
*value |= mask;
|
||||
} else {
|
||||
*value &= !mask;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
} else if let Some(abs_index) = index.checked_abs() {
|
||||
let offset = abs_index as usize;
|
||||
|
||||
// Count from end if negative
|
||||
if offset > BITS {
|
||||
EvalAltResult::ErrorBitFieldBounds(BITS, index, Position::NONE).into()
|
||||
} else {
|
||||
let mask = 1 << offset;
|
||||
if new_value {
|
||||
*value |= mask;
|
||||
} else {
|
||||
*value &= !mask;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
} else {
|
||||
EvalAltResult::ErrorBitFieldBounds(BITS, index, Position::NONE).into()
|
||||
}
|
||||
}
|
||||
#[rhai_fn(return_raw)]
|
||||
pub fn get_bits(value: INT, index: INT, bits: INT) -> Result<INT, Box<EvalAltResult>> {
|
||||
if bits < 1 {
|
||||
return Ok(0);
|
||||
}
|
||||
|
||||
let offset = if index >= 0 {
|
||||
let offset = index as usize;
|
||||
|
||||
if offset >= BITS {
|
||||
return EvalAltResult::ErrorBitFieldBounds(BITS, index, Position::NONE).into();
|
||||
}
|
||||
|
||||
offset
|
||||
} else if let Some(abs_index) = index.checked_abs() {
|
||||
let offset = abs_index as usize;
|
||||
|
||||
// Count from end if negative
|
||||
if offset > BITS {
|
||||
return EvalAltResult::ErrorBitFieldBounds(BITS, index, Position::NONE).into();
|
||||
}
|
||||
BITS - offset
|
||||
} else {
|
||||
return EvalAltResult::ErrorBitFieldBounds(BITS, index, Position::NONE).into();
|
||||
};
|
||||
|
||||
let bits = if offset + bits as usize > BITS {
|
||||
BITS - offset
|
||||
} else {
|
||||
bits as usize
|
||||
};
|
||||
|
||||
let mut base = 1;
|
||||
let mut mask = 0;
|
||||
|
||||
for _ in 0..bits {
|
||||
mask |= base;
|
||||
base <<= 1;
|
||||
}
|
||||
|
||||
Ok(((value & (mask << index)) >> index) & mask)
|
||||
}
|
||||
#[rhai_fn(return_raw)]
|
||||
pub fn set_bits(
|
||||
value: &mut INT,
|
||||
index: INT,
|
||||
bits: INT,
|
||||
new_value: INT,
|
||||
) -> Result<(), Box<EvalAltResult>> {
|
||||
if bits < 1 {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let offset = if index >= 0 {
|
||||
let offset = index as usize;
|
||||
|
||||
if offset >= BITS {
|
||||
return EvalAltResult::ErrorBitFieldBounds(BITS, index, Position::NONE).into();
|
||||
}
|
||||
|
||||
offset
|
||||
} else if let Some(abs_index) = index.checked_abs() {
|
||||
let offset = abs_index as usize;
|
||||
|
||||
// Count from end if negative
|
||||
if offset > BITS {
|
||||
return EvalAltResult::ErrorBitFieldBounds(BITS, index, Position::NONE).into();
|
||||
}
|
||||
BITS - offset
|
||||
} else {
|
||||
return EvalAltResult::ErrorBitFieldBounds(BITS, index, Position::NONE).into();
|
||||
};
|
||||
|
||||
let bits = if offset + bits as usize > BITS {
|
||||
BITS - offset
|
||||
} else {
|
||||
bits as usize
|
||||
};
|
||||
|
||||
let mut base = 1;
|
||||
let mut mask = 0;
|
||||
|
||||
for _ in 0..bits {
|
||||
mask |= base;
|
||||
base <<= 1;
|
||||
}
|
||||
|
||||
*value &= !(mask << index);
|
||||
*value |= (new_value & mask) << index;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user