Add parse/write to blobs.

This commit is contained in:
Stephen Chung 2021-12-12 16:29:54 +08:00
parent 780c36e675
commit 6af406bafc
3 changed files with 272 additions and 19 deletions

View File

@ -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
=============

View File

@ -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::<INT>();
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::<INT>();
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::<FLOAT>();
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::<FLOAT>();
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)
}
}

View File

@ -74,3 +74,66 @@ fn test_blobs() -> Result<(), Box<EvalAltResult>> {
Ok(())
}
#[test]
fn test_blobs_parse() -> Result<(), Box<EvalAltResult>> {
let engine = Engine::new();
assert_eq!(
engine.eval::<INT>(
"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::<INT>(
"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::<INT>(
"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::<INT>(
"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::<INT>(
"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::<INT>(
"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::<INT>(
"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::<INT>(
"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(())
}