use num_bigint::BigInt;
use oxiz::{TermId, TermManager};
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub struct IntType {
pub width: u32,
pub signed: bool,
}
#[must_use]
pub fn int_type_of(ty: &syn::Type) -> Option<IntType> {
let syn::Type::Path(type_path) = ty else {
return None;
};
if type_path.qself.is_some() {
return None;
}
if type_path.path.segments.len() != 1 {
return None;
}
let segment = type_path.path.segments.first()?;
if !matches!(segment.arguments, syn::PathArguments::None) {
return None;
}
int_type_from_name(&segment.ident.to_string())
}
#[must_use]
pub fn int_type_from_name(name: &str) -> Option<IntType> {
let (signed, width) = match name {
"i8" => (true, 8),
"u8" => (false, 8),
"i16" => (true, 16),
"u16" => (false, 16),
"i32" => (true, 32),
"u32" => (false, 32),
"i64" => (true, 64),
"u64" => (false, 64),
"i128" => (true, 128),
"u128" => (false, 128),
"isize" => (true, 64),
"usize" => (false, 64),
_ => return None,
};
Some(IntType { width, signed })
}
#[must_use]
pub fn decode_bits(raw: &BigInt, ty: IntType) -> String {
let modulus = BigInt::from(1u8) << ty.width;
let mut unsigned = raw % &modulus;
if unsigned.sign() == num_bigint::Sign::Minus {
unsigned += &modulus;
}
if ty.signed {
let half = BigInt::from(1u8) << (ty.width - 1);
if unsigned >= half {
return (unsigned - modulus).to_string();
}
}
unsigned.to_string()
}
pub fn zero_extend(tm: &mut TermManager, term: TermId, from_w: u32, to_w: u32) -> TermId {
if to_w <= from_w {
return term;
}
let pad_bits = to_w - from_w;
let zero = tm.mk_bitvec(0i64, pad_bits);
tm.mk_bv_concat(zero, term)
}
pub fn sign_extend(tm: &mut TermManager, term: TermId, from_w: u32, to_w: u32) -> TermId {
if to_w <= from_w {
return term;
}
let pad_bits = to_w - from_w;
let sign_bit = tm.mk_bv_extract(from_w - 1, from_w - 1, term);
let mut replicated = sign_bit;
for _ in 1..pad_bits {
replicated = tm.mk_bv_concat(replicated, sign_bit);
}
tm.mk_bv_concat(replicated, term)
}
#[cfg(test)]
mod tests {
use super::*;
use num_bigint::BigInt;
fn parse_ty(src: &str) -> syn::Type {
syn::parse_str(src).expect("type should parse")
}
#[test]
fn maps_unsigned_widths() {
assert_eq!(
int_type_of(&parse_ty("u8")),
Some(IntType {
width: 8,
signed: false
})
);
assert_eq!(
int_type_of(&parse_ty("u32")),
Some(IntType {
width: 32,
signed: false
})
);
assert_eq!(
int_type_of(&parse_ty("u128")),
Some(IntType {
width: 128,
signed: false
})
);
}
#[test]
fn maps_signed_widths() {
assert_eq!(
int_type_of(&parse_ty("i8")),
Some(IntType {
width: 8,
signed: true
})
);
assert_eq!(
int_type_of(&parse_ty("i64")),
Some(IntType {
width: 64,
signed: true
})
);
}
#[test]
fn maps_isize_usize_to_64() {
assert_eq!(
int_type_of(&parse_ty("usize")),
Some(IntType {
width: 64,
signed: false
})
);
assert_eq!(
int_type_of(&parse_ty("isize")),
Some(IntType {
width: 64,
signed: true
})
);
}
#[test]
fn rejects_non_integer_types() {
assert_eq!(int_type_of(&parse_ty("f32")), None);
assert_eq!(int_type_of(&parse_ty("f64")), None);
assert_eq!(int_type_of(&parse_ty("String")), None);
assert_eq!(int_type_of(&parse_ty("&u32")), None);
assert_eq!(int_type_of(&parse_ty("[u8]")), None);
assert_eq!(int_type_of(&parse_ty("Vec<u8>")), None);
assert_eq!(int_type_of(&parse_ty("std::primitive::u32")), None);
assert_eq!(int_type_of(&parse_ty("bool")), None);
}
#[test]
fn decodes_unsigned() {
let u8t = IntType {
width: 8,
signed: false,
};
assert_eq!(decode_bits(&BigInt::from(0), u8t), "0");
assert_eq!(decode_bits(&BigInt::from(255), u8t), "255");
assert_eq!(decode_bits(&BigInt::from(128), u8t), "128");
}
#[test]
fn decodes_signed_two_complement() {
let i8t = IntType {
width: 8,
signed: true,
};
assert_eq!(decode_bits(&BigInt::from(255), i8t), "-1");
assert_eq!(decode_bits(&BigInt::from(128), i8t), "-128");
assert_eq!(decode_bits(&BigInt::from(127), i8t), "127");
assert_eq!(decode_bits(&BigInt::from(0), i8t), "0");
}
#[test]
fn decodes_signed_32bit() {
let i32t = IntType {
width: 32,
signed: true,
};
let all_ones = (BigInt::from(1u8) << 32) - 1;
assert_eq!(decode_bits(&all_ones, i32t), "-1");
let min = BigInt::from(1u8) << 31;
assert_eq!(decode_bits(&min, i32t), "-2147483648");
}
}