Add blobs.

This commit is contained in:
Stephen Chung 2021-11-23 14:58:54 +08:00
parent 6ad7b344bf
commit a757dfe89d
9 changed files with 760 additions and 21 deletions

View File

@ -30,7 +30,7 @@ use std::{
}; };
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
use crate::Array; use crate::{Array, Blob};
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
use crate::Map; use crate::Map;
@ -482,6 +482,10 @@ pub enum Target<'a> {
/// This is necessary because directly pointing to a bit inside an [`INT`][crate::INT] is impossible. /// This is necessary because directly pointing to a bit inside an [`INT`][crate::INT] is impossible.
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
BitField(&'a mut Dynamic, usize, Dynamic), BitField(&'a mut Dynamic, usize, Dynamic),
/// The target is a byte inside a Blob.
/// This is necessary because directly pointing to a byte (in [`Dynamic`] form) inside a blob is impossible.
#[cfg(not(feature = "no_index"))]
BlobByte(&'a mut Dynamic, usize, Dynamic),
/// The target is a character inside a String. /// The target is a character inside a String.
/// This is necessary because directly pointing to a char inside a String is impossible. /// This is necessary because directly pointing to a char inside a String is impossible.
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
@ -500,9 +504,7 @@ impl<'a> Target<'a> {
Self::LockGuard(_) => true, Self::LockGuard(_) => true,
Self::TempValue(_) => false, Self::TempValue(_) => false,
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Self::BitField(_, _, _) => false, Self::BitField(_, _, _) | Self::BlobByte(_, _, _) | Self::StringChar(_, _, _) => false,
#[cfg(not(feature = "no_index"))]
Self::StringChar(_, _, _) => false,
} }
} }
/// Is the `Target` a temp value? /// Is the `Target` a temp value?
@ -515,9 +517,7 @@ impl<'a> Target<'a> {
Self::LockGuard(_) => false, Self::LockGuard(_) => false,
Self::TempValue(_) => true, Self::TempValue(_) => true,
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Self::BitField(_, _, _) => false, Self::BitField(_, _, _) | Self::BlobByte(_, _, _) | Self::StringChar(_, _, _) => false,
#[cfg(not(feature = "no_index"))]
Self::StringChar(_, _, _) => false,
} }
} }
/// Is the `Target` a shared value? /// Is the `Target` a shared value?
@ -531,9 +531,7 @@ impl<'a> Target<'a> {
Self::LockGuard(_) => true, Self::LockGuard(_) => true,
Self::TempValue(r) => r.is_shared(), Self::TempValue(r) => r.is_shared(),
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Self::BitField(_, _, _) => false, Self::BitField(_, _, _) | Self::BlobByte(_, _, _) | Self::StringChar(_, _, _) => false,
#[cfg(not(feature = "no_index"))]
Self::StringChar(_, _, _) => false,
} }
} }
/// Is the `Target` a specific type? /// Is the `Target` a specific type?
@ -549,6 +547,8 @@ impl<'a> Target<'a> {
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Self::BitField(_, _, _) => TypeId::of::<T>() == TypeId::of::<bool>(), Self::BitField(_, _, _) => TypeId::of::<T>() == TypeId::of::<bool>(),
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Self::BlobByte(_, _, _) => TypeId::of::<T>() == TypeId::of::<Blob>(),
#[cfg(not(feature = "no_index"))]
Self::StringChar(_, _, _) => TypeId::of::<T>() == TypeId::of::<char>(), Self::StringChar(_, _, _) => TypeId::of::<T>() == TypeId::of::<char>(),
} }
} }
@ -564,6 +564,8 @@ impl<'a> Target<'a> {
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Self::BitField(_, _, value) => value, // Boolean is taken Self::BitField(_, _, value) => value, // Boolean is taken
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Self::BlobByte(_, _, value) => value, // Byte is taken
#[cfg(not(feature = "no_index"))]
Self::StringChar(_, _, ch) => ch, // Character is taken Self::StringChar(_, _, ch) => ch, // Character is taken
} }
} }
@ -617,6 +619,27 @@ impl<'a> Target<'a> {
} }
} }
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Self::BlobByte(value, index, new_val) => {
// Replace the byte at the specified index position
let new_byte = new_val.as_int().map_err(|err| {
Box::new(EvalAltResult::ErrorMismatchDataType(
"INT".to_string(),
err.to_string(),
Position::NONE,
))
})?;
let value = &mut *value.write_lock::<Blob>().expect("`Blob`");
let index = *index;
if index < value.len() {
value[index] = (new_byte & 0x000f) as u8;
} else {
unreachable!("blob index out of bounds: {}", index);
}
}
#[cfg(not(feature = "no_index"))]
Self::StringChar(s, index, new_val) => { Self::StringChar(s, index, new_val) => {
// Replace the character at the specified index position // Replace the character at the specified index position
let new_ch = new_val.as_char().map_err(|err| { let new_ch = new_val.as_char().map_err(|err| {
@ -670,9 +693,9 @@ impl Deref for Target<'_> {
Self::LockGuard((r, _)) => &**r, Self::LockGuard((r, _)) => &**r,
Self::TempValue(ref r) => r, Self::TempValue(ref r) => r,
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Self::BitField(_, _, ref r) => r, Self::BitField(_, _, ref r)
#[cfg(not(feature = "no_index"))] | Self::BlobByte(_, _, ref r)
Self::StringChar(_, _, ref r) => r, | Self::StringChar(_, _, ref r) => r,
} }
} }
} }
@ -693,9 +716,9 @@ impl DerefMut for Target<'_> {
Self::LockGuard((r, _)) => r.deref_mut(), Self::LockGuard((r, _)) => r.deref_mut(),
Self::TempValue(ref mut r) => r, Self::TempValue(ref mut r) => r,
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Self::BitField(_, _, ref mut r) => r, Self::BitField(_, _, ref mut r)
#[cfg(not(feature = "no_index"))] | Self::BlobByte(_, _, ref mut r)
Self::StringChar(_, _, ref mut r) => r, | Self::StringChar(_, _, ref mut r) => r,
} }
} }
} }
@ -2059,6 +2082,45 @@ impl Engine {
.ok_or_else(|| EvalAltResult::ErrorArrayBounds(arr_len, index, idx_pos).into()) .ok_or_else(|| EvalAltResult::ErrorArrayBounds(arr_len, index, idx_pos).into())
} }
#[cfg(not(feature = "no_index"))]
Dynamic(Union::Blob(arr, _, _)) => {
// val_blob[idx]
let index = idx
.as_int()
.map_err(|typ| self.make_type_mismatch_err::<crate::INT>(typ, idx_pos))?;
let arr_len = arr.len();
#[cfg(not(feature = "unchecked"))]
let arr_idx = if index < 0 {
// Count from end if negative
arr_len
- index
.checked_abs()
.ok_or_else(|| EvalAltResult::ErrorArrayBounds(arr_len, index, idx_pos))
.and_then(|n| {
if n as usize > arr_len {
Err(EvalAltResult::ErrorArrayBounds(arr_len, index, idx_pos)
.into())
} else {
Ok(n as usize)
}
})?
} else {
index as usize
};
#[cfg(feature = "unchecked")]
let arr_idx = if index < 0 {
// Count from end if negative
arr_len - index.abs() as usize
} else {
index as usize
};
let value = (arr[arr_idx] as INT).into();
Ok(Target::BlobByte(target, arr_idx, value))
}
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
Dynamic(Union::Map(map, _, _)) => { Dynamic(Union::Map(map, _, _)) => {
// val_map[idx] // val_map[idx]
@ -3203,6 +3265,7 @@ impl Engine {
let (a, m, s) = calc_size(value); let (a, m, s) = calc_size(value);
(arrays + a + 1, maps + m, strings + s) (arrays + a + 1, maps + m, strings + s)
} }
Union::Blob(ref a, _, _) => (arrays + 1 + a.len(), maps, strings),
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
Union::Map(_, _, _) => { Union::Map(_, _, _) => {
let (a, m, s) = calc_size(value); let (a, m, s) = calc_size(value);
@ -3212,6 +3275,8 @@ impl Engine {
_ => (arrays + 1, maps, strings), _ => (arrays + 1, maps, strings),
}) })
} }
#[cfg(not(feature = "no_index"))]
Union::Blob(ref arr, _, _) => (arr.len(), 0, 0),
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
Union::Map(ref map, _, _) => { Union::Map(ref map, _, _) => {
map.values() map.values()
@ -3221,6 +3286,8 @@ impl Engine {
let (a, m, s) = calc_size(value); let (a, m, s) = calc_size(value);
(arrays + a, maps + m + 1, strings + s) (arrays + a, maps + m + 1, strings + s)
} }
#[cfg(not(feature = "no_index"))]
Union::Blob(ref a, _, _) => (arrays + a.len(), maps, strings),
Union::Map(_, _, _) => { Union::Map(_, _, _) => {
let (a, m, s) = calc_size(value); let (a, m, s) = calc_size(value);
(arrays + a, maps + m + 1, strings + s) (arrays + a, maps + m + 1, strings + s)

View File

@ -176,6 +176,11 @@ pub use ast::ScriptFnMetadata;
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
pub type Array = Vec<Dynamic>; pub type Array = Vec<Dynamic>;
/// Variable-sized array of [`u8`] values (byte array).
/// Not available under `no_index`.
#[cfg(not(feature = "no_index"))]
pub type Blob = Vec<u8>;
/// Hash map of [`Dynamic`] values with [`SmartString`](https://crates.io/crates/smartstring) keys. /// Hash map of [`Dynamic`] values with [`SmartString`](https://crates.io/crates/smartstring) keys.
/// Not available under `no_object`. /// Not available under `no_object`.
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]

507
src/packages/blob_basic.rs Normal file
View File

@ -0,0 +1,507 @@
#![cfg(not(feature = "no_index"))]
#![allow(non_snake_case)]
use crate::plugin::*;
use crate::{def_package, Blob, Dynamic, EvalAltResult, FnPtr, NativeCallContext, Position, INT};
#[cfg(feature = "no_std")]
use std::prelude::v1::*;
use std::{any::TypeId, mem};
def_package!(crate:BasicBlobPackage:"Basic BLOB utilities.", lib, {
lib.standard = true;
combine_with_exported_module!(lib, "blob", blob_functions);
// Register blob iterator
lib.set_iterable::<Blob>();
});
#[export_module]
mod blob_functions {
pub fn blob() -> Blob {
Blob::new()
}
#[rhai_fn(name = "blob", return_raw)]
pub fn blob_with_capacity(
ctx: NativeCallContext,
len: INT,
) -> Result<Blob, Box<EvalAltResult>> {
blob_with_capacity_and_value(ctx, len, 0)
}
#[rhai_fn(name = "blob", return_raw)]
pub fn blob_with_capacity_and_value(
_ctx: NativeCallContext,
len: INT,
value: INT,
) -> Result<Blob, Box<EvalAltResult>> {
let len = if len < 0 { 0 } else { len as usize };
// Check if blob will be over max size limit
#[cfg(not(feature = "unchecked"))]
if _ctx.engine().max_array_size() > 0 && len > _ctx.engine().max_array_size() {
return Err(EvalAltResult::ErrorDataTooLarge(
"Size of BLOB".to_string(),
Position::NONE,
)
.into());
}
let value = (value & 0x000f) as u8;
let mut blob = Blob::with_capacity(len);
for _ in 0..len {
blob.push(value);
}
Ok(blob)
}
#[rhai_fn(name = "len", get = "len", pure)]
pub fn len(blob: &mut Blob) -> INT {
blob.len() as INT
}
#[rhai_fn(name = "push", name = "+=")]
pub fn push(blob: &mut Blob, item: INT) {
let item = (item & 0x000f) as u8;
blob.push(item);
}
#[rhai_fn(name = "append", name = "+=")]
pub fn append(blob: &mut Blob, y: Blob) {
if !y.is_empty() {
if blob.is_empty() {
*blob = y;
} else {
blob.extend(y);
}
}
}
#[rhai_fn(name = "+")]
pub fn concat(mut blob: Blob, y: Blob) -> Blob {
if !y.is_empty() {
if blob.is_empty() {
blob = y;
} else {
blob.extend(y);
}
}
blob
}
pub fn insert(blob: &mut Blob, position: INT, item: INT) {
let item = (item & 0x000f) as u8;
if blob.is_empty() {
blob.push(item);
} else if position < 0 {
if let Some(n) = position.checked_abs() {
if n as usize > blob.len() {
blob.insert(0, item);
} else {
blob.insert(blob.len() - n as usize, item);
}
} else {
blob.insert(0, item);
}
} else if (position as usize) >= blob.len() {
blob.push(item);
} else {
blob.insert(position as usize, item);
}
}
#[rhai_fn(return_raw)]
pub fn pad(
_ctx: NativeCallContext,
blob: &mut Blob,
len: INT,
item: INT,
) -> Result<(), Box<EvalAltResult>> {
if len <= 0 {
return Ok(());
}
let item = (item & 0x000f) as u8;
// Check if blob will be over max size limit
#[cfg(not(feature = "unchecked"))]
if _ctx.engine().max_array_size() > 0 && (len as usize) > _ctx.engine().max_array_size() {
return Err(EvalAltResult::ErrorDataTooLarge(
"Size of BLOB".to_string(),
Position::NONE,
)
.into());
}
if len as usize > blob.len() {
blob.resize(len as usize, item);
}
Ok(())
}
pub fn pop(blob: &mut Blob) -> INT {
if blob.is_empty() {
0
} else {
blob.pop().map_or_else(|| 0, |v| v as INT)
}
}
pub fn shift(blob: &mut Blob) -> INT {
if blob.is_empty() {
0
} else {
blob.remove(0) as INT
}
}
pub fn remove(blob: &mut Blob, len: INT) -> INT {
if len < 0 || (len as usize) >= blob.len() {
0
} else {
blob.remove(len as usize) as INT
}
}
pub fn clear(blob: &mut Blob) {
if !blob.is_empty() {
blob.clear();
}
}
pub fn truncate(blob: &mut Blob, len: INT) {
if !blob.is_empty() {
if len >= 0 {
blob.truncate(len as usize);
} else {
blob.clear();
}
}
}
pub fn chop(blob: &mut Blob, len: INT) {
if !blob.is_empty() && len as usize >= blob.len() {
if len >= 0 {
blob.drain(0..blob.len() - len as usize);
} else {
blob.clear();
}
}
}
pub fn reverse(blob: &mut Blob) {
if !blob.is_empty() {
blob.reverse();
}
}
pub fn splice(blob: &mut Blob, start: INT, len: INT, replace: Blob) {
if blob.is_empty() {
*blob = replace;
return;
}
let start = if start < 0 {
let arr_len = blob.len();
start
.checked_abs()
.map_or(0, |n| arr_len - (n as usize).min(arr_len))
} else if start as usize >= blob.len() {
blob.extend(replace.into_iter());
return;
} else {
start as usize
};
let len = if len < 0 {
0
} else if len as usize > blob.len() - start {
blob.len() - start
} else {
len as usize
};
blob.splice(start..start + len, replace.into_iter());
}
pub fn extract(blob: &mut Blob, start: INT, len: INT) -> Blob {
if blob.is_empty() || len <= 0 {
return Blob::new();
}
let start = if start < 0 {
let arr_len = blob.len();
start
.checked_abs()
.map_or(0, |n| arr_len - (n as usize).min(arr_len))
} else if start as usize >= blob.len() {
return Blob::new();
} else {
start as usize
};
let len = if len <= 0 {
0
} else if len as usize > blob.len() - start {
blob.len() - start
} else {
len as usize
};
if len == 0 {
Blob::new()
} else {
blob[start..start + len].to_vec()
}
}
#[rhai_fn(name = "extract")]
pub fn extract_tail(blob: &mut Blob, start: INT) -> Blob {
if blob.is_empty() {
return Blob::new();
}
let start = if start < 0 {
let arr_len = blob.len();
start
.checked_abs()
.map_or(0, |n| arr_len - (n as usize).min(arr_len))
} else if start as usize >= blob.len() {
return Blob::new();
} else {
start as usize
};
blob[start..].to_vec()
}
#[rhai_fn(name = "split")]
pub fn split_at(blob: &mut Blob, start: INT) -> Blob {
if blob.is_empty() {
Blob::new()
} else if start < 0 {
if let Some(n) = start.checked_abs() {
if n as usize > blob.len() {
mem::take(blob)
} else {
let mut result = Blob::new();
result.extend(blob.drain(blob.len() - n as usize..));
result
}
} else {
mem::take(blob)
}
} else if start as usize >= blob.len() {
Blob::new()
} else {
let mut result = Blob::new();
result.extend(blob.drain(start as usize..));
result
}
}
#[rhai_fn(return_raw, pure)]
pub fn drain(
ctx: NativeCallContext,
blob: &mut Blob,
filter: FnPtr,
) -> Result<Blob, Box<EvalAltResult>> {
drain_with_fn_name(ctx, blob, filter.fn_name())
}
#[rhai_fn(name = "drain", return_raw)]
pub fn drain_with_fn_name(
ctx: NativeCallContext,
blob: &mut Blob,
filter: &str,
) -> Result<Blob, Box<EvalAltResult>> {
if blob.is_empty() {
return Ok(Blob::new());
}
let mut index_val = Dynamic::ZERO;
let mut removed = Vec::with_capacity(blob.len());
let mut count = 0;
for (i, &item) in blob.iter().enumerate() {
let mut item = (item as INT).into();
let mut args = [&mut item, &mut index_val];
let remove = match ctx.call_fn_raw(filter, true, false, &mut args[..1]) {
Ok(r) => r,
Err(err) => match *err {
EvalAltResult::ErrorFunctionNotFound(fn_sig, _)
if fn_sig.starts_with(filter) =>
{
*args[1] = Dynamic::from(i as INT);
ctx.call_fn_raw(filter, true, false, &mut args)?
}
_ => {
return Err(EvalAltResult::ErrorInFunctionCall(
"drain".to_string(),
ctx.source().unwrap_or("").to_string(),
err,
Position::NONE,
)
.into())
}
},
}
.as_bool()
.unwrap_or(false);
removed.push(remove);
if remove {
count += 1;
}
}
if count == 0 {
return Ok(Blob::new());
}
let mut result = Vec::with_capacity(count);
let mut x = 0;
let mut i = 0;
while i < blob.len() {
if removed[x] {
result.push(blob.remove(i));
} else {
i += 1;
}
x += 1;
}
Ok(result.into())
}
#[rhai_fn(name = "drain")]
pub fn drain_range(blob: &mut Blob, start: INT, len: INT) -> Blob {
if blob.is_empty() || len <= 0 {
return Blob::new();
}
let start = if start < 0 {
let arr_len = blob.len();
start
.checked_abs()
.map_or(0, |n| arr_len - (n as usize).min(arr_len))
} else if start as usize >= blob.len() {
return Blob::new();
} else {
start as usize
};
let len = if len <= 0 {
0
} else if len as usize > blob.len() - start {
blob.len() - start
} else {
len as usize
};
blob.drain(start..start + len).collect()
}
#[rhai_fn(return_raw, pure)]
pub fn retain(
ctx: NativeCallContext,
blob: &mut Blob,
filter: FnPtr,
) -> Result<Blob, Box<EvalAltResult>> {
retain_with_fn_name(ctx, blob, filter.fn_name())
}
#[rhai_fn(name = "retain", return_raw)]
pub fn retain_with_fn_name(
ctx: NativeCallContext,
blob: &mut Blob,
filter: &str,
) -> Result<Blob, Box<EvalAltResult>> {
if blob.is_empty() {
return Ok(Blob::new());
}
let mut index_val = Dynamic::ZERO;
let mut removed = Vec::with_capacity(blob.len());
let mut count = 0;
for (i, &item) in blob.iter().enumerate() {
let mut item = (item as INT).into();
let mut args = [&mut item, &mut index_val];
let keep = match ctx.call_fn_raw(filter, true, false, &mut args[..1]) {
Ok(r) => r,
Err(err) => match *err {
EvalAltResult::ErrorFunctionNotFound(fn_sig, _)
if fn_sig.starts_with(filter) =>
{
*args[1] = Dynamic::from(i as INT);
ctx.call_fn_raw(filter, true, false, &mut args)?
}
_ => {
return Err(EvalAltResult::ErrorInFunctionCall(
"retain".to_string(),
ctx.source().unwrap_or("").to_string(),
err,
Position::NONE,
)
.into())
}
},
}
.as_bool()
.unwrap_or(false);
removed.push(!keep);
if !keep {
count += 1;
}
}
if count == 0 {
return Ok(Blob::new());
}
let mut result = Vec::with_capacity(count);
let mut x = 0;
let mut i = 0;
while i < blob.len() {
if removed[x] {
result.push(blob.remove(i));
} else {
i += 1;
}
x += 1;
}
Ok(result.into())
}
#[rhai_fn(name = "retain")]
pub fn retain_range(blob: &mut Blob, start: INT, len: INT) -> Blob {
if blob.is_empty() || len <= 0 {
return Blob::new();
}
let start = if start < 0 {
let arr_len = blob.len();
start
.checked_abs()
.map_or(0, |n| arr_len - (n as usize).min(arr_len))
} else if start as usize >= blob.len() {
return mem::take(blob);
} else {
start as usize
};
let len = if len < 0 {
0
} else if len as usize > blob.len() - start {
blob.len() - start
} else {
len as usize
};
let mut drained: Blob = blob.drain(..start).collect();
drained.extend(blob.drain(len..));
drained
}
#[rhai_fn(name = "==", pure)]
pub fn equals(blob1: &mut Blob, blob2: Blob) -> bool {
if blob1.len() != blob2.len() {
false
} else if blob1.is_empty() {
true
} else {
blob1.iter().zip(blob2.iter()).all(|(&v1, &v2)| v1 == v2)
}
}
#[rhai_fn(name = "!=", pure)]
pub fn not_equals(blob1: &mut Blob, blob2: Blob) -> bool {
!equals(blob1, blob2)
}
}

View File

@ -3,22 +3,29 @@
use crate::{Module, Shared}; use crate::{Module, Shared};
pub(crate) mod arithmetic; pub(crate) mod arithmetic;
#[cfg(not(feature = "no_index"))]
mod array_basic; mod array_basic;
#[cfg(not(feature = "no_index"))]
mod blob_basic;
mod fn_basic; mod fn_basic;
mod iter_basic; mod iter_basic;
mod lang_core; mod lang_core;
mod logic; mod logic;
#[cfg(not(feature = "no_object"))]
mod map_basic; mod map_basic;
mod math_basic; mod math_basic;
mod pkg_core; mod pkg_core;
mod pkg_std; mod pkg_std;
mod string_basic; mod string_basic;
mod string_more; mod string_more;
#[cfg(not(feature = "no_std"))]
mod time_basic; mod time_basic;
pub use arithmetic::ArithmeticPackage; pub use arithmetic::ArithmeticPackage;
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
pub use array_basic::BasicArrayPackage; pub use array_basic::BasicArrayPackage;
#[cfg(not(feature = "no_index"))]
pub use blob_basic::BasicBlobPackage;
pub use fn_basic::BasicFnPackage; pub use fn_basic::BasicFnPackage;
pub use iter_basic::BasicIteratorPackage; pub use iter_basic::BasicIteratorPackage;
pub use logic::LogicPackage; pub use logic::LogicPackage;

View File

@ -1,5 +1,7 @@
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
use super::array_basic::BasicArrayPackage; use super::array_basic::BasicArrayPackage;
#[cfg(not(feature = "no_index"))]
use super::blob_basic::BasicBlobPackage;
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
use super::map_basic::BasicMapPackage; use super::map_basic::BasicMapPackage;
use super::math_basic::BasicMathPackage; use super::math_basic::BasicMathPackage;
@ -18,7 +20,10 @@ def_package!(crate:StandardPackage:"_Standard_ package containing all built-in f
CorePackage::init(lib); CorePackage::init(lib);
BasicMathPackage::init(lib); BasicMathPackage::init(lib);
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
BasicArrayPackage::init(lib); {
BasicArrayPackage::init(lib);
BasicBlobPackage::init(lib);
}
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
BasicMapPackage::init(lib); BasicMapPackage::init(lib);
#[cfg(not(feature = "no_std"))] #[cfg(not(feature = "no_std"))]

View File

@ -153,6 +153,8 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> {
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Union::Array(_, _, _) => self.deserialize_seq(visitor), Union::Array(_, _, _) => self.deserialize_seq(visitor),
#[cfg(not(feature = "no_index"))]
Union::Blob(_, _, _) => self.deserialize_seq(visitor),
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
Union::Map(_, _, _) => self.deserialize_map(visitor), Union::Map(_, _, _) => self.deserialize_map(visitor),
Union::FnPtr(_, _, _) => self.type_error(), Union::FnPtr(_, _, _) => self.type_error(),

View File

@ -57,6 +57,8 @@ impl Serialize for Dynamic {
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Union::Array(ref a, _, _) => (**a).serialize(ser), Union::Array(ref a, _, _) => (**a).serialize(ser),
#[cfg(not(feature = "no_index"))]
Union::Blob(ref a, _, _) => (**a).serialize(ser),
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
Union::Map(ref m, _, _) => { Union::Map(ref m, _, _) => {
let mut map = ser.serialize_map(Some(m.len()))?; let mut map = ser.serialize_map(Some(m.len()))?;

View File

@ -20,7 +20,7 @@ use crate::{ast::FloatWrapper, FLOAT};
use rust_decimal::Decimal; use rust_decimal::Decimal;
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
use crate::Array; use crate::{Array, Blob};
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
use crate::Map; use crate::Map;
@ -200,6 +200,11 @@ pub enum Union {
/// Not available under `no_index`. /// Not available under `no_index`.
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Array(Box<Array>, Tag, AccessMode), Array(Box<Array>, Tag, AccessMode),
/// An blob (byte array).
///
/// Not available under `no_index`.
#[cfg(not(feature = "no_index"))]
Blob(Box<Blob>, Tag, AccessMode),
/// An object map value. /// An object map value.
/// ///
/// Not available under `no_object`. /// Not available under `no_object`.
@ -332,7 +337,7 @@ impl Dynamic {
#[cfg(feature = "decimal")] #[cfg(feature = "decimal")]
Union::Decimal(_, tag, _) => tag, Union::Decimal(_, tag, _) => tag,
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Union::Array(_, tag, _) => tag, Union::Array(_, tag, _) | Union::Blob(_, tag, _) => tag,
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
Union::Map(_, tag, _) => tag, Union::Map(_, tag, _) => tag,
#[cfg(not(feature = "no_std"))] #[cfg(not(feature = "no_std"))]
@ -357,7 +362,7 @@ impl Dynamic {
#[cfg(feature = "decimal")] #[cfg(feature = "decimal")]
Union::Decimal(_, ref mut tag, _) => *tag = value, Union::Decimal(_, ref mut tag, _) => *tag = value,
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Union::Array(_, ref mut tag, _) => *tag = value, Union::Array(_, ref mut tag, _) | Union::Blob(_, ref mut tag, _) => *tag = value,
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
Union::Map(_, ref mut tag, _) => *tag = value, Union::Map(_, ref mut tag, _) => *tag = value,
#[cfg(not(feature = "no_std"))] #[cfg(not(feature = "no_std"))]
@ -419,6 +424,8 @@ impl Dynamic {
Union::Decimal(_, _, _) => TypeId::of::<Decimal>(), Union::Decimal(_, _, _) => TypeId::of::<Decimal>(),
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Union::Array(_, _, _) => TypeId::of::<Array>(), Union::Array(_, _, _) => TypeId::of::<Array>(),
#[cfg(not(feature = "no_index"))]
Union::Blob(_, _, _) => TypeId::of::<Blob>(),
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
Union::Map(_, _, _) => TypeId::of::<Map>(), Union::Map(_, _, _) => TypeId::of::<Map>(),
Union::FnPtr(_, _, _) => TypeId::of::<FnPtr>(), Union::FnPtr(_, _, _) => TypeId::of::<FnPtr>(),
@ -456,6 +463,8 @@ impl Dynamic {
Union::Decimal(_, _, _) => "decimal", Union::Decimal(_, _, _) => "decimal",
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Union::Array(_, _, _) => "array", Union::Array(_, _, _) => "array",
#[cfg(not(feature = "no_index"))]
Union::Blob(_, _, _) => "blob",
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
Union::Map(_, _, _) => "map", Union::Map(_, _, _) => "map",
Union::FnPtr(_, _, _) => "Fn", Union::FnPtr(_, _, _) => "Fn",
@ -498,6 +507,8 @@ impl Hash for Dynamic {
Union::Decimal(ref d, _, _) => d.hash(state), Union::Decimal(ref d, _, _) => d.hash(state),
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Union::Array(ref a, _, _) => a.as_ref().hash(state), Union::Array(ref a, _, _) => a.as_ref().hash(state),
#[cfg(not(feature = "no_index"))]
Union::Blob(ref a, _, _) => a.as_ref().hash(state),
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
Union::Map(ref m, _, _) => m.as_ref().hash(state), Union::Map(ref m, _, _) => m.as_ref().hash(state),
Union::FnPtr(ref f, _, _) => f.hash(state), Union::FnPtr(ref f, _, _) => f.hash(state),
@ -586,6 +597,10 @@ pub(crate) fn map_std_type_name(name: &str) -> &str {
if name == type_name::<Array>() { if name == type_name::<Array>() {
return "array"; return "array";
} }
#[cfg(not(feature = "no_index"))]
if name == type_name::<Blob>() {
return "blob";
}
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
if name == type_name::<Map>() { if name == type_name::<Map>() {
return "map"; return "map";
@ -612,6 +627,8 @@ impl fmt::Display for Dynamic {
Union::Decimal(ref value, _, _) => fmt::Display::fmt(value, f), Union::Decimal(ref value, _, _) => fmt::Display::fmt(value, f),
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Union::Array(ref value, _, _) => fmt::Debug::fmt(value, f), Union::Array(ref value, _, _) => fmt::Debug::fmt(value, f),
#[cfg(not(feature = "no_index"))]
Union::Blob(ref value, _, _) => fmt::Debug::fmt(value, f),
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
Union::Map(ref value, _, _) => { Union::Map(ref value, _, _) => {
f.write_str("#")?; f.write_str("#")?;
@ -692,6 +709,8 @@ impl fmt::Debug for Dynamic {
Union::Decimal(ref value, _, _) => fmt::Debug::fmt(value, f), Union::Decimal(ref value, _, _) => fmt::Debug::fmt(value, f),
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Union::Array(ref value, _, _) => fmt::Debug::fmt(value, f), Union::Array(ref value, _, _) => fmt::Debug::fmt(value, f),
#[cfg(not(feature = "no_index"))]
Union::Blob(ref value, _, _) => fmt::Debug::fmt(value, f),
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
Union::Map(ref value, _, _) => { Union::Map(ref value, _, _) => {
f.write_str("#")?; f.write_str("#")?;
@ -781,6 +800,8 @@ impl Clone for Dynamic {
} }
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Union::Array(ref value, tag, _) => Self(Union::Array(value.clone(), tag, ReadWrite)), Union::Array(ref value, tag, _) => Self(Union::Array(value.clone(), tag, ReadWrite)),
#[cfg(not(feature = "no_index"))]
Union::Blob(ref value, tag, _) => Self(Union::Blob(value.clone(), tag, ReadWrite)),
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
Union::Map(ref value, tag, _) => Self(Union::Map(value.clone(), tag, ReadWrite)), Union::Map(ref value, tag, _) => Self(Union::Map(value.clone(), tag, ReadWrite)),
Union::FnPtr(ref value, tag, _) => Self(Union::FnPtr(value.clone(), tag, ReadWrite)), Union::FnPtr(ref value, tag, _) => Self(Union::FnPtr(value.clone(), tag, ReadWrite)),
@ -972,7 +993,7 @@ impl Dynamic {
#[cfg(feature = "decimal")] #[cfg(feature = "decimal")]
Union::Decimal(_, _, access) => access, Union::Decimal(_, _, access) => access,
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Union::Array(_, _, access) => access, Union::Array(_, _, access) | Union::Blob(_, _, access) => access,
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
Union::Map(_, _, access) => access, Union::Map(_, _, access) => access,
#[cfg(not(feature = "no_std"))] #[cfg(not(feature = "no_std"))]
@ -1003,6 +1024,8 @@ impl Dynamic {
v.set_access_mode(typ); v.set_access_mode(typ);
}); });
} }
#[cfg(not(feature = "no_index"))]
Union::Blob(_, _, ref mut access) => *access = typ,
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
Union::Map(ref mut m, _, ref mut access) => { Union::Map(ref mut m, _, ref mut access) => {
*access = typ; *access = typ;
@ -1166,6 +1189,13 @@ impl Dynamic {
Err(value) => value, Err(value) => value,
}; };
} }
#[cfg(not(feature = "no_index"))]
{
value = match unsafe_try_cast::<_, Blob>(value) {
Ok(blob) => return Self(Union::Blob(Box::new(blob), DEFAULT_TAG_VALUE, ReadWrite)),
Err(value) => value,
};
}
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
{ {
@ -1327,6 +1357,14 @@ impl Dynamic {
}; };
} }
#[cfg(not(feature = "no_index"))]
if TypeId::of::<T>() == TypeId::of::<Blob>() {
return match self.0 {
Union::Blob(value, _, _) => unsafe_cast_box::<_, T>(value).ok().map(|v| *v),
_ => None,
};
}
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
if TypeId::of::<T>() == TypeId::of::<Map>() { if TypeId::of::<T>() == TypeId::of::<Map>() {
return match self.0 { return match self.0 {
@ -1660,6 +1698,13 @@ impl Dynamic {
_ => None, _ => None,
}; };
} }
#[cfg(not(feature = "no_index"))]
if TypeId::of::<T>() == TypeId::of::<Blob>() {
return match self.0 {
Union::Blob(ref value, _, _) => value.as_ref().as_any().downcast_ref::<T>(),
_ => None,
};
}
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
if TypeId::of::<T>() == TypeId::of::<Map>() { if TypeId::of::<T>() == TypeId::of::<Map>() {
return match self.0 { return match self.0 {
@ -1757,6 +1802,13 @@ impl Dynamic {
_ => None, _ => None,
}; };
} }
#[cfg(not(feature = "no_index"))]
if TypeId::of::<T>() == TypeId::of::<Blob>() {
return match self.0 {
Union::Blob(ref mut value, _, _) => value.as_mut().as_mut_any().downcast_mut::<T>(),
_ => None,
};
}
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
if TypeId::of::<T>() == TypeId::of::<Map>() { if TypeId::of::<T>() == TypeId::of::<Map>() {
return match self.0 { return match self.0 {
@ -2097,6 +2149,30 @@ impl Dynamic {
} }
} }
} }
#[cfg(not(feature = "no_index"))]
impl Dynamic {
/// Convert the [`Dynamic`] into a [`Vec<u8>`].
/// Returns the name of the actual type if the cast fails.
#[inline(always)]
pub fn into_blob(self) -> Result<Blob, &'static str> {
match self.0 {
Union::Blob(a, _, _) => Ok(*a),
#[cfg(not(feature = "no_closure"))]
Union::Shared(cell, _, _) => {
#[cfg(not(feature = "sync"))]
let value = cell.borrow();
#[cfg(feature = "sync")]
let value = cell.read().unwrap();
match value.0 {
Union::Blob(ref a, _, _) => Ok(a.as_ref().clone()),
_ => Err((*value).type_name()),
}
}
_ => Err(self.type_name()),
}
}
}
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
impl Dynamic { impl Dynamic {
/// Create a [`Dynamic`] from a [`Map`]. /// Create a [`Dynamic`] from a [`Map`].

68
tests/blobs.rs Normal file
View File

@ -0,0 +1,68 @@
#![cfg(not(feature = "no_index"))]
use rhai::{Blob, Engine, EvalAltResult, Scope, INT};
#[test]
fn test_blobs() -> Result<(), Box<EvalAltResult>> {
let mut a = Blob::new();
a.push(1);
a.push(2);
a.push(3);
let engine = Engine::new();
let mut orig_scope = Scope::new();
orig_scope.push("x", a);
let mut scope = orig_scope.clone();
assert_eq!(engine.eval_with_scope::<INT>(&mut scope, "x[1]")?, 2);
assert_eq!(engine.eval_with_scope::<INT>(&mut scope, "x[0]")?, 1);
assert_eq!(engine.eval_with_scope::<INT>(&mut scope, "x[-1]")?, 3);
assert_eq!(engine.eval_with_scope::<INT>(&mut scope, "x[-3]")?, 1);
assert_eq!(
engine.eval_with_scope::<INT>(&mut scope, "x += 4; x[3]")?,
4
);
#[cfg(not(feature = "no_object"))]
{
assert_eq!(
engine.eval_with_scope::<Blob>(&mut orig_scope.clone(), "x.push(4); x")?,
[1, 2, 3, 4]
);
assert_eq!(
engine.eval_with_scope::<Blob>(&mut orig_scope.clone(), "x.insert(0, 4); x")?,
[4, 1, 2, 3]
);
assert_eq!(
engine.eval_with_scope::<Blob>(&mut orig_scope.clone(), "x.insert(999, 4); x")?,
[1, 2, 3, 4]
);
assert_eq!(
engine.eval_with_scope::<Blob>(&mut orig_scope.clone(), "x.insert(-2, 4); x")?,
[1, 4, 2, 3]
);
assert_eq!(
engine.eval_with_scope::<Blob>(&mut orig_scope.clone(), "x.insert(-999, 4); x")?,
[4, 1, 2, 3]
);
assert_eq!(
engine.eval_with_scope::<INT>(&mut orig_scope.clone(), "let z = [42]; x[z.len]")?,
2
);
assert_eq!(
engine.eval_with_scope::<INT>(&mut orig_scope.clone(), "let z = [2]; x[z[0]]")?,
3
);
}
assert_eq!(
engine.eval_with_scope::<Blob>(&mut orig_scope.clone(), "x += x; x")?,
[1, 2, 3, 1, 2, 3]
);
assert_eq!(
engine.eval_with_scope::<Blob>(&mut orig_scope.clone(), "x + x")?,
[1, 2, 3, 1, 2, 3]
);
Ok(())
}