use core::{f32, i32, i64, u32, u64};
use nan_preserving_float::{F32, F64};
use types::ValueType;
use TrapKind;
#[derive(Debug)]
pub enum Error {
InvalidLittleEndianBuffer,
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum RuntimeValue {
I32(i32),
I64(i64),
F32(F32),
F64(F64),
}
pub trait FromRuntimeValue
where
Self: Sized,
{
fn from_runtime_value(val: RuntimeValue) -> Option<Self>;
}
pub trait WrapInto<T> {
fn wrap_into(self) -> T;
}
pub trait TryTruncateInto<T, E> {
fn try_truncate_into(self) -> Result<T, E>;
}
pub trait ExtendInto<T> {
fn extend_into(self) -> T;
}
pub trait TransmuteInto<T> {
fn transmute_into(self) -> T;
}
pub trait LittleEndianConvert
where
Self: Sized,
{
fn into_little_endian(self, buffer: &mut [u8]);
fn from_little_endian(buffer: &[u8]) -> Result<Self, Error>;
}
pub trait ArithmeticOps<T> {
fn add(self, other: T) -> T;
fn sub(self, other: T) -> T;
fn mul(self, other: T) -> T;
fn div(self, other: T) -> Result<T, TrapKind>;
}
pub trait Integer<T>: ArithmeticOps<T> {
fn leading_zeros(self) -> T;
fn trailing_zeros(self) -> T;
fn count_ones(self) -> T;
fn rotl(self, other: T) -> T;
fn rotr(self, other: T) -> T;
fn rem(self, other: T) -> Result<T, TrapKind>;
}
pub trait Float<T>: ArithmeticOps<T> {
fn abs(self) -> T;
fn floor(self) -> T;
fn ceil(self) -> T;
fn trunc(self) -> T;
fn round(self) -> T;
fn nearest(self) -> T;
fn sqrt(self) -> T;
fn min(self, other: T) -> T;
fn max(self, other: T) -> T;
fn copysign(self, other: T) -> T;
}
impl RuntimeValue {
pub fn default(value_type: ValueType) -> Self {
match value_type {
ValueType::I32 => RuntimeValue::I32(0),
ValueType::I64 => RuntimeValue::I64(0),
ValueType::F32 => RuntimeValue::F32(0f32.into()),
ValueType::F64 => RuntimeValue::F64(0f64.into()),
}
}
pub fn decode_f32(val: u32) -> Self {
RuntimeValue::F32(F32::from_bits(val))
}
pub fn decode_f64(val: u64) -> Self {
RuntimeValue::F64(F64::from_bits(val))
}
pub fn value_type(&self) -> ValueType {
match *self {
RuntimeValue::I32(_) => ValueType::I32,
RuntimeValue::I64(_) => ValueType::I64,
RuntimeValue::F32(_) => ValueType::F32,
RuntimeValue::F64(_) => ValueType::F64,
}
}
pub fn try_into<T: FromRuntimeValue>(self) -> Option<T> {
FromRuntimeValue::from_runtime_value(self)
}
}
impl From<i8> for RuntimeValue {
fn from(val: i8) -> Self {
RuntimeValue::I32(val as i32)
}
}
impl From<i16> for RuntimeValue {
fn from(val: i16) -> Self {
RuntimeValue::I32(val as i32)
}
}
impl From<i32> for RuntimeValue {
fn from(val: i32) -> Self {
RuntimeValue::I32(val)
}
}
impl From<i64> for RuntimeValue {
fn from(val: i64) -> Self {
RuntimeValue::I64(val)
}
}
impl From<u8> for RuntimeValue {
fn from(val: u8) -> Self {
RuntimeValue::I32(val as i32)
}
}
impl From<u16> for RuntimeValue {
fn from(val: u16) -> Self {
RuntimeValue::I32(val as i32)
}
}
impl From<u32> for RuntimeValue {
fn from(val: u32) -> Self {
RuntimeValue::I32(val.transmute_into())
}
}
impl From<u64> for RuntimeValue {
fn from(val: u64) -> Self {
RuntimeValue::I64(val.transmute_into())
}
}
impl From<F32> for RuntimeValue {
fn from(val: F32) -> Self {
RuntimeValue::F32(val)
}
}
impl From<F64> for RuntimeValue {
fn from(val: F64) -> Self {
RuntimeValue::F64(val)
}
}
macro_rules! impl_from_runtime_value {
($expected_rt_ty: ident, $into: ty) => {
impl FromRuntimeValue for $into {
fn from_runtime_value(val: RuntimeValue) -> Option<Self> {
match val {
RuntimeValue::$expected_rt_ty(val) => Some(val.transmute_into()),
_ => None,
}
}
}
};
}
impl FromRuntimeValue for bool {
fn from_runtime_value(val: RuntimeValue) -> Option<Self> {
match val {
RuntimeValue::I32(val) => Some(val != 0),
_ => None,
}
}
}
impl FromRuntimeValue for i8 {
fn from_runtime_value(val: RuntimeValue) -> Option<Self> {
let min = i8::min_value() as i32;
let max = i8::max_value() as i32;
match val {
RuntimeValue::I32(val) if min <= val && val <= max => Some(val as i8),
_ => None,
}
}
}
impl FromRuntimeValue for i16 {
fn from_runtime_value(val: RuntimeValue) -> Option<Self> {
let min = i16::min_value() as i32;
let max = i16::max_value() as i32;
match val {
RuntimeValue::I32(val) if min <= val && val <= max => Some(val as i16),
_ => None,
}
}
}
impl FromRuntimeValue for u8 {
fn from_runtime_value(val: RuntimeValue) -> Option<Self> {
let min = u8::min_value() as i32;
let max = u8::max_value() as i32;
match val {
RuntimeValue::I32(val) if min <= val && val <= max => Some(val as u8),
_ => None,
}
}
}
impl FromRuntimeValue for u16 {
fn from_runtime_value(val: RuntimeValue) -> Option<Self> {
let min = u16::min_value() as i32;
let max = u16::max_value() as i32;
match val {
RuntimeValue::I32(val) if min <= val && val <= max => Some(val as u16),
_ => None,
}
}
}
impl_from_runtime_value!(I32, i32);
impl_from_runtime_value!(I64, i64);
impl_from_runtime_value!(F32, F32);
impl_from_runtime_value!(F64, F64);
impl_from_runtime_value!(I32, u32);
impl_from_runtime_value!(I64, u64);
macro_rules! impl_wrap_into {
($from:ident, $into:ident) => {
impl WrapInto<$into> for $from {
fn wrap_into(self) -> $into {
self as $into
}
}
};
($from:ident, $intermediate:ident, $into:ident) => {
impl WrapInto<$into> for $from {
fn wrap_into(self) -> $into {
$into::from(self as $intermediate)
}
}
};
}
impl_wrap_into!(i32, i8);
impl_wrap_into!(i32, i16);
impl_wrap_into!(i64, i8);
impl_wrap_into!(i64, i16);
impl_wrap_into!(i64, i32);
impl_wrap_into!(i64, f32, F32);
impl_wrap_into!(u64, f32, F32);
impl_wrap_into!(f64, f32);
impl WrapInto<F32> for F64 {
fn wrap_into(self) -> F32 {
(f64::from(self) as f32).into()
}
}
macro_rules! impl_try_truncate_into {
(@primitive $from: ident, $into: ident, $to_primitive:path) => {
impl TryTruncateInto<$into, TrapKind> for $from {
fn try_truncate_into(self) -> Result<$into, TrapKind> {
num_rational::BigRational::from_float(self)
.map(|val| val.to_integer())
.and_then(|val| $to_primitive(&val))
.ok_or(TrapKind::InvalidConversionToInt)
}
}
};
(@wrapped $from:ident, $intermediate:ident, $into:ident) => {
impl TryTruncateInto<$into, TrapKind> for $from {
fn try_truncate_into(self) -> Result<$into, TrapKind> {
$intermediate::from(self).try_truncate_into()
}
}
};
}
impl_try_truncate_into!(@primitive f32, i32, num_traits::cast::ToPrimitive::to_i32);
impl_try_truncate_into!(@primitive f32, i64, num_traits::cast::ToPrimitive::to_i64);
impl_try_truncate_into!(@primitive f64, i32, num_traits::cast::ToPrimitive::to_i32);
impl_try_truncate_into!(@primitive f64, i64, num_traits::cast::ToPrimitive::to_i64);
impl_try_truncate_into!(@primitive f32, u32, num_traits::cast::ToPrimitive::to_u32);
impl_try_truncate_into!(@primitive f32, u64, num_traits::cast::ToPrimitive::to_u64);
impl_try_truncate_into!(@primitive f64, u32, num_traits::cast::ToPrimitive::to_u32);
impl_try_truncate_into!(@primitive f64, u64, num_traits::cast::ToPrimitive::to_u64);
impl_try_truncate_into!(@wrapped F32, f32, i32);
impl_try_truncate_into!(@wrapped F32, f32, i64);
impl_try_truncate_into!(@wrapped F64, f64, i32);
impl_try_truncate_into!(@wrapped F64, f64, i64);
impl_try_truncate_into!(@wrapped F32, f32, u32);
impl_try_truncate_into!(@wrapped F32, f32, u64);
impl_try_truncate_into!(@wrapped F64, f64, u32);
impl_try_truncate_into!(@wrapped F64, f64, u64);
macro_rules! impl_extend_into {
($from:ident, $into:ident) => {
impl ExtendInto<$into> for $from {
fn extend_into(self) -> $into {
self as $into
}
}
};
($from:ident, $intermediate:ident, $into:ident) => {
impl ExtendInto<$into> for $from {
fn extend_into(self) -> $into {
$into::from(self as $intermediate)
}
}
};
}
impl_extend_into!(i8, i32);
impl_extend_into!(u8, i32);
impl_extend_into!(i16, i32);
impl_extend_into!(u16, i32);
impl_extend_into!(i8, i64);
impl_extend_into!(u8, i64);
impl_extend_into!(i16, i64);
impl_extend_into!(u16, i64);
impl_extend_into!(i32, i64);
impl_extend_into!(u32, i64);
impl_extend_into!(u32, u64);
impl_extend_into!(i32, f32);
impl_extend_into!(i32, f64);
impl_extend_into!(u32, f32);
impl_extend_into!(u32, f64);
impl_extend_into!(i64, f64);
impl_extend_into!(u64, f64);
impl_extend_into!(f32, f64);
impl_extend_into!(i32, f32, F32);
impl_extend_into!(i32, f64, F64);
impl_extend_into!(u32, f32, F32);
impl_extend_into!(u32, f64, F64);
impl_extend_into!(i64, f64, F64);
impl_extend_into!(u64, f64, F64);
impl_extend_into!(f32, f64, F64);
impl ExtendInto<F64> for F32 {
fn extend_into(self) -> F64 {
(f32::from(self) as f64).into()
}
}
macro_rules! impl_transmute_into_self {
($type: ident) => {
impl TransmuteInto<$type> for $type {
fn transmute_into(self) -> $type {
self
}
}
};
}
impl_transmute_into_self!(i32);
impl_transmute_into_self!(i64);
impl_transmute_into_self!(f32);
impl_transmute_into_self!(f64);
impl_transmute_into_self!(F32);
impl_transmute_into_self!(F64);
macro_rules! impl_transmute_into_as {
($from: ident, $into: ident) => {
impl TransmuteInto<$into> for $from {
fn transmute_into(self) -> $into {
self as $into
}
}
};
}
impl_transmute_into_as!(i8, u8);
impl_transmute_into_as!(i32, u32);
impl_transmute_into_as!(i64, u64);
macro_rules! impl_transmute_into_npf {
($npf:ident, $float:ident, $signed:ident, $unsigned:ident) => {
impl TransmuteInto<$float> for $npf {
fn transmute_into(self) -> $float {
self.into()
}
}
impl TransmuteInto<$npf> for $float {
fn transmute_into(self) -> $npf {
self.into()
}
}
impl TransmuteInto<$signed> for $npf {
fn transmute_into(self) -> $signed {
self.to_bits() as _
}
}
impl TransmuteInto<$unsigned> for $npf {
fn transmute_into(self) -> $unsigned {
self.to_bits()
}
}
impl TransmuteInto<$npf> for $signed {
fn transmute_into(self) -> $npf {
$npf::from_bits(self as _)
}
}
impl TransmuteInto<$npf> for $unsigned {
fn transmute_into(self) -> $npf {
$npf::from_bits(self)
}
}
};
}
impl_transmute_into_npf!(F32, f32, i32, u32);
impl_transmute_into_npf!(F64, f64, i64, u64);
impl TransmuteInto<i32> for f32 {
fn transmute_into(self) -> i32 {
self.to_bits() as i32
}
}
impl TransmuteInto<i64> for f64 {
fn transmute_into(self) -> i64 {
self.to_bits() as i64
}
}
impl TransmuteInto<f32> for i32 {
fn transmute_into(self) -> f32 {
f32::from_bits(self as u32)
}
}
impl TransmuteInto<f64> for i64 {
fn transmute_into(self) -> f64 {
f64::from_bits(self as u64)
}
}
impl TransmuteInto<i32> for u32 {
fn transmute_into(self) -> i32 {
self as _
}
}
impl TransmuteInto<i64> for u64 {
fn transmute_into(self) -> i64 {
self as _
}
}
impl LittleEndianConvert for i8 {
fn into_little_endian(self, buffer: &mut [u8]) {
buffer[0] = self as u8;
}
fn from_little_endian(buffer: &[u8]) -> Result<Self, Error> {
buffer
.get(0)
.map(|v| *v as i8)
.ok_or_else(|| Error::InvalidLittleEndianBuffer)
}
}
impl LittleEndianConvert for u8 {
fn into_little_endian(self, buffer: &mut [u8]) {
buffer[0] = self;
}
fn from_little_endian(buffer: &[u8]) -> Result<Self, Error> {
buffer
.get(0)
.cloned()
.ok_or_else(|| Error::InvalidLittleEndianBuffer)
}
}
impl LittleEndianConvert for i16 {
fn into_little_endian(self, buffer: &mut [u8]) {
buffer.copy_from_slice(&self.to_le_bytes());
}
fn from_little_endian(buffer: &[u8]) -> Result<Self, Error> {
let mut res = [0u8; 2];
buffer
.get(0..2)
.map(|s| {
res.copy_from_slice(s);
Self::from_le_bytes(res)
})
.ok_or_else(|| Error::InvalidLittleEndianBuffer)
}
}
impl LittleEndianConvert for u16 {
fn into_little_endian(self, buffer: &mut [u8]) {
buffer.copy_from_slice(&self.to_le_bytes());
}
fn from_little_endian(buffer: &[u8]) -> Result<Self, Error> {
let mut res = [0u8; 2];
buffer
.get(0..2)
.map(|s| {
res.copy_from_slice(s);
Self::from_le_bytes(res)
})
.ok_or_else(|| Error::InvalidLittleEndianBuffer)
}
}
impl LittleEndianConvert for i32 {
fn into_little_endian(self, buffer: &mut [u8]) {
buffer.copy_from_slice(&self.to_le_bytes());
}
fn from_little_endian(buffer: &[u8]) -> Result<Self, Error> {
let mut res = [0u8; 4];
buffer
.get(0..4)
.map(|s| {
res.copy_from_slice(s);
Self::from_le_bytes(res)
})
.ok_or_else(|| Error::InvalidLittleEndianBuffer)
}
}
impl LittleEndianConvert for u32 {
fn into_little_endian(self, buffer: &mut [u8]) {
buffer.copy_from_slice(&self.to_le_bytes());
}
fn from_little_endian(buffer: &[u8]) -> Result<Self, Error> {
let mut res = [0u8; 4];
buffer
.get(0..4)
.map(|s| {
res.copy_from_slice(s);
Self::from_le_bytes(res)
})
.ok_or_else(|| Error::InvalidLittleEndianBuffer)
}
}
impl LittleEndianConvert for i64 {
fn into_little_endian(self, buffer: &mut [u8]) {
buffer.copy_from_slice(&self.to_le_bytes());
}
fn from_little_endian(buffer: &[u8]) -> Result<Self, Error> {
let mut res = [0u8; 8];
buffer
.get(0..8)
.map(|s| {
res.copy_from_slice(s);
Self::from_le_bytes(res)
})
.ok_or_else(|| Error::InvalidLittleEndianBuffer)
}
}
impl LittleEndianConvert for f32 {
fn into_little_endian(self, buffer: &mut [u8]) {
buffer.copy_from_slice(&self.to_bits().to_le_bytes());
}
fn from_little_endian(buffer: &[u8]) -> Result<Self, Error> {
let mut res = [0u8; 4];
buffer
.get(0..4)
.map(|s| {
res.copy_from_slice(s);
Self::from_bits(u32::from_le_bytes(res))
})
.ok_or_else(|| Error::InvalidLittleEndianBuffer)
}
}
impl LittleEndianConvert for f64 {
fn into_little_endian(self, buffer: &mut [u8]) {
buffer.copy_from_slice(&self.to_bits().to_le_bytes());
}
fn from_little_endian(buffer: &[u8]) -> Result<Self, Error> {
let mut res = [0u8; 8];
buffer
.get(0..8)
.map(|s| {
res.copy_from_slice(s);
Self::from_bits(u64::from_le_bytes(res))
})
.ok_or_else(|| Error::InvalidLittleEndianBuffer)
}
}
impl LittleEndianConvert for F32 {
fn into_little_endian(self, buffer: &mut [u8]) {
(self.to_bits() as i32).into_little_endian(buffer)
}
fn from_little_endian(buffer: &[u8]) -> Result<Self, Error> {
i32::from_little_endian(buffer).map(|val| Self::from_bits(val as _))
}
}
impl LittleEndianConvert for F64 {
fn into_little_endian(self, buffer: &mut [u8]) {
(self.to_bits() as i64).into_little_endian(buffer)
}
fn from_little_endian(buffer: &[u8]) -> Result<Self, Error> {
i64::from_little_endian(buffer).map(|val| Self::from_bits(val as _))
}
}
macro_rules! impl_integer_arithmetic_ops {
($type: ident) => {
impl ArithmeticOps<$type> for $type {
fn add(self, other: $type) -> $type {
self.wrapping_add(other)
}
fn sub(self, other: $type) -> $type {
self.wrapping_sub(other)
}
fn mul(self, other: $type) -> $type {
self.wrapping_mul(other)
}
fn div(self, other: $type) -> Result<$type, TrapKind> {
if other == 0 {
Err(TrapKind::DivisionByZero)
} else {
let (result, overflow) = self.overflowing_div(other);
if overflow {
Err(TrapKind::InvalidConversionToInt)
} else {
Ok(result)
}
}
}
}
};
}
impl_integer_arithmetic_ops!(i32);
impl_integer_arithmetic_ops!(u32);
impl_integer_arithmetic_ops!(i64);
impl_integer_arithmetic_ops!(u64);
macro_rules! impl_float_arithmetic_ops {
($type: ident) => {
impl ArithmeticOps<$type> for $type {
fn add(self, other: $type) -> $type {
self + other
}
fn sub(self, other: $type) -> $type {
self - other
}
fn mul(self, other: $type) -> $type {
self * other
}
fn div(self, other: $type) -> Result<$type, TrapKind> {
Ok(self / other)
}
}
};
}
impl_float_arithmetic_ops!(f32);
impl_float_arithmetic_ops!(f64);
impl_float_arithmetic_ops!(F32);
impl_float_arithmetic_ops!(F64);
macro_rules! impl_integer {
($type: ident) => {
impl Integer<$type> for $type {
fn leading_zeros(self) -> $type {
self.leading_zeros() as $type
}
fn trailing_zeros(self) -> $type {
self.trailing_zeros() as $type
}
fn count_ones(self) -> $type {
self.count_ones() as $type
}
fn rotl(self, other: $type) -> $type {
self.rotate_left(other as u32)
}
fn rotr(self, other: $type) -> $type {
self.rotate_right(other as u32)
}
fn rem(self, other: $type) -> Result<$type, TrapKind> {
if other == 0 {
Err(TrapKind::DivisionByZero)
} else {
Ok(self.wrapping_rem(other))
}
}
}
};
}
impl_integer!(i32);
impl_integer!(u32);
impl_integer!(i64);
impl_integer!(u64);
#[cfg(feature = "std")]
macro_rules! call_math {
($op:ident, $e:expr, $fXX:ident, $FXXExt:ident) => {
$fXX::$op($e)
};
}
#[cfg(not(feature = "std"))]
macro_rules! call_math {
($op:ident, $e:expr, $fXX:ident, $FXXExt:ident) => {
::libm::$FXXExt::$op($e)
};
}
macro_rules! impl_float {
($type:ident, $fXX:ident, $FXXExt:ident, $iXX:ident) => {
impl Float<$type> for $type {
fn abs(self) -> $type {
call_math!(abs, $fXX::from(self), $fXX, $FXXExt).into()
}
fn floor(self) -> $type {
call_math!(floor, $fXX::from(self), $fXX, $FXXExt).into()
}
fn ceil(self) -> $type {
call_math!(ceil, $fXX::from(self), $fXX, $FXXExt).into()
}
fn trunc(self) -> $type {
call_math!(trunc, $fXX::from(self), $fXX, $FXXExt).into()
}
fn round(self) -> $type {
call_math!(round, $fXX::from(self), $fXX, $FXXExt).into()
}
fn nearest(self) -> $type {
let round = self.round();
if call_math!(fract, $fXX::from(self), $fXX, $FXXExt).abs() != 0.5 {
return round;
}
use core::ops::Rem;
if round.rem(2.0) == 1.0 {
self.floor()
} else if round.rem(2.0) == -1.0 {
self.ceil()
} else {
round
}
}
fn sqrt(self) -> $type {
call_math!(sqrt, $fXX::from(self), $fXX, $FXXExt).into()
}
fn min(self, other: $type) -> $type {
if self.is_nan() {
return self;
}
if other.is_nan() {
return other;
}
self.min(other)
}
fn max(self, other: $type) -> $type {
if self.is_nan() {
return self;
}
if other.is_nan() {
return other;
}
self.max(other)
}
fn copysign(self, other: $type) -> $type {
use core::mem::size_of;
if self.is_nan() {
return self;
}
let sign_mask: $iXX = 1 << ((size_of::<$iXX>() << 3) - 1);
let self_int: $iXX = self.transmute_into();
let other_int: $iXX = other.transmute_into();
let is_self_sign_set = (self_int & sign_mask) != 0;
let is_other_sign_set = (other_int & sign_mask) != 0;
if is_self_sign_set == is_other_sign_set {
self
} else if is_other_sign_set {
(self_int | sign_mask).transmute_into()
} else {
(self_int & !sign_mask).transmute_into()
}
}
}
};
}
impl_float!(f32, f32, F32Ext, i32);
impl_float!(f64, f64, F64Ext, i64);
impl_float!(F32, f32, F32Ext, i32);
impl_float!(F64, f64, F64Ext, i64);