Add blobs.
This commit is contained in:
parent
6ad7b344bf
commit
a757dfe89d
@ -30,7 +30,7 @@ use std::{
|
||||
};
|
||||
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
use crate::Array;
|
||||
use crate::{Array, Blob};
|
||||
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
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.
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
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.
|
||||
/// This is necessary because directly pointing to a char inside a String is impossible.
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
@ -500,9 +504,7 @@ impl<'a> Target<'a> {
|
||||
Self::LockGuard(_) => true,
|
||||
Self::TempValue(_) => false,
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Self::BitField(_, _, _) => false,
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Self::StringChar(_, _, _) => false,
|
||||
Self::BitField(_, _, _) | Self::BlobByte(_, _, _) | Self::StringChar(_, _, _) => false,
|
||||
}
|
||||
}
|
||||
/// Is the `Target` a temp value?
|
||||
@ -515,9 +517,7 @@ impl<'a> Target<'a> {
|
||||
Self::LockGuard(_) => false,
|
||||
Self::TempValue(_) => true,
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Self::BitField(_, _, _) => false,
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Self::StringChar(_, _, _) => false,
|
||||
Self::BitField(_, _, _) | Self::BlobByte(_, _, _) | Self::StringChar(_, _, _) => false,
|
||||
}
|
||||
}
|
||||
/// Is the `Target` a shared value?
|
||||
@ -531,9 +531,7 @@ impl<'a> Target<'a> {
|
||||
Self::LockGuard(_) => true,
|
||||
Self::TempValue(r) => r.is_shared(),
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Self::BitField(_, _, _) => false,
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Self::StringChar(_, _, _) => false,
|
||||
Self::BitField(_, _, _) | Self::BlobByte(_, _, _) | Self::StringChar(_, _, _) => false,
|
||||
}
|
||||
}
|
||||
/// Is the `Target` a specific type?
|
||||
@ -549,6 +547,8 @@ impl<'a> Target<'a> {
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Self::BitField(_, _, _) => TypeId::of::<T>() == TypeId::of::<bool>(),
|
||||
#[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>(),
|
||||
}
|
||||
}
|
||||
@ -564,6 +564,8 @@ impl<'a> Target<'a> {
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Self::BitField(_, _, value) => value, // Boolean is taken
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Self::BlobByte(_, _, value) => value, // Byte is taken
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Self::StringChar(_, _, ch) => ch, // Character is taken
|
||||
}
|
||||
}
|
||||
@ -617,6 +619,27 @@ impl<'a> Target<'a> {
|
||||
}
|
||||
}
|
||||
#[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) => {
|
||||
// Replace the character at the specified index position
|
||||
let new_ch = new_val.as_char().map_err(|err| {
|
||||
@ -670,9 +693,9 @@ impl Deref for Target<'_> {
|
||||
Self::LockGuard((r, _)) => &**r,
|
||||
Self::TempValue(ref r) => r,
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Self::BitField(_, _, ref r) => r,
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Self::StringChar(_, _, ref r) => r,
|
||||
Self::BitField(_, _, ref r)
|
||||
| Self::BlobByte(_, _, ref r)
|
||||
| Self::StringChar(_, _, ref r) => r,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -693,9 +716,9 @@ impl DerefMut for Target<'_> {
|
||||
Self::LockGuard((r, _)) => r.deref_mut(),
|
||||
Self::TempValue(ref mut r) => r,
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Self::BitField(_, _, ref mut r) => r,
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Self::StringChar(_, _, ref mut r) => r,
|
||||
Self::BitField(_, _, ref mut r)
|
||||
| Self::BlobByte(_, _, ref mut r)
|
||||
| Self::StringChar(_, _, ref mut r) => r,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2059,6 +2082,45 @@ impl Engine {
|
||||
.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"))]
|
||||
Dynamic(Union::Map(map, _, _)) => {
|
||||
// val_map[idx]
|
||||
@ -3203,6 +3265,7 @@ impl Engine {
|
||||
let (a, m, s) = calc_size(value);
|
||||
(arrays + a + 1, maps + m, strings + s)
|
||||
}
|
||||
Union::Blob(ref a, _, _) => (arrays + 1 + a.len(), maps, strings),
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
Union::Map(_, _, _) => {
|
||||
let (a, m, s) = calc_size(value);
|
||||
@ -3212,6 +3275,8 @@ impl Engine {
|
||||
_ => (arrays + 1, maps, strings),
|
||||
})
|
||||
}
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Union::Blob(ref arr, _, _) => (arr.len(), 0, 0),
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
Union::Map(ref map, _, _) => {
|
||||
map.values()
|
||||
@ -3221,6 +3286,8 @@ impl Engine {
|
||||
let (a, m, s) = calc_size(value);
|
||||
(arrays + a, maps + m + 1, strings + s)
|
||||
}
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Union::Blob(ref a, _, _) => (arrays + a.len(), maps, strings),
|
||||
Union::Map(_, _, _) => {
|
||||
let (a, m, s) = calc_size(value);
|
||||
(arrays + a, maps + m + 1, strings + s)
|
||||
|
@ -176,6 +176,11 @@ pub use ast::ScriptFnMetadata;
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
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.
|
||||
/// Not available under `no_object`.
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
|
507
src/packages/blob_basic.rs
Normal file
507
src/packages/blob_basic.rs
Normal 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)
|
||||
}
|
||||
}
|
@ -3,22 +3,29 @@
|
||||
use crate::{Module, Shared};
|
||||
|
||||
pub(crate) mod arithmetic;
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
mod array_basic;
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
mod blob_basic;
|
||||
mod fn_basic;
|
||||
mod iter_basic;
|
||||
mod lang_core;
|
||||
mod logic;
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
mod map_basic;
|
||||
mod math_basic;
|
||||
mod pkg_core;
|
||||
mod pkg_std;
|
||||
mod string_basic;
|
||||
mod string_more;
|
||||
#[cfg(not(feature = "no_std"))]
|
||||
mod time_basic;
|
||||
|
||||
pub use arithmetic::ArithmeticPackage;
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
pub use array_basic::BasicArrayPackage;
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
pub use blob_basic::BasicBlobPackage;
|
||||
pub use fn_basic::BasicFnPackage;
|
||||
pub use iter_basic::BasicIteratorPackage;
|
||||
pub use logic::LogicPackage;
|
||||
|
@ -1,5 +1,7 @@
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
use super::array_basic::BasicArrayPackage;
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
use super::blob_basic::BasicBlobPackage;
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
use super::map_basic::BasicMapPackage;
|
||||
use super::math_basic::BasicMathPackage;
|
||||
@ -18,7 +20,10 @@ def_package!(crate:StandardPackage:"_Standard_ package containing all built-in f
|
||||
CorePackage::init(lib);
|
||||
BasicMathPackage::init(lib);
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
BasicArrayPackage::init(lib);
|
||||
{
|
||||
BasicArrayPackage::init(lib);
|
||||
BasicBlobPackage::init(lib);
|
||||
}
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
BasicMapPackage::init(lib);
|
||||
#[cfg(not(feature = "no_std"))]
|
||||
|
@ -153,6 +153,8 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> {
|
||||
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Union::Array(_, _, _) => self.deserialize_seq(visitor),
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Union::Blob(_, _, _) => self.deserialize_seq(visitor),
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
Union::Map(_, _, _) => self.deserialize_map(visitor),
|
||||
Union::FnPtr(_, _, _) => self.type_error(),
|
||||
|
@ -57,6 +57,8 @@ impl Serialize for Dynamic {
|
||||
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Union::Array(ref a, _, _) => (**a).serialize(ser),
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Union::Blob(ref a, _, _) => (**a).serialize(ser),
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
Union::Map(ref m, _, _) => {
|
||||
let mut map = ser.serialize_map(Some(m.len()))?;
|
||||
|
@ -20,7 +20,7 @@ use crate::{ast::FloatWrapper, FLOAT};
|
||||
use rust_decimal::Decimal;
|
||||
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
use crate::Array;
|
||||
use crate::{Array, Blob};
|
||||
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
use crate::Map;
|
||||
@ -200,6 +200,11 @@ pub enum Union {
|
||||
/// Not available under `no_index`.
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
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.
|
||||
///
|
||||
/// Not available under `no_object`.
|
||||
@ -332,7 +337,7 @@ impl Dynamic {
|
||||
#[cfg(feature = "decimal")]
|
||||
Union::Decimal(_, tag, _) => tag,
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Union::Array(_, tag, _) => tag,
|
||||
Union::Array(_, tag, _) | Union::Blob(_, tag, _) => tag,
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
Union::Map(_, tag, _) => tag,
|
||||
#[cfg(not(feature = "no_std"))]
|
||||
@ -357,7 +362,7 @@ impl Dynamic {
|
||||
#[cfg(feature = "decimal")]
|
||||
Union::Decimal(_, ref mut tag, _) => *tag = value,
|
||||
#[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"))]
|
||||
Union::Map(_, ref mut tag, _) => *tag = value,
|
||||
#[cfg(not(feature = "no_std"))]
|
||||
@ -419,6 +424,8 @@ impl Dynamic {
|
||||
Union::Decimal(_, _, _) => TypeId::of::<Decimal>(),
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Union::Array(_, _, _) => TypeId::of::<Array>(),
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Union::Blob(_, _, _) => TypeId::of::<Blob>(),
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
Union::Map(_, _, _) => TypeId::of::<Map>(),
|
||||
Union::FnPtr(_, _, _) => TypeId::of::<FnPtr>(),
|
||||
@ -456,6 +463,8 @@ impl Dynamic {
|
||||
Union::Decimal(_, _, _) => "decimal",
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Union::Array(_, _, _) => "array",
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Union::Blob(_, _, _) => "blob",
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
Union::Map(_, _, _) => "map",
|
||||
Union::FnPtr(_, _, _) => "Fn",
|
||||
@ -498,6 +507,8 @@ impl Hash for Dynamic {
|
||||
Union::Decimal(ref d, _, _) => d.hash(state),
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
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"))]
|
||||
Union::Map(ref m, _, _) => m.as_ref().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>() {
|
||||
return "array";
|
||||
}
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
if name == type_name::<Blob>() {
|
||||
return "blob";
|
||||
}
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
if name == type_name::<Map>() {
|
||||
return "map";
|
||||
@ -612,6 +627,8 @@ impl fmt::Display for Dynamic {
|
||||
Union::Decimal(ref value, _, _) => fmt::Display::fmt(value, f),
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
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"))]
|
||||
Union::Map(ref value, _, _) => {
|
||||
f.write_str("#")?;
|
||||
@ -692,6 +709,8 @@ impl fmt::Debug for Dynamic {
|
||||
Union::Decimal(ref value, _, _) => fmt::Debug::fmt(value, f),
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
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"))]
|
||||
Union::Map(ref value, _, _) => {
|
||||
f.write_str("#")?;
|
||||
@ -781,6 +800,8 @@ impl Clone for Dynamic {
|
||||
}
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
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"))]
|
||||
Union::Map(ref value, tag, _) => Self(Union::Map(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")]
|
||||
Union::Decimal(_, _, access) => access,
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Union::Array(_, _, access) => access,
|
||||
Union::Array(_, _, access) | Union::Blob(_, _, access) => access,
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
Union::Map(_, _, access) => access,
|
||||
#[cfg(not(feature = "no_std"))]
|
||||
@ -1003,6 +1024,8 @@ impl Dynamic {
|
||||
v.set_access_mode(typ);
|
||||
});
|
||||
}
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Union::Blob(_, _, ref mut access) => *access = typ,
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
Union::Map(ref mut m, _, ref mut access) => {
|
||||
*access = typ;
|
||||
@ -1166,6 +1189,13 @@ impl Dynamic {
|
||||
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"))]
|
||||
{
|
||||
@ -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"))]
|
||||
if TypeId::of::<T>() == TypeId::of::<Map>() {
|
||||
return match self.0 {
|
||||
@ -1660,6 +1698,13 @@ impl Dynamic {
|
||||
_ => 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"))]
|
||||
if TypeId::of::<T>() == TypeId::of::<Map>() {
|
||||
return match self.0 {
|
||||
@ -1757,6 +1802,13 @@ impl Dynamic {
|
||||
_ => 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"))]
|
||||
if TypeId::of::<T>() == TypeId::of::<Map>() {
|
||||
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"))]
|
||||
impl Dynamic {
|
||||
/// Create a [`Dynamic`] from a [`Map`].
|
||||
|
68
tests/blobs.rs
Normal file
68
tests/blobs.rs
Normal 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(())
|
||||
}
|
Loading…
Reference in New Issue
Block a user