v86/src/rust/util.rs
2020-08-30 19:27:07 -05:00

191 lines
4.8 KiB
Rust

pub trait SafeToU8 { fn safe_to_u8(self) -> u8; }
pub trait SafeToU16 { fn safe_to_u16(self) -> u16; }
pub trait SafeToI32 { fn safe_to_i32(self) -> i32; }
impl SafeToU8 for u16 {
fn safe_to_u8(self) -> u8 {
dbg_assert!(self <= ::std::u8::MAX as u16);
self as u8
}
}
impl SafeToU8 for u32 {
fn safe_to_u8(self) -> u8 {
dbg_assert!(self <= ::std::u8::MAX as u32);
self as u8
}
}
impl SafeToU8 for i32 {
fn safe_to_u8(self) -> u8 {
dbg_assert!(self >= 0 && self <= ::std::u8::MAX as i32);
self as u8
}
}
impl SafeToU8 for usize {
fn safe_to_u8(self) -> u8 {
dbg_assert!(self <= ::std::u8::MAX as usize);
self as u8
}
}
impl SafeToU16 for u32 {
fn safe_to_u16(self) -> u16 {
dbg_assert!(self <= ::std::u16::MAX as u32);
self as u16
}
}
impl SafeToU16 for i32 {
fn safe_to_u16(self) -> u16 {
dbg_assert!(self >= 0 && self <= ::std::u16::MAX as i32);
self as u16
}
}
impl SafeToU16 for usize {
fn safe_to_u16(self) -> u16 {
dbg_assert!(self <= ::std::u16::MAX as usize);
self as u16
}
}
impl SafeToI32 for u32 {
fn safe_to_i32(self) -> i32 {
dbg_assert!(self <= ::std::i32::MAX as u32);
self as i32
}
}
impl SafeToI32 for usize {
fn safe_to_i32(self) -> i32 {
dbg_assert!(self <= ::std::i32::MAX as usize);
self as i32
}
}
pub fn write_leb_i32(buf: &mut Vec<u8>, mut v: i32) {
// Super complex stuff. See the following:
// https://en.wikipedia.org/wiki/LEB128#Encode_signed_integer
// http://llvm.org/doxygen/LEB128_8h_source.html#l00048
let mut more = true;
let negative = v < 0;
let size = 32;
while more {
let mut byte = (v & 0b1111111) as u8; // get last 7 bits
v >>= 7; // shift them away from the value
if negative {
v |= (!0 as i32) << (size - 7); // extend sign
}
let sign_bit = byte & (1 << 6);
if (v == 0 && sign_bit == 0) || (v == -1 && sign_bit != 0) {
more = false;
}
else {
byte |= 0b10000000; // turn on MSB
}
buf.push(byte);
}
}
pub fn write_leb_u32(buf: &mut Vec<u8>, mut v: u32) {
loop {
let mut byte = v as u8 & 0b01111111; // get last 7 bits
v >>= 7; // shift them away from the value
if v != 0 {
byte |= 0b10000000; // turn on MSB
}
buf.push(byte);
if v == 0 {
break;
}
}
}
pub fn write_fixed_leb16_at_idx(vec: &mut Vec<u8>, idx: usize, x: u16) {
dbg_assert!(x < (1 << 14)); // we have 14 bits of available space in 2 bytes for leb
vec[idx ] = ((x & 0b1111111) | 0b10000000) as u8;
vec[idx + 1] = (x >> 7) as u8;
}
pub fn write_fixed_leb32_at_idx(vec: &mut Vec<u8>, idx: usize, x: u32) {
dbg_assert!(x < (1 << 28)); // we have 28 bits of available space in 4 bytes for leb
vec[idx ] = (x & 0b1111111) as u8 | 0b10000000;
vec[idx + 1] = (x >> 7 & 0b1111111) as u8 | 0b10000000;
vec[idx + 2] = (x >> 14 & 0b1111111) as u8 | 0b10000000;
vec[idx + 3] = (x >> 21 & 0b1111111) as u8;
}
pub type PackedStr = (u64, u64, u64);
#[allow(dead_code)]
pub fn pack_str(s: &str) -> PackedStr {
assert!(s.len() <= 24);
let mut a: [u8; 24] = [0; 24];
for (i, ch) in s.char_indices() {
a[i] = ch as u8;
}
unsafe { ::std::mem::transmute(a) }
}
pub fn unpack_str(s: PackedStr) -> String {
let mut buf = String::with_capacity(24);
let bytes: [u8; 24] = unsafe { ::std::mem::transmute(s) };
for i in 0..24 {
if bytes[i] == 0 {
break;
}
buf.push(bytes[i] as char);
}
buf
}
#[allow(dead_code)]
pub const DEBUG: bool = cfg!(debug_assertions);
#[cfg(target_arch = "wasm32")]
extern "C" {
pub fn log_from_wasm(ptr: *const u8, len: usize);
pub fn abort();
}
#[cfg(target_arch = "wasm32")]
use std::string::ToString;
#[cfg(target_arch = "wasm32")]
pub fn _log_to_js_console<T: ToString>(s: T) {
let s: String = s.to_string();
let len = s.len();
unsafe { log_from_wasm(s.as_bytes().as_ptr(), len); }
}
#[cfg(test)]
mod tests {
use ::util::*;
use quickcheck::TestResult;
#[test]
fn packed_strs() {
let pstr = pack_str("foo");
assert_eq!("foo", unpack_str(pstr));
let pstr = pack_str("abcdefghijkl");
assert_eq!("abcdefghijkl", unpack_str(pstr));
}
quickcheck! {
fn prop(xs: Vec<u8>) -> TestResult {
if xs.len() > 24 || xs.contains(&0) { return TestResult::discard(); }
let xs = match String::from_utf8(xs) {
Ok(x) => x,
Err(_) => { return TestResult::discard(); },
};
TestResult::from_bool(xs == unpack_str(pack_str(&xs)))
}
}
}