From 6af406bafc4eb194d665f9f4dd120ee458ad7875 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sun, 12 Dec 2021 16:29:54 +0800 Subject: [PATCH] Add parse/write to blobs. --- CHANGELOG.md | 17 ++- src/packages/blob_basic.rs | 211 +++++++++++++++++++++++++++++++++++-- tests/blobs.rs | 63 +++++++++++ 3 files changed, 272 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4192ccc6..1e07fe18 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,11 +4,19 @@ Rhai Release Notes Version 1.3.0 ============= +This version adds native support for `BLOB`'s (byte arrays), as well as a number of configuration +settings to fine-tun language features. + Compiler requirement -------------------- * Minimum compiler version is now 1.51. +Bug fixes +--------- + +* `from_dynamic` now supports deserializing `Option`. + New features ------------ @@ -40,15 +48,6 @@ Deprecated and Gated API's * `FnPtr::call_dynamic` is deprecated in favor of `FnPtr::call_raw`. -Version 1.2.2 -============= - -Bug fixes ---------- - -* `from_dynamic` now supports deserializing `Option`. - - Version 1.2.1 ============= diff --git a/src/packages/blob_basic.rs b/src/packages/blob_basic.rs index a0dcd40c..ae7a35cc 100644 --- a/src/packages/blob_basic.rs +++ b/src/packages/blob_basic.rs @@ -7,6 +7,9 @@ use crate::{def_package, Blob, Dynamic, EvalAltResult, NativeCallContext, Positi use std::prelude::v1::*; use std::{any::TypeId, mem}; +#[cfg(not(feature = "no_float"))] +use crate::FLOAT; + def_package!(crate:BasicBlobPackage:"Basic BLOB utilities.", lib, { lib.standard = true; @@ -188,10 +191,10 @@ mod blob_functions { } let start = if start < 0 { - let arr_len = blob.len(); + let blob_len = blob.len(); start .checked_abs() - .map_or(0, |n| arr_len - (n as usize).min(arr_len)) + .map_or(0, |n| blob_len - (n as usize).min(blob_len)) } else if start as usize >= blob.len() { blob.extend(replace.into_iter()); return; @@ -215,10 +218,10 @@ mod blob_functions { } let start = if start < 0 { - let arr_len = blob.len(); + let blob_len = blob.len(); start .checked_abs() - .map_or(0, |n| arr_len - (n as usize).min(arr_len)) + .map_or(0, |n| blob_len - (n as usize).min(blob_len)) } else if start as usize >= blob.len() { return Blob::new(); } else { @@ -246,10 +249,10 @@ mod blob_functions { } let start = if start < 0 { - let arr_len = blob.len(); + let blob_len = blob.len(); start .checked_abs() - .map_or(0, |n| arr_len - (n as usize).min(arr_len)) + .map_or(0, |n| blob_len - (n as usize).min(blob_len)) } else if start as usize >= blob.len() { return Blob::new(); } else { @@ -288,10 +291,10 @@ mod blob_functions { } let start = if start < 0 { - let arr_len = blob.len(); + let blob_len = blob.len(); start .checked_abs() - .map_or(0, |n| arr_len - (n as usize).min(arr_len)) + .map_or(0, |n| blob_len - (n as usize).min(blob_len)) } else if start as usize >= blob.len() { return Blob::new(); } else { @@ -314,10 +317,10 @@ mod blob_functions { } let start = if start < 0 { - let arr_len = blob.len(); + let blob_len = blob.len(); start .checked_abs() - .map_or(0, |n| arr_len - (n as usize).min(arr_len)) + .map_or(0, |n| blob_len - (n as usize).min(blob_len)) } else if start as usize >= blob.len() { return mem::take(blob); } else { @@ -351,4 +354,192 @@ mod blob_functions { pub fn not_equals(blob1: &mut Blob, blob2: Blob) -> bool { !equals(blob1, blob2) } + + #[inline] + fn parse_int(blob: &mut Blob, start: INT, len: INT, is_le: bool) -> INT { + if blob.is_empty() || len <= 0 { + return 0; + } + let blob_len = blob.len(); + + let start = if start < 0 { + start + .checked_abs() + .map_or(0, |n| blob_len - (n as usize).min(blob_len)) + } else if start as usize >= blob_len { + return 0; + } else { + start as usize + }; + + let len = if len as usize > blob_len - start { + blob_len - start + } else { + len as usize + }; + + const INT_BYTES: usize = mem::size_of::(); + + let len = usize::min(len, INT_BYTES); + + let mut buf = [0_u8; INT_BYTES]; + + buf[..len].copy_from_slice(&blob[start..][..len]); + + if is_le { + INT::from_le_bytes(buf) + } else { + INT::from_be_bytes(buf) + } + } + + pub fn parse_le_int(blob: &mut Blob, start: INT, len: INT) -> INT { + parse_int(blob, start, len, true) + } + pub fn parse_be_int(blob: &mut Blob, start: INT, len: INT) -> INT { + parse_int(blob, start, len, false) + } + + #[inline] + fn write_int(blob: &mut Blob, start: INT, len: INT, value: INT, is_le: bool) { + if blob.is_empty() || len <= 0 { + return; + } + let blob_len = blob.len(); + + let start = if start < 0 { + start + .checked_abs() + .map_or(0, |n| blob_len - (n as usize).min(blob_len)) + } else if start as usize >= blob_len { + return; + } else { + start as usize + }; + + let len = if len as usize > blob_len - start { + blob_len - start + } else { + len as usize + }; + + const INT_BYTES: usize = mem::size_of::(); + + let len = usize::min(len, INT_BYTES); + + let mut buf = [0_u8; INT_BYTES]; + + buf.copy_from_slice(&if is_le { + value.to_le_bytes() + } else { + value.to_be_bytes() + }); + + blob[start..][..len].copy_from_slice(&buf[..len]); + } + #[rhai_fn(name = "write_le")] + pub fn write_le_int(blob: &mut Blob, start: INT, len: INT, value: INT) { + write_int(blob, start, len, value, true) + } + #[rhai_fn(name = "write_be")] + pub fn write_be_int(blob: &mut Blob, start: INT, len: INT, value: INT) { + write_int(blob, start, len, value, false) + } + + #[cfg(not(feature = "no_float"))] + #[inline] + fn parse_float(blob: &mut Blob, start: INT, len: INT, is_le: bool) -> FLOAT { + if blob.is_empty() || len <= 0 { + return 0.0; + } + let blob_len = blob.len(); + + let start = if start < 0 { + start + .checked_abs() + .map_or(0, |n| blob_len - (n as usize).min(blob_len)) + } else if start as usize >= blob_len { + return 0.0; + } else { + start as usize + }; + + let len = if len as usize > blob_len - start { + blob_len - start + } else { + len as usize + }; + + const FLOAT_BYTES: usize = mem::size_of::(); + + let len = usize::min(len, FLOAT_BYTES); + + let mut buf = [0_u8; FLOAT_BYTES]; + + buf[..len].copy_from_slice(&blob[start..][..len]); + + if is_le { + FLOAT::from_le_bytes(buf) + } else { + FLOAT::from_be_bytes(buf) + } + } + + #[cfg(not(feature = "no_float"))] + pub fn parse_le_float(blob: &mut Blob, start: INT, len: INT) -> FLOAT { + parse_float(blob, start, len, true) + } + #[cfg(not(feature = "no_float"))] + pub fn parse_be_float(blob: &mut Blob, start: INT, len: INT) -> FLOAT { + parse_float(blob, start, len, false) + } + + #[cfg(not(feature = "no_float"))] + #[inline] + fn write_float(blob: &mut Blob, start: INT, len: INT, value: FLOAT, is_le: bool) { + if blob.is_empty() || len <= 0 { + return; + } + let blob_len = blob.len(); + + let start = if start < 0 { + start + .checked_abs() + .map_or(0, |n| blob_len - (n as usize).min(blob_len)) + } else if start as usize >= blob_len { + return; + } else { + start as usize + }; + + let len = if len as usize > blob_len - start { + blob_len - start + } else { + len as usize + }; + + const FLOAT_BYTES: usize = mem::size_of::(); + + let len = usize::min(len, FLOAT_BYTES); + + let mut buf = [0_u8; FLOAT_BYTES]; + + buf.copy_from_slice(&if is_le { + value.to_le_bytes() + } else { + value.to_be_bytes() + }); + + blob[start..][..len].copy_from_slice(&buf[..len]); + } + #[cfg(not(feature = "no_float"))] + #[rhai_fn(name = "write_le")] + pub fn write_le_float(blob: &mut Blob, start: INT, len: INT, value: FLOAT) { + write_float(blob, start, len, value, true) + } + #[cfg(not(feature = "no_float"))] + #[rhai_fn(name = "write_be")] + pub fn write_be_float(blob: &mut Blob, start: INT, len: INT, value: FLOAT) { + write_float(blob, start, len, value, false) + } } diff --git a/tests/blobs.rs b/tests/blobs.rs index f350b26a..b549b8d8 100644 --- a/tests/blobs.rs +++ b/tests/blobs.rs @@ -74,3 +74,66 @@ fn test_blobs() -> Result<(), Box> { Ok(()) } + +#[test] +fn test_blobs_parse() -> Result<(), Box> { + let engine = Engine::new(); + + assert_eq!( + engine.eval::( + "let x = blob(16, 0); for n in range(0, 16) { x[n] = n; } parse_le_int(x,2,0)" + )?, + 0 + ); + + assert_eq!( + engine.eval::( + "let x = blob(16, 0); for n in range(0, 16) { x[n] = n; } parse_le_int(x,2,9)" + )?, + 0x0908070605040302 + ); + + assert_eq!( + engine.eval::( + "let x = blob(16, 0); for n in range(0, 16) { x[n] = n; } parse_be_int(x,2,10)" + )?, + 0x0203040506070809 + ); + + assert_eq!( + engine.eval::( + "let x = blob(16, 0); for n in range(0, 16) { x[n] = n; } parse_le_int(x,-5,99)" + )?, + 0x0f0e0d0c0b + ); + + assert_eq!( + engine.eval::( + "let x = blob(16, 0); for n in range(0, 16) { x[n] = n; } parse_le_int(x,-5,2)" + )?, + 0x0c0b + ); + + assert_eq!( + engine.eval::( + "let x = blob(16, 0); for n in range(0, 16) { x[n] = n; } parse_le_int(x,-99,99)" + )?, + 0x0706050403020100 + ); + + assert_eq!( + engine.eval::( + "let x = blob(16, 0); for n in range(0, 16) { x[n] = n; } write_be(x, 3, 3, -98765432); parse_be_int(x, 3, 3)" + )?, + 0xffffff0000000000_u64 as i64 + ); + + assert_eq!( + engine.eval::( + "let x = blob(16, 0); for n in range(0, 16) { x[n] = n; } write_le(x, 3, 3, -98765432); parse_le_int(x, 3, 3)" + )?, + 0x1cf588 + ); + + Ok(()) +}