| #![cfg(not(feature = "no_index"))] |
| |
| use crate::eval::{calc_index, calc_offset_len}; |
| use crate::module::ModuleFlags; |
| use crate::plugin::*; |
| use crate::{ |
| def_package, Array, Blob, Dynamic, ExclusiveRange, InclusiveRange, NativeCallContext, Position, |
| RhaiResultOf, ERR, INT, INT_BYTES, MAX_USIZE_INT, |
| }; |
| #[cfg(feature = "no_std")] |
| use std::prelude::v1::*; |
| use std::{any::TypeId, borrow::Cow, mem}; |
| |
| #[cfg(not(feature = "no_float"))] |
| use crate::{FLOAT, FLOAT_BYTES}; |
| |
| def_package! { |
| /// Package of basic BLOB utilities. |
| pub BasicBlobPackage(lib) { |
| lib.flags |= ModuleFlags::STANDARD_LIB; |
| |
| combine_with_exported_module!(lib, "blob", blob_functions); |
| combine_with_exported_module!(lib, "parse_int", parse_int_functions); |
| combine_with_exported_module!(lib, "write_int", write_int_functions); |
| combine_with_exported_module!(lib, "write_string", write_string_functions); |
| |
| #[cfg(not(feature = "no_float"))] |
| { |
| combine_with_exported_module!(lib, "parse_float", parse_float_functions); |
| combine_with_exported_module!(lib, "write_float", write_float_functions); |
| } |
| |
| // Register blob iterator |
| lib.set_iterable::<Blob>(); |
| } |
| } |
| |
| #[export_module] |
| pub mod blob_functions { |
| /// Return a new, empty BLOB. |
| pub const fn blob() -> Blob { |
| Blob::new() |
| } |
| /// Return a new BLOB of the specified length, filled with zeros. |
| /// |
| /// If `len` ≤ 0, an empty BLOB is returned. |
| /// |
| /// # Example |
| /// |
| /// ```rhai |
| /// let b = blob(10); |
| /// |
| /// print(b); // prints "[0000000000000000 0000]" |
| /// ``` |
| #[rhai_fn(name = "blob", return_raw)] |
| pub fn blob_with_capacity(ctx: NativeCallContext, len: INT) -> RhaiResultOf<Blob> { |
| blob_with_capacity_and_value(ctx, len, 0) |
| } |
| /// Return a new BLOB of the specified length, filled with copies of the initial `value`. |
| /// |
| /// If `len` ≤ 0, an empty BLOB is returned. |
| /// |
| /// Only the lower 8 bits of the initial `value` are used; all other bits are ignored. |
| /// |
| /// # Example |
| /// |
| /// ```rhai |
| /// let b = blob(10, 0x42); |
| /// |
| /// print(b); // prints "[4242424242424242 4242]" |
| /// ``` |
| #[rhai_fn(name = "blob", return_raw)] |
| pub fn blob_with_capacity_and_value( |
| ctx: NativeCallContext, |
| len: INT, |
| value: INT, |
| ) -> RhaiResultOf<Blob> { |
| #[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)] |
| let len = len.clamp(0, MAX_USIZE_INT) as usize; |
| let _ctx = ctx; |
| |
| // Check if blob will be over max size limit |
| #[cfg(not(feature = "unchecked"))] |
| _ctx.engine().throw_on_size((len, 0, 0))?; |
| |
| let mut blob = Blob::new(); |
| #[allow(clippy::cast_sign_loss)] |
| blob.resize(len, (value & 0x0000_00ff) as u8); |
| Ok(blob) |
| } |
| /// Convert the BLOB into an array of integers. |
| /// |
| /// # Example |
| /// |
| /// ```rhai |
| /// let b = blob(5, 0x42); |
| /// |
| /// let x = b.to_array(); |
| /// |
| /// print(x); // prints "[66, 66, 66, 66, 66]" |
| /// ``` |
| #[rhai_fn(pure)] |
| pub fn to_array(blob: &mut Blob) -> Array { |
| blob.iter().map(|&ch| (ch as INT).into()).collect() |
| } |
| /// Convert the BLOB into a string. |
| /// |
| /// The byte stream must be valid UTF-8, otherwise an error is raised. |
| /// |
| /// # Example |
| /// |
| /// ```rhai |
| /// let b = blob(5, 0x42); |
| /// |
| /// let x = b.as_string(); |
| /// |
| /// print(x); // prints "FFFFF" |
| /// ``` |
| pub fn as_string(blob: Blob) -> String { |
| let s = String::from_utf8_lossy(&blob); |
| |
| match s { |
| Cow::Borrowed(_) => String::from_utf8(blob).unwrap(), |
| Cow::Owned(_) => s.into_owned(), |
| } |
| } |
| /// Return the length of the BLOB. |
| /// |
| /// # Example |
| /// |
| /// ```rhai |
| /// let b = blob(10, 0x42); |
| /// |
| /// print(b); // prints "[4242424242424242 4242]" |
| /// |
| /// print(b.len()); // prints 10 |
| /// ``` |
| #[rhai_fn(name = "len", get = "len", pure)] |
| pub fn len(blob: &mut Blob) -> INT { |
| blob.len() as INT |
| } |
| /// Return true if the BLOB is empty. |
| #[rhai_fn(name = "is_empty", get = "is_empty", pure)] |
| pub fn is_empty(blob: &mut Blob) -> bool { |
| blob.len() == 0 |
| } |
| /// Return `true` if the BLOB contains a specified byte value. |
| /// |
| /// # Example |
| /// |
| /// ```rhai |
| /// let text = "hello, world!"; |
| /// |
| /// print(text.contains('h')); // prints true |
| /// |
| /// print(text.contains('x')); // prints false |
| /// ``` |
| #[rhai_fn(name = "contains")] |
| pub fn contains(blob: &mut Blob, value: INT) -> bool { |
| #[allow(clippy::cast_sign_loss)] |
| blob.contains(&((value & 0x0000_00ff) as u8)) |
| } |
| /// Get the byte value at the `index` position in the BLOB. |
| /// |
| /// * If `index` < 0, position counts from the end of the BLOB (`-1` is the last element). |
| /// * If `index` < -length of BLOB, zero is returned. |
| /// * If `index` ≥ length of BLOB, zero is returned. |
| /// |
| /// # Example |
| /// |
| /// ```rhai |
| /// let b = blob(); |
| /// |
| /// b += 1; b += 2; b += 3; b += 4; b += 5; |
| /// |
| /// print(b.get(0)); // prints 1 |
| /// |
| /// print(b.get(-1)); // prints 5 |
| /// |
| /// print(b.get(99)); // prints 0 |
| /// ``` |
| pub fn get(blob: &mut Blob, index: INT) -> INT { |
| if blob.is_empty() { |
| return 0; |
| } |
| |
| let (index, ..) = calc_offset_len(blob.len(), index, 0); |
| |
| if index >= blob.len() { |
| 0 |
| } else { |
| blob[index] as INT |
| } |
| } |
| /// Set the particular `index` position in the BLOB to a new byte `value`. |
| /// |
| /// * If `index` < 0, position counts from the end of the BLOB (`-1` is the last byte). |
| /// * If `index` < -length of BLOB, the BLOB is not modified. |
| /// * If `index` ≥ length of BLOB, the BLOB is not modified. |
| /// |
| /// # Example |
| /// |
| /// ```rhai |
| /// let b = blob(); |
| /// |
| /// b += 1; b += 2; b += 3; b += 4; b += 5; |
| /// |
| /// b.set(0, 0x42); |
| /// |
| /// print(b); // prints "[4202030405]" |
| /// |
| /// b.set(-3, 0); |
| /// |
| /// print(b); // prints "[4202000405]" |
| /// |
| /// b.set(99, 123); |
| /// |
| /// print(b); // prints "[4202000405]" |
| /// ``` |
| pub fn set(blob: &mut Blob, index: INT, value: INT) { |
| if blob.is_empty() { |
| return; |
| } |
| |
| let (index, ..) = calc_offset_len(blob.len(), index, 0); |
| |
| #[allow(clippy::cast_sign_loss)] |
| if index < blob.len() { |
| blob[index] = (value & 0x0000_00ff) as u8; |
| } |
| } |
| /// Add a new byte `value` to the end of the BLOB. |
| /// |
| /// Only the lower 8 bits of the `value` are used; all other bits are ignored. |
| /// |
| /// # Example |
| /// |
| /// ```rhai |
| /// let b = blob(); |
| /// |
| /// b.push(0x42); |
| /// |
| /// print(b); // prints "[42]" |
| /// ``` |
| #[rhai_fn(name = "push", name = "append")] |
| pub fn push(blob: &mut Blob, value: INT) { |
| #[allow(clippy::cast_sign_loss)] |
| blob.push((value & 0x0000_00ff) as u8); |
| } |
| /// Add another BLOB to the end of the BLOB. |
| /// |
| /// # Example |
| /// |
| /// ```rhai |
| /// let b1 = blob(5, 0x42); |
| /// let b2 = blob(3, 0x11); |
| /// |
| /// b1.push(b2); |
| /// |
| /// print(b1); // prints "[4242424242111111]" |
| /// ``` |
| pub fn append(blob1: &mut Blob, blob2: Blob) { |
| if !blob2.is_empty() { |
| if blob1.is_empty() { |
| *blob1 = blob2; |
| } else { |
| blob1.extend(blob2); |
| } |
| } |
| } |
| /// Add a string (as UTF-8 encoded byte-stream) to the end of the BLOB |
| /// |
| /// # Example |
| /// |
| /// ```rhai |
| /// let b = blob(5, 0x42); |
| /// |
| /// b.append("hello"); |
| /// |
| /// print(b); // prints "[424242424268656c 6c6f]" |
| /// ``` |
| #[rhai_fn(name = "append")] |
| pub fn append_str(blob: &mut Blob, string: &str) { |
| if !string.is_empty() { |
| blob.extend(string.as_bytes()); |
| } |
| } |
| /// Add a character (as UTF-8 encoded byte-stream) to the end of the BLOB |
| /// |
| /// # Example |
| /// |
| /// ```rhai |
| /// let b = blob(5, 0x42); |
| /// |
| /// b.append('!'); |
| /// |
| /// print(b); // prints "[424242424221]" |
| /// ``` |
| #[rhai_fn(name = "append")] |
| pub fn append_char(blob: &mut Blob, character: char) { |
| let mut buf = [0_u8; 4]; |
| let x = character.encode_utf8(&mut buf); |
| blob.extend(x.as_bytes()); |
| } |
| /// Add a byte `value` to the BLOB at a particular `index` position. |
| /// |
| /// * If `index` < 0, position counts from the end of the BLOB (`-1` is the last byte). |
| /// * If `index` < -length of BLOB, the byte value is added to the beginning of the BLOB. |
| /// * If `index` ≥ length of BLOB, the byte value is appended to the end of the BLOB. |
| /// |
| /// Only the lower 8 bits of the `value` are used; all other bits are ignored. |
| /// |
| /// # Example |
| /// |
| /// ```rhai |
| /// let b = blob(5, 0x42); |
| /// |
| /// b.insert(2, 0x18); |
| /// |
| /// print(b); // prints "[4242184242]" |
| /// ``` |
| pub fn insert(blob: &mut Blob, index: INT, value: INT) { |
| #[allow(clippy::cast_sign_loss)] |
| let value = (value & 0x0000_00ff) as u8; |
| |
| if blob.is_empty() { |
| blob.push(value); |
| return; |
| } |
| |
| let (index, ..) = calc_offset_len(blob.len(), index, 0); |
| |
| if index >= blob.len() { |
| blob.push(value); |
| } else { |
| blob.insert(index, value); |
| } |
| } |
| /// Pad the BLOB to at least the specified length with copies of a specified byte `value`. |
| /// |
| /// If `len` ≤ length of BLOB, no padding is done. |
| /// |
| /// Only the lower 8 bits of the `value` are used; all other bits are ignored. |
| /// |
| /// # Example |
| /// |
| /// ```rhai |
| /// let b = blob(3, 0x42); |
| /// |
| /// b.pad(5, 0x18) |
| /// |
| /// print(b); // prints "[4242421818]" |
| /// |
| /// b.pad(3, 0xab) |
| /// |
| /// print(b); // prints "[4242421818]" |
| /// ``` |
| #[rhai_fn(return_raw)] |
| pub fn pad(ctx: NativeCallContext, blob: &mut Blob, len: INT, value: INT) -> RhaiResultOf<()> { |
| if len <= 0 { |
| return Ok(()); |
| } |
| #[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)] |
| let len = len.min(MAX_USIZE_INT) as usize; |
| |
| #[allow(clippy::cast_sign_loss)] |
| let value = (value & 0x0000_00ff) as u8; |
| let _ctx = ctx; |
| |
| // Check if blob will be over max size limit |
| if _ctx.engine().max_array_size() > 0 && len > _ctx.engine().max_array_size() { |
| return Err(ERR::ErrorDataTooLarge("Size of BLOB".to_string(), Position::NONE).into()); |
| } |
| |
| if len > blob.len() { |
| blob.resize(len, value); |
| } |
| |
| Ok(()) |
| } |
| /// Remove the last byte from the BLOB and return it. |
| /// |
| /// If the BLOB is empty, zero is returned. |
| /// |
| /// # Example |
| /// |
| /// ```rhai |
| /// let b = blob(); |
| /// |
| /// b += 1; b += 2; b += 3; b += 4; b += 5; |
| /// |
| /// print(b.pop()); // prints 5 |
| /// |
| /// print(b); // prints "[01020304]" |
| /// ``` |
| pub fn pop(blob: &mut Blob) -> INT { |
| if blob.is_empty() { |
| 0 |
| } else { |
| blob.pop().map_or_else(|| 0, |v| v as INT) |
| } |
| } |
| /// Remove the first byte from the BLOB and return it. |
| /// |
| /// If the BLOB is empty, zero is returned. |
| /// |
| /// # Example |
| /// |
| /// ```rhai |
| /// let b = blob(); |
| /// |
| /// b += 1; b += 2; b += 3; b += 4; b += 5; |
| /// |
| /// print(b.shift()); // prints 1 |
| /// |
| /// print(b); // prints "[02030405]" |
| /// ``` |
| pub fn shift(blob: &mut Blob) -> INT { |
| if blob.is_empty() { |
| 0 |
| } else { |
| blob.remove(0) as INT |
| } |
| } |
| /// Remove the byte at the specified `index` from the BLOB and return it. |
| /// |
| /// * If `index` < 0, position counts from the end of the BLOB (`-1` is the last byte). |
| /// * If `index` < -length of BLOB, zero is returned. |
| /// * If `index` ≥ length of BLOB, zero is returned. |
| /// |
| /// # Example |
| /// |
| /// ```rhai |
| /// let b = blob(); |
| /// |
| /// b += 1; b += 2; b += 3; b += 4; b += 5; |
| /// |
| /// print(x.remove(1)); // prints 2 |
| /// |
| /// print(x); // prints "[01030405]" |
| /// |
| /// print(x.remove(-2)); // prints 4 |
| /// |
| /// print(x); // prints "[010305]" |
| /// ``` |
| pub fn remove(blob: &mut Blob, index: INT) -> INT { |
| let index = match calc_index(blob.len(), index, true, || Err(())) { |
| Ok(n) => n, |
| Err(_) => return 0, |
| }; |
| |
| blob.remove(index) as INT |
| } |
| /// Clear the BLOB. |
| pub fn clear(blob: &mut Blob) { |
| if !blob.is_empty() { |
| blob.clear(); |
| } |
| } |
| /// Cut off the BLOB at the specified length. |
| /// |
| /// * If `len` ≤ 0, the BLOB is cleared. |
| /// * If `len` ≥ length of BLOB, the BLOB is not truncated. |
| /// |
| /// # Example |
| /// |
| /// ```rhai |
| /// let b = blob(); |
| /// |
| /// b += 1; b += 2; b += 3; b += 4; b += 5; |
| /// |
| /// b.truncate(3); |
| /// |
| /// print(b); // prints "[010203]" |
| /// |
| /// b.truncate(10); |
| /// |
| /// print(b); // prints "[010203]" |
| /// ``` |
| pub fn truncate(blob: &mut Blob, len: INT) { |
| if len <= 0 { |
| blob.clear(); |
| return; |
| } |
| if !blob.is_empty() { |
| #[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)] |
| let len = len.min(MAX_USIZE_INT) as usize; |
| |
| if len > 0 { |
| blob.truncate(len); |
| } else { |
| blob.clear(); |
| } |
| } |
| } |
| /// Cut off the head of the BLOB, leaving a tail of the specified length. |
| /// |
| /// * If `len` ≤ 0, the BLOB is cleared. |
| /// * If `len` ≥ length of BLOB, the BLOB is not modified. |
| /// |
| /// # Example |
| /// |
| /// ```rhai |
| /// let b = blob(); |
| /// |
| /// b += 1; b += 2; b += 3; b += 4; b += 5; |
| /// |
| /// b.chop(3); |
| /// |
| /// print(b); // prints "[030405]" |
| /// |
| /// b.chop(10); |
| /// |
| /// print(b); // prints "[030405]" |
| /// ``` |
| #[allow( |
| clippy::cast_sign_loss, |
| clippy::needless_pass_by_value, |
| clippy::cast_possible_truncation |
| )] |
| pub fn chop(blob: &mut Blob, len: INT) { |
| if !blob.is_empty() { |
| if len <= 0 { |
| blob.clear(); |
| } else if len > MAX_USIZE_INT { |
| // len > BLOB length |
| } else if (len as usize) < blob.len() { |
| blob.drain(0..blob.len() - len as usize); |
| } |
| } |
| } |
| /// Reverse the BLOB. |
| /// |
| /// # Example |
| /// |
| /// ```rhai |
| /// let b = blob(); |
| /// |
| /// b += 1; b += 2; b += 3; b += 4; b += 5; |
| /// |
| /// print(b); // prints "[0102030405]" |
| /// |
| /// b.reverse(); |
| /// |
| /// print(b); // prints "[0504030201]" |
| /// ``` |
| pub fn reverse(blob: &mut Blob) { |
| if !blob.is_empty() { |
| blob.reverse(); |
| } |
| } |
| /// Replace an exclusive `range` of the BLOB with another BLOB. |
| /// |
| /// # Example |
| /// |
| /// ```rhai |
| /// let b1 = blob(10, 0x42); |
| /// let b2 = blob(5, 0x18); |
| /// |
| /// b1.splice(1..4, b2); |
| /// |
| /// print(b1); // prints "[4218181818184242 42424242]" |
| /// ``` |
| #[rhai_fn(name = "splice")] |
| pub fn splice_range(blob: &mut Blob, range: ExclusiveRange, replace: Blob) { |
| let start = INT::max(range.start, 0); |
| let end = INT::max(range.end, start); |
| splice(blob, start, end - start, replace); |
| } |
| /// Replace an inclusive `range` of the BLOB with another BLOB. |
| /// |
| /// # Example |
| /// |
| /// ```rhai |
| /// let b1 = blob(10, 0x42); |
| /// let b2 = blob(5, 0x18); |
| /// |
| /// b1.splice(1..=4, b2); |
| /// |
| /// print(b1); // prints "[4218181818184242 424242]" |
| /// ``` |
| #[rhai_fn(name = "splice")] |
| pub fn splice_range_inclusive(blob: &mut Blob, range: InclusiveRange, replace: Blob) { |
| let start = INT::max(*range.start(), 0); |
| let end = INT::max(*range.end(), start); |
| splice(blob, start, end - start + 1, replace); |
| } |
| /// Replace a portion of the BLOB with another BLOB. |
| /// |
| /// * If `start` < 0, position counts from the end of the BLOB (`-1` is the last byte). |
| /// * If `start` < -length of BLOB, position counts from the beginning of the BLOB. |
| /// * If `start` ≥ length of BLOB, the other BLOB is appended to the end of the BLOB. |
| /// * If `len` ≤ 0, the other BLOB is inserted into the BLOB at the `start` position without replacing anything. |
| /// * If `start` position + `len` ≥ length of BLOB, entire portion of the BLOB after the `start` position is replaced. |
| /// |
| /// # Example |
| /// |
| /// ```rhai |
| /// let b1 = blob(10, 0x42); |
| /// let b2 = blob(5, 0x18); |
| /// |
| /// b1.splice(1, 3, b2); |
| /// |
| /// print(b1); // prints "[4218181818184242 42424242]" |
| /// |
| /// b1.splice(-5, 4, b2); |
| /// |
| /// print(b1); // prints "[4218181818184218 1818181842]" |
| /// ``` |
| pub fn splice(blob: &mut Blob, start: INT, len: INT, replace: Blob) { |
| if blob.is_empty() { |
| *blob = replace; |
| return; |
| } |
| |
| let (start, len) = calc_offset_len(blob.len(), start, len); |
| |
| if len == 0 { |
| blob.extend(replace); |
| } else { |
| blob.splice(start..start + len, replace); |
| } |
| } |
| /// Copy an exclusive `range` of the BLOB and return it as a new BLOB. |
| /// |
| /// # Example |
| /// |
| /// ```rhai |
| /// let b = blob(); |
| /// |
| /// b += 1; b += 2; b += 3; b += 4; b += 5; |
| /// |
| /// print(b.extract(1..3)); // prints "[0203]" |
| /// |
| /// print(b); // prints "[0102030405]" |
| /// ``` |
| #[rhai_fn(name = "extract")] |
| pub fn extract_range(blob: &mut Blob, range: ExclusiveRange) -> Blob { |
| let start = INT::max(range.start, 0); |
| let end = INT::max(range.end, start); |
| extract(blob, start, end - start) |
| } |
| /// Copy an inclusive `range` of the BLOB and return it as a new BLOB. |
| /// |
| /// # Example |
| /// |
| /// ```rhai |
| /// let b = blob(); |
| /// |
| /// b += 1; b += 2; b += 3; b += 4; b += 5; |
| /// |
| /// print(b.extract(1..=3)); // prints "[020304]" |
| /// |
| /// print(b); // prints "[0102030405]" |
| /// ``` |
| #[rhai_fn(name = "extract")] |
| pub fn extract_range_inclusive(blob: &mut Blob, range: InclusiveRange) -> Blob { |
| let start = INT::max(*range.start(), 0); |
| let end = INT::max(*range.end(), start); |
| extract(blob, start, end - start + 1) |
| } |
| /// Copy a portion of the BLOB and return it as a new BLOB. |
| /// |
| /// * If `start` < 0, position counts from the end of the BLOB (`-1` is the last byte). |
| /// * If `start` < -length of BLOB, position counts from the beginning of the BLOB. |
| /// * If `start` ≥ length of BLOB, an empty BLOB is returned. |
| /// * If `len` ≤ 0, an empty BLOB is returned. |
| /// * If `start` position + `len` ≥ length of BLOB, entire portion of the BLOB after the `start` position is copied and returned. |
| /// |
| /// # Example |
| /// |
| /// ```rhai |
| /// let b = blob(); |
| /// |
| /// b += 1; b += 2; b += 3; b += 4; b += 5; |
| /// |
| /// print(b.extract(1, 3)); // prints "[020303]" |
| /// |
| /// print(b.extract(-3, 2)); // prints "[0304]" |
| /// |
| /// print(b); // prints "[0102030405]" |
| /// ``` |
| pub fn extract(blob: &mut Blob, start: INT, len: INT) -> Blob { |
| if blob.is_empty() || len <= 0 { |
| return Blob::new(); |
| } |
| |
| let (start, len) = calc_offset_len(blob.len(), start, len); |
| |
| if len == 0 { |
| Blob::new() |
| } else { |
| blob[start..start + len].to_vec() |
| } |
| } |
| /// Copy a portion of the BLOB beginning at the `start` position till the end and return it as |
| /// a new BLOB. |
| /// |
| /// * If `start` < 0, position counts from the end of the BLOB (`-1` is the last byte). |
| /// * If `start` < -length of BLOB, the entire BLOB is copied and returned. |
| /// * If `start` ≥ length of BLOB, an empty BLOB is returned. |
| /// |
| /// # Example |
| /// |
| /// ```rhai |
| /// let b = blob(); |
| /// |
| /// b += 1; b += 2; b += 3; b += 4; b += 5; |
| /// |
| /// print(b.extract(2)); // prints "[030405]" |
| /// |
| /// print(b.extract(-3)); // prints "[030405]" |
| /// |
| /// print(b); // prints "[0102030405]" |
| /// ``` |
| #[rhai_fn(name = "extract")] |
| pub fn extract_tail(blob: &mut Blob, start: INT) -> Blob { |
| extract(blob, start, INT::MAX) |
| } |
| /// Cut off the BLOB at `index` and return it as a new BLOB. |
| /// |
| /// * If `index` < 0, position counts from the end of the BLOB (`-1` is the last byte). |
| /// * If `index` is zero, the entire BLOB is cut and returned. |
| /// * If `index` < -length of BLOB, the entire BLOB is cut and returned. |
| /// * If `index` ≥ length of BLOB, nothing is cut from the BLOB and an empty BLOB is returned. |
| /// |
| /// # Example |
| /// |
| /// ```rhai |
| /// let b1 = blob(); |
| /// |
| /// b1 += 1; b1 += 2; b1 += 3; b1 += 4; b1 += 5; |
| /// |
| /// let b2 = b1.split(2); |
| /// |
| /// print(b2); // prints "[030405]" |
| /// |
| /// print(b1); // prints "[0102]" |
| /// ``` |
| #[rhai_fn(name = "split")] |
| pub fn split_at(blob: &mut Blob, index: INT) -> Blob { |
| if blob.is_empty() { |
| return Blob::new(); |
| } |
| |
| let (index, len) = calc_offset_len(blob.len(), index, INT::MAX); |
| |
| if index == 0 { |
| if len > blob.len() { |
| mem::take(blob) |
| } else { |
| let mut result = Blob::new(); |
| result.extend(blob.drain(blob.len() - len..)); |
| result |
| } |
| } else if index >= blob.len() { |
| Blob::new() |
| } else { |
| let mut result = Blob::new(); |
| result.extend(blob.drain(index as usize..)); |
| result |
| } |
| } |
| /// Remove all bytes in the BLOB within an exclusive `range` and return them as a new BLOB. |
| /// |
| /// # Example |
| /// |
| /// ```rhai |
| /// let b1 = blob(); |
| /// |
| /// b1 += 1; b1 += 2; b1 += 3; b1 += 4; b1 += 5; |
| /// |
| /// let b2 = b1.drain(1..3); |
| /// |
| /// print(b1); // prints "[010405]" |
| /// |
| /// print(b2); // prints "[0203]" |
| /// |
| /// let b3 = b1.drain(2..3); |
| /// |
| /// print(b1); // prints "[0104]" |
| /// |
| /// print(b3); // prints "[05]" |
| /// ``` |
| #[rhai_fn(name = "drain")] |
| pub fn drain_range(blob: &mut Blob, range: ExclusiveRange) -> Blob { |
| let start = INT::max(range.start, 0); |
| let end = INT::max(range.end, start); |
| drain(blob, start, end - start) |
| } |
| /// Remove all bytes in the BLOB within an inclusive `range` and return them as a new BLOB. |
| /// |
| /// # Example |
| /// |
| /// ```rhai |
| /// let b1 = blob(); |
| /// |
| /// b1 += 1; b1 += 2; b1 += 3; b1 += 4; b1 += 5; |
| /// |
| /// let b2 = b1.drain(1..=2); |
| /// |
| /// print(b1); // prints "[010405]" |
| /// |
| /// print(b2); // prints "[0203]" |
| /// |
| /// let b3 = b1.drain(2..=2); |
| /// |
| /// print(b1); // prints "[0104]" |
| /// |
| /// print(b3); // prints "[05]" |
| /// ``` |
| #[rhai_fn(name = "drain")] |
| pub fn drain_range_inclusive(blob: &mut Blob, range: InclusiveRange) -> Blob { |
| let start = INT::max(*range.start(), 0); |
| let end = INT::max(*range.end(), start); |
| drain(blob, start, end - start + 1) |
| } |
| /// Remove all bytes within a portion of the BLOB and return them as a new BLOB. |
| /// |
| /// * If `start` < 0, position counts from the end of the BLOB (`-1` is the last byte). |
| /// * If `start` < -length of BLOB, position counts from the beginning of the BLOB. |
| /// * If `start` ≥ length of BLOB, nothing is removed and an empty BLOB is returned. |
| /// * If `len` ≤ 0, nothing is removed and an empty BLOB is returned. |
| /// * If `start` position + `len` ≥ length of BLOB, entire portion of the BLOB after the `start` position is removed and returned. |
| /// |
| /// # Example |
| /// |
| /// ```rhai |
| /// let b1 = blob(); |
| /// |
| /// b1 += 1; b1 += 2; b1 += 3; b1 += 4; b1 += 5; |
| /// |
| /// let b2 = b1.drain(1, 2); |
| /// |
| /// print(b1); // prints "[010405]" |
| /// |
| /// print(b2); // prints "[0203]" |
| /// |
| /// let b3 = b1.drain(-1, 1); |
| /// |
| /// print(b3); // prints "[0104]" |
| /// |
| /// print(z); // prints "[5]" |
| /// ``` |
| pub fn drain(blob: &mut Blob, start: INT, len: INT) -> Blob { |
| if blob.is_empty() || len <= 0 { |
| return Blob::new(); |
| } |
| |
| let (start, len) = calc_offset_len(blob.len(), start, len); |
| |
| if len == 0 { |
| Blob::new() |
| } else { |
| blob.drain(start..start + len).collect() |
| } |
| } |
| /// Remove all bytes in the BLOB not within an exclusive `range` and return them as a new BLOB. |
| /// |
| /// # Example |
| /// |
| /// ```rhai |
| /// let b1 = blob(); |
| /// |
| /// b1 += 1; b1 += 2; b1 += 3; b1 += 4; b1 += 5; |
| /// |
| /// let b2 = b1.retain(1..4); |
| /// |
| /// print(b1); // prints "[020304]" |
| /// |
| /// print(b2); // prints "[0105]" |
| /// |
| /// let b3 = b1.retain(1..3); |
| /// |
| /// print(b1); // prints "[0304]" |
| /// |
| /// print(b2); // prints "[01]" |
| /// ``` |
| #[rhai_fn(name = "retain")] |
| pub fn retain_range(blob: &mut Blob, range: ExclusiveRange) -> Blob { |
| let start = INT::max(range.start, 0); |
| let end = INT::max(range.end, start); |
| retain(blob, start, end - start) |
| } |
| /// Remove all bytes in the BLOB not within an inclusive `range` and return them as a new BLOB. |
| /// |
| /// # Example |
| /// |
| /// ```rhai |
| /// let b1 = blob(); |
| /// |
| /// b1 += 1; b1 += 2; b1 += 3; b1 += 4; b1 += 5; |
| /// |
| /// let b2 = b1.retain(1..=3); |
| /// |
| /// print(b1); // prints "[020304]" |
| /// |
| /// print(b2); // prints "[0105]" |
| /// |
| /// let b3 = b1.retain(1..=2); |
| /// |
| /// print(b1); // prints "[0304]" |
| /// |
| /// print(b2); // prints "[01]" |
| /// ``` |
| #[rhai_fn(name = "retain")] |
| pub fn retain_range_inclusive(blob: &mut Blob, range: InclusiveRange) -> Blob { |
| let start = INT::max(*range.start(), 0); |
| let end = INT::max(*range.end(), start); |
| retain(blob, start, end - start + 1) |
| } |
| /// Remove all bytes not within a portion of the BLOB and return them as a new BLOB. |
| /// |
| /// * If `start` < 0, position counts from the end of the BLOB (`-1` is the last byte). |
| /// * If `start` < -length of BLOB, position counts from the beginning of the BLOB. |
| /// * If `start` ≥ length of BLOB, all elements are removed returned. |
| /// * If `len` ≤ 0, all elements are removed and returned. |
| /// * If `start` position + `len` ≥ length of BLOB, entire portion of the BLOB before the `start` position is removed and returned. |
| /// |
| /// # Example |
| /// |
| /// ```rhai |
| /// let b1 = blob(); |
| /// |
| /// b1 += 1; b1 += 2; b1 += 3; b1 += 4; b1 += 5; |
| /// |
| /// let b2 = b1.retain(1, 2); |
| /// |
| /// print(b1); // prints "[0203]" |
| /// |
| /// print(b2); // prints "[010405]" |
| /// |
| /// let b3 = b1.retain(-1, 1); |
| /// |
| /// print(b1); // prints "[03]" |
| /// |
| /// print(b3); // prints "[02]" |
| /// ``` |
| pub fn retain(blob: &mut Blob, start: INT, len: INT) -> Blob { |
| if blob.is_empty() || len <= 0 { |
| return Blob::new(); |
| } |
| |
| let (start, len) = calc_offset_len(blob.len(), start, len); |
| |
| if len == 0 { |
| mem::take(blob) |
| } else { |
| let mut drained: Blob = blob.drain(..start).collect(); |
| drained.extend(blob.drain(len..)); |
| |
| drained |
| } |
| } |
| } |
| |
| #[export_module] |
| mod parse_int_functions { |
| #[inline] |
| fn parse_int(blob: &mut Blob, start: INT, len: INT, is_le: bool) -> INT { |
| if blob.is_empty() || len <= 0 { |
| return 0; |
| } |
| let (start, len) = calc_offset_len(blob.len(), start, len); |
| |
| if len == 0 { |
| return 0; |
| } |
| |
| 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) |
| } |
| } |
| |
| /// Parse the bytes within an exclusive `range` in the BLOB as an `INT` |
| /// in little-endian byte order. |
| /// |
| /// * If number of bytes in `range` < number of bytes for `INT`, zeros are padded. |
| /// * If number of bytes in `range` > number of bytes for `INT`, extra bytes are ignored. |
| /// |
| /// ```rhai |
| /// let b = blob(); |
| /// |
| /// b += 1; b += 2; b += 3; b += 4; b += 5; |
| /// |
| /// let x = b.parse_le_int(1..3); // parse two bytes |
| /// |
| /// print(x.to_hex()); // prints "0302" |
| /// ``` |
| #[rhai_fn(name = "parse_le_int")] |
| pub fn parse_le_int_range(blob: &mut Blob, range: ExclusiveRange) -> INT { |
| let start = INT::max(range.start, 0); |
| let end = INT::max(range.end, start); |
| parse_le_int(blob, start, end - start) |
| } |
| /// Parse the bytes within an inclusive `range` in the BLOB as an `INT` |
| /// in little-endian byte order. |
| /// |
| /// * If number of bytes in `range` < number of bytes for `INT`, zeros are padded. |
| /// * If number of bytes in `range` > number of bytes for `INT`, extra bytes are ignored. |
| /// |
| /// ```rhai |
| /// let b = blob(); |
| /// |
| /// b += 1; b += 2; b += 3; b += 4; b += 5; |
| /// |
| /// let x = b.parse_le_int(1..=3); // parse three bytes |
| /// |
| /// print(x.to_hex()); // prints "040302" |
| /// ``` |
| #[rhai_fn(name = "parse_le_int")] |
| pub fn parse_le_int_range_inclusive(blob: &mut Blob, range: InclusiveRange) -> INT { |
| let start = INT::max(*range.start(), 0); |
| let end = INT::max(*range.end(), start); |
| parse_le_int(blob, start, end - start + 1) |
| } |
| /// Parse the bytes beginning at the `start` position in the BLOB as an `INT` |
| /// in little-endian byte order. |
| /// |
| /// * If `start` < 0, position counts from the end of the BLOB (`-1` is the last byte). |
| /// * If `start` < -length of BLOB, position counts from the beginning of the BLOB. |
| /// * If `start` ≥ length of BLOB, zero is returned. |
| /// * If `len` ≤ 0, zero is returned. |
| /// * If `start` position + `len` ≥ length of BLOB, entire portion of the BLOB after the `start` position is parsed. |
| /// |
| /// * If number of bytes in range < number of bytes for `INT`, zeros are padded. |
| /// * If number of bytes in range > number of bytes for `INT`, extra bytes are ignored. |
| /// |
| /// ```rhai |
| /// let b = blob(); |
| /// |
| /// b += 1; b += 2; b += 3; b += 4; b += 5; |
| /// |
| /// let x = b.parse_le_int(1, 2); |
| /// |
| /// print(x.to_hex()); // prints "0302" |
| /// ``` |
| pub fn parse_le_int(blob: &mut Blob, start: INT, len: INT) -> INT { |
| parse_int(blob, start, len, true) |
| } |
| /// Parse the bytes within an exclusive `range` in the BLOB as an `INT` |
| /// in big-endian byte order. |
| /// |
| /// * If number of bytes in `range` < number of bytes for `INT`, zeros are padded. |
| /// * If number of bytes in `range` > number of bytes for `INT`, extra bytes are ignored. |
| /// |
| /// ```rhai |
| /// let b = blob(); |
| /// |
| /// b += 1; b += 2; b += 3; b += 4; b += 5; |
| /// |
| /// let x = b.parse_be_int(1..3); // parse two bytes |
| /// |
| /// print(x.to_hex()); // prints "02030000...00" |
| /// ``` |
| #[rhai_fn(name = "parse_be_int")] |
| pub fn parse_be_int_range(blob: &mut Blob, range: ExclusiveRange) -> INT { |
| let start = INT::max(range.start, 0); |
| let end = INT::max(range.end, start); |
| parse_be_int(blob, start, end - start) |
| } |
| /// Parse the bytes within an inclusive `range` in the BLOB as an `INT` |
| /// in big-endian byte order. |
| /// |
| /// * If number of bytes in `range` < number of bytes for `INT`, zeros are padded. |
| /// * If number of bytes in `range` > number of bytes for `INT`, extra bytes are ignored. |
| /// |
| /// ```rhai |
| /// let b = blob(); |
| /// |
| /// b += 1; b += 2; b += 3; b += 4; b += 5; |
| /// |
| /// let x = b.parse_be_int(1..=3); // parse three bytes |
| /// |
| /// print(x.to_hex()); // prints "0203040000...00" |
| /// ``` |
| #[rhai_fn(name = "parse_be_int")] |
| pub fn parse_be_int_range_inclusive(blob: &mut Blob, range: InclusiveRange) -> INT { |
| let start = INT::max(*range.start(), 0); |
| let end = INT::max(*range.end(), start); |
| parse_be_int(blob, start, end - start + 1) |
| } |
| /// Parse the bytes beginning at the `start` position in the BLOB as an `INT` |
| /// in big-endian byte order. |
| /// |
| /// * If `start` < 0, position counts from the end of the BLOB (`-1` is the last byte). |
| /// * If `start` < -length of BLOB, position counts from the beginning of the BLOB. |
| /// * If `start` ≥ length of BLOB, zero is returned. |
| /// * If `len` ≤ 0, zero is returned. |
| /// * If `start` position + `len` ≥ length of BLOB, entire portion of the BLOB after the `start` position is parsed. |
| /// |
| /// * If number of bytes in range < number of bytes for `INT`, zeros are padded. |
| /// * If number of bytes in range > number of bytes for `INT`, extra bytes are ignored. |
| /// |
| /// ```rhai |
| /// let b = blob(); |
| /// |
| /// b += 1; b += 2; b += 3; b += 4; b += 5; |
| /// |
| /// let x = b.parse_be_int(1, 2); |
| /// |
| /// print(x.to_hex()); // prints "02030000...00" |
| /// ``` |
| pub fn parse_be_int(blob: &mut Blob, start: INT, len: INT) -> INT { |
| parse_int(blob, start, len, false) |
| } |
| } |
| |
| #[cfg(not(feature = "no_float"))] |
| #[export_module] |
| mod parse_float_functions { |
| #[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 (start, len) = calc_offset_len(blob.len(), start, len); |
| |
| if len == 0 { |
| return 0.0; |
| } |
| |
| 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) |
| } |
| } |
| |
| /// Parse the bytes within an exclusive `range` in the BLOB as a `FLOAT` |
| /// in little-endian byte order. |
| /// |
| /// * If number of bytes in `range` < number of bytes for `FLOAT`, zeros are padded. |
| /// * If number of bytes in `range` > number of bytes for `FLOAT`, extra bytes are ignored. |
| #[rhai_fn(name = "parse_le_float")] |
| pub fn parse_le_float_range(blob: &mut Blob, range: ExclusiveRange) -> FLOAT { |
| let start = INT::max(range.start, 0); |
| let end = INT::max(range.end, start); |
| parse_le_float(blob, start, end - start) |
| } |
| /// Parse the bytes within an inclusive `range` in the BLOB as a `FLOAT` |
| /// in little-endian byte order. |
| /// |
| /// * If number of bytes in `range` < number of bytes for `FLOAT`, zeros are padded. |
| /// * If number of bytes in `range` > number of bytes for `FLOAT`, extra bytes are ignored. |
| #[rhai_fn(name = "parse_le_float")] |
| pub fn parse_le_float_range_inclusive(blob: &mut Blob, range: InclusiveRange) -> FLOAT { |
| let start = INT::max(*range.start(), 0); |
| let end = INT::max(*range.end(), start); |
| parse_le_float(blob, start, end - start + 1) |
| } |
| /// Parse the bytes beginning at the `start` position in the BLOB as a `FLOAT` |
| /// in little-endian byte order. |
| /// |
| /// * If `start` < 0, position counts from the end of the BLOB (`-1` is the last byte). |
| /// * If `start` < -length of BLOB, position counts from the beginning of the BLOB. |
| /// * If `start` ≥ length of BLOB, zero is returned. |
| /// * If `len` ≤ 0, zero is returned. |
| /// * If `start` position + `len` ≥ length of BLOB, entire portion of the BLOB after the `start` position is parsed. |
| /// |
| /// * If number of bytes in range < number of bytes for `FLOAT`, zeros are padded. |
| /// * If number of bytes in range > number of bytes for `FLOAT`, extra bytes are ignored. |
| pub fn parse_le_float(blob: &mut Blob, start: INT, len: INT) -> FLOAT { |
| parse_float(blob, start, len, true) |
| } |
| /// Parse the bytes within an exclusive `range` in the BLOB as a `FLOAT` |
| /// in big-endian byte order. |
| /// |
| /// * If number of bytes in `range` < number of bytes for `FLOAT`, zeros are padded. |
| /// * If number of bytes in `range` > number of bytes for `FLOAT`, extra bytes are ignored. |
| #[rhai_fn(name = "parse_be_float")] |
| pub fn parse_be_float_range(blob: &mut Blob, range: ExclusiveRange) -> FLOAT { |
| let start = INT::max(range.start, 0); |
| let end = INT::max(range.end, start); |
| parse_be_float(blob, start, end - start) |
| } |
| /// Parse the bytes within an inclusive `range` in the BLOB as a `FLOAT` |
| /// in big-endian byte order. |
| /// |
| /// * If number of bytes in `range` < number of bytes for `FLOAT`, zeros are padded. |
| /// * If number of bytes in `range` > number of bytes for `FLOAT`, extra bytes are ignored. |
| #[rhai_fn(name = "parse_be_float")] |
| pub fn parse_be_float_range_inclusive(blob: &mut Blob, range: InclusiveRange) -> FLOAT { |
| let start = INT::max(*range.start(), 0); |
| let end = INT::max(*range.end(), start); |
| parse_be_float(blob, start, end - start + 1) |
| } |
| /// Parse the bytes beginning at the `start` position in the BLOB as a `FLOAT` |
| /// in big-endian byte order. |
| /// |
| /// * If `start` < 0, position counts from the end of the BLOB (`-1` is the last byte). |
| /// * If `start` < -length of BLOB, position counts from the beginning of the BLOB. |
| /// * If `start` ≥ length of BLOB, zero is returned. |
| /// * If `len` ≤ 0, zero is returned. |
| /// * If `start` position + `len` ≥ length of BLOB, entire portion of the BLOB after the `start` position is parsed. |
| /// |
| /// * If number of bytes in range < number of bytes for `FLOAT`, zeros are padded. |
| /// * If number of bytes in range > number of bytes for `FLOAT`, extra bytes are ignored. |
| pub fn parse_be_float(blob: &mut Blob, start: INT, len: INT) -> FLOAT { |
| parse_float(blob, start, len, false) |
| } |
| } |
| |
| #[export_module] |
| mod write_int_functions { |
| #[inline] |
| fn write_int(blob: &mut Blob, start: INT, len: INT, value: INT, is_le: bool) { |
| if blob.is_empty() || len <= 0 { |
| return; |
| } |
| |
| let (start, len) = calc_offset_len(blob.len(), start, len); |
| |
| if len == 0 { |
| return; |
| } |
| |
| let len = usize::min(len, INT_BYTES); |
| |
| let buf = if is_le { |
| value.to_le_bytes() |
| } else { |
| value.to_be_bytes() |
| }; |
| |
| blob[start..][..len].copy_from_slice(&buf[..len]); |
| } |
| /// Write an `INT` value to the bytes within an exclusive `range` in the BLOB |
| /// in little-endian byte order. |
| /// |
| /// * If number of bytes in `range` < number of bytes for `INT`, extra bytes in `INT` are not written. |
| /// * If number of bytes in `range` > number of bytes for `INT`, extra bytes in `range` are not modified. |
| /// |
| /// ```rhai |
| /// let b = blob(8); |
| /// |
| /// b.write_le_int(1..3, 0x12345678); |
| /// |
| /// print(b); // prints "[0078560000000000]" |
| /// ``` |
| #[rhai_fn(name = "write_le")] |
| pub fn write_le_int_range(blob: &mut Blob, range: ExclusiveRange, value: INT) { |
| let start = INT::max(range.start, 0); |
| let end = INT::max(range.end, start); |
| write_le_int(blob, start, end - start, value); |
| } |
| /// Write an `INT` value to the bytes within an inclusive `range` in the BLOB |
| /// in little-endian byte order. |
| /// |
| /// * If number of bytes in `range` < number of bytes for `INT`, extra bytes in `INT` are not written. |
| /// * If number of bytes in `range` > number of bytes for `INT`, extra bytes in `range` are not modified. |
| /// |
| /// ```rhai |
| /// let b = blob(8); |
| /// |
| /// b.write_le_int(1..=3, 0x12345678); |
| /// |
| /// print(b); // prints "[0078563400000000]" |
| /// ``` |
| #[rhai_fn(name = "write_le")] |
| pub fn write_le_int_range_inclusive(blob: &mut Blob, range: InclusiveRange, value: INT) { |
| let start = INT::max(*range.start(), 0); |
| let end = INT::max(*range.end(), start); |
| write_le_int(blob, start, end - start + 1, value); |
| } |
| /// Write an `INT` value to the bytes beginning at the `start` position in the BLOB |
| /// in little-endian byte order. |
| /// |
| /// * If `start` < 0, position counts from the end of the BLOB (`-1` is the last byte). |
| /// * If `start` < -length of BLOB, position counts from the beginning of the BLOB. |
| /// * If `start` ≥ length of BLOB, zero is returned. |
| /// * If `len` ≤ 0, zero is returned. |
| /// * If `start` position + `len` ≥ length of BLOB, entire portion of the BLOB after the `start` position is parsed. |
| /// |
| /// * If number of bytes in `range` < number of bytes for `INT`, extra bytes in `INT` are not written. |
| /// * If number of bytes in `range` > number of bytes for `INT`, extra bytes in `range` are not modified. |
| /// |
| /// ```rhai |
| /// let b = blob(8); |
| /// |
| /// b.write_le_int(1, 3, 0x12345678); |
| /// |
| /// print(b); // prints "[0078563400000000]" |
| /// ``` |
| #[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); |
| } |
| /// Write an `INT` value to the bytes within an exclusive `range` in the BLOB |
| /// in big-endian byte order. |
| /// |
| /// * If number of bytes in `range` < number of bytes for `INT`, extra bytes in `INT` are not written. |
| /// * If number of bytes in `range` > number of bytes for `INT`, extra bytes in `range` are not modified. |
| /// |
| /// ```rhai |
| /// let b = blob(8, 0x42); |
| /// |
| /// b.write_be_int(1..3, 0x99); |
| /// |
| /// print(b); // prints "[4200004242424242]" |
| /// ``` |
| #[rhai_fn(name = "write_be")] |
| pub fn write_be_int_range(blob: &mut Blob, range: ExclusiveRange, value: INT) { |
| let start = INT::max(range.start, 0); |
| let end = INT::max(range.end, start); |
| write_be_int(blob, start, end - start, value); |
| } |
| /// Write an `INT` value to the bytes within an inclusive `range` in the BLOB |
| /// in big-endian byte order. |
| /// |
| /// * If number of bytes in `range` < number of bytes for `INT`, extra bytes in `INT` are not written. |
| /// * If number of bytes in `range` > number of bytes for `INT`, extra bytes in `range` are not modified. |
| /// |
| /// ```rhai |
| /// let b = blob(8, 0x42); |
| /// |
| /// b.write_be_int(1..=3, 0x99); |
| /// |
| /// print(b); // prints "[4200000042424242]" |
| /// ``` |
| #[rhai_fn(name = "write_be")] |
| pub fn write_be_int_range_inclusive(blob: &mut Blob, range: InclusiveRange, value: INT) { |
| let start = INT::max(*range.start(), 0); |
| let end = INT::max(*range.end(), start); |
| write_be_int(blob, start, end - start + 1, value); |
| } |
| /// Write an `INT` value to the bytes beginning at the `start` position in the BLOB |
| /// in big-endian byte order. |
| /// |
| /// * If `start` < 0, position counts from the end of the BLOB (`-1` is the last byte). |
| /// * If `start` < -length of BLOB, position counts from the beginning of the BLOB. |
| /// * If `start` ≥ length of BLOB, zero is returned. |
| /// * If `len` ≤ 0, zero is returned. |
| /// * If `start` position + `len` ≥ length of BLOB, entire portion of the BLOB after the `start` position is parsed. |
| /// |
| /// * If number of bytes in `range` < number of bytes for `INT`, extra bytes in `INT` are not written. |
| /// * If number of bytes in `range` > number of bytes for `INT`, extra bytes in `range` are not modified. |
| /// |
| /// ```rhai |
| /// let b = blob(8, 0x42); |
| /// |
| /// b.write_be_int(1, 3, 0x99); |
| /// |
| /// print(b); // prints "[4200000042424242]" |
| /// ``` |
| #[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"))] |
| #[export_module] |
| mod write_float_functions { |
| #[inline] |
| fn write_float(blob: &mut Blob, start: INT, len: INT, value: FLOAT, is_le: bool) { |
| if blob.is_empty() || len <= 0 { |
| return; |
| } |
| |
| let (start, len) = calc_offset_len(blob.len(), start, len); |
| |
| if len == 0 { |
| return; |
| } |
| |
| let len = usize::min(len, FLOAT_BYTES); |
| let buf = if is_le { |
| value.to_le_bytes() |
| } else { |
| value.to_be_bytes() |
| }; |
| |
| blob[start..][..len].copy_from_slice(&buf[..len]); |
| } |
| /// Write a `FLOAT` value to the bytes within an exclusive `range` in the BLOB |
| /// in little-endian byte order. |
| /// |
| /// * If number of bytes in `range` < number of bytes for `FLOAT`, extra bytes in `FLOAT` are not written. |
| /// * If number of bytes in `range` > number of bytes for `FLOAT`, extra bytes in `range` are not modified. |
| #[rhai_fn(name = "write_le")] |
| pub fn write_le_float_range(blob: &mut Blob, range: ExclusiveRange, value: FLOAT) { |
| let start = INT::max(range.start, 0); |
| let end = INT::max(range.end, start); |
| write_le_float(blob, start, end - start, value); |
| } |
| /// Write a `FLOAT` value to the bytes within an inclusive `range` in the BLOB |
| /// in little-endian byte order. |
| /// |
| /// * If number of bytes in `range` < number of bytes for `FLOAT`, extra bytes in `FLOAT` are not written. |
| /// * If number of bytes in `range` > number of bytes for `FLOAT`, extra bytes in `range` are not modified. |
| #[rhai_fn(name = "write_le")] |
| pub fn write_le_float_range_inclusive(blob: &mut Blob, range: InclusiveRange, value: FLOAT) { |
| let start = INT::max(*range.start(), 0); |
| let end = INT::max(*range.end(), start); |
| write_le_float(blob, start, end - start + 1, value); |
| } |
| /// Write a `FLOAT` value to the bytes beginning at the `start` position in the BLOB |
| /// in little-endian byte order. |
| /// |
| /// * If `start` < 0, position counts from the end of the BLOB (`-1` is the last byte). |
| /// * If `start` < -length of BLOB, position counts from the beginning of the BLOB. |
| /// * If `start` ≥ length of BLOB, zero is returned. |
| /// * If `len` ≤ 0, zero is returned. |
| /// * If `start` position + `len` ≥ length of BLOB, entire portion of the BLOB after the `start` position is parsed. |
| /// |
| /// * If number of bytes in `range` < number of bytes for `FLOAT`, extra bytes in `FLOAT` are not written. |
| /// * If number of bytes in `range` > number of bytes for `FLOAT`, extra bytes in `range` are not modified. |
| #[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); |
| } |
| /// Write a `FLOAT` value to the bytes within an exclusive `range` in the BLOB |
| /// in big-endian byte order. |
| /// |
| /// * If number of bytes in `range` < number of bytes for `FLOAT`, extra bytes in `FLOAT` are not written. |
| /// * If number of bytes in `range` > number of bytes for `FLOAT`, extra bytes in `range` are not modified. |
| #[rhai_fn(name = "write_be")] |
| pub fn write_be_float_range(blob: &mut Blob, range: ExclusiveRange, value: FLOAT) { |
| let start = INT::max(range.start, 0); |
| let end = INT::max(range.end, start); |
| write_be_float(blob, start, end - start, value); |
| } |
| /// Write a `FLOAT` value to the bytes within an inclusive `range` in the BLOB |
| /// in big-endian byte order. |
| /// |
| /// * If number of bytes in `range` < number of bytes for `FLOAT`, extra bytes in `FLOAT` are not written. |
| /// * If number of bytes in `range` > number of bytes for `FLOAT`, extra bytes in `range` are not modified. |
| #[rhai_fn(name = "write_be")] |
| pub fn write_be_float_range_inclusive(blob: &mut Blob, range: InclusiveRange, value: FLOAT) { |
| let start = INT::max(*range.start(), 0); |
| let end = INT::max(*range.end(), start); |
| write_be_float(blob, start, end - start + 1, value); |
| } |
| /// Write a `FLOAT` value to the bytes beginning at the `start` position in the BLOB |
| /// in big-endian byte order. |
| /// |
| /// * If `start` < 0, position counts from the end of the BLOB (`-1` is the last byte). |
| /// * If `start` < -length of BLOB, position counts from the beginning of the BLOB. |
| /// * If `start` ≥ length of BLOB, zero is returned. |
| /// * If `len` ≤ 0, zero is returned. |
| /// * If `start` position + `len` ≥ length of BLOB, entire portion of the BLOB after the `start` position is parsed. |
| /// |
| /// * If number of bytes in `range` < number of bytes for `FLOAT`, extra bytes in `FLOAT` are not written. |
| /// * If number of bytes in `range` > number of bytes for `FLOAT`, extra bytes in `range` are not modified. |
| #[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); |
| } |
| } |
| |
| #[export_module] |
| mod write_string_functions { |
| #[inline] |
| fn write_string(blob: &mut Blob, start: INT, len: INT, string: &str, ascii_only: bool) { |
| if len <= 0 || blob.is_empty() || string.is_empty() { |
| return; |
| } |
| |
| let (start, len) = calc_offset_len(blob.len(), start, len); |
| |
| if len == 0 { |
| return; |
| } |
| |
| let len = usize::min(len, string.len()); |
| |
| if ascii_only { |
| string |
| .chars() |
| .filter(char::is_ascii) |
| .take(len) |
| .map(|ch| ch as u8) |
| .enumerate() |
| .for_each(|(i, x)| blob[start + i] = x); |
| } else { |
| blob[start..][..len].copy_from_slice(&string.as_bytes()[..len]); |
| } |
| } |
| /// Write a string to the bytes within an exclusive `range` in the BLOB in UTF-8 encoding. |
| /// |
| /// * If number of bytes in `range` < length of `string`, extra bytes in `string` are not written. |
| /// * If number of bytes in `range` > length of `string`, extra bytes in `range` are not modified. |
| /// |
| /// ```rhai |
| /// let b = blob(8); |
| /// |
| /// b.write_utf8(1..5, "朝には紅顔ありて夕べには白骨となる"); |
| /// |
| /// print(b); // prints "[00e69c9de3000000]" |
| /// ``` |
| #[rhai_fn(name = "write_utf8")] |
| pub fn write_utf8_string_range(blob: &mut Blob, range: ExclusiveRange, string: &str) { |
| let start = INT::max(range.start, 0); |
| let end = INT::max(range.end, start); |
| write_string(blob, start, end - start, string, false); |
| } |
| /// Write a string to the bytes within an inclusive `range` in the BLOB in UTF-8 encoding. |
| /// |
| /// * If number of bytes in `range` < length of `string`, extra bytes in `string` are not written. |
| /// * If number of bytes in `range` > length of `string`, extra bytes in `range` are not modified. |
| /// |
| /// ```rhai |
| /// let b = blob(8); |
| /// |
| /// b.write_utf8(1..=5, "朝には紅顔ありて夕べには白骨となる"); |
| /// |
| /// print(b); // prints "[00e69c9de3810000]" |
| /// ``` |
| #[rhai_fn(name = "write_utf8")] |
| pub fn write_utf8_string_range_inclusive(blob: &mut Blob, range: InclusiveRange, string: &str) { |
| let start = INT::max(*range.start(), 0); |
| let end = INT::max(*range.end(), start); |
| write_string(blob, start, end - start + 1, string, false); |
| } |
| /// Write a string to the bytes within an inclusive `range` in the BLOB in UTF-8 encoding. |
| /// |
| /// * If `start` < 0, position counts from the end of the BLOB (`-1` is the last byte). |
| /// * If `start` < -length of BLOB, position counts from the beginning of the BLOB. |
| /// * If `start` ≥ length of BLOB, the BLOB is not modified. |
| /// * If `len` ≤ 0, the BLOB is not modified. |
| /// * If `start` position + `len` ≥ length of BLOB, only the portion of the BLOB after the `start` position is modified. |
| /// |
| /// * If number of bytes in `range` < length of `string`, extra bytes in `string` are not written. |
| /// * If number of bytes in `range` > length of `string`, extra bytes in `range` are not modified. |
| /// |
| /// ```rhai |
| /// let b = blob(8); |
| /// |
| /// b.write_utf8(1, 5, "朝には紅顔ありて夕べには白骨となる"); |
| /// |
| /// print(b); // prints "[00e69c9de3810000]" |
| /// ``` |
| #[rhai_fn(name = "write_utf8")] |
| pub fn write_utf8_string(blob: &mut Blob, start: INT, len: INT, string: &str) { |
| write_string(blob, start, len, string, false); |
| } |
| /// Write an ASCII string to the bytes within an exclusive `range` in the BLOB. |
| /// |
| /// Each ASCII character encodes to one single byte in the BLOB. |
| /// Non-ASCII characters are ignored. |
| /// |
| /// * If number of bytes in `range` < length of `string`, extra bytes in `string` are not written. |
| /// * If number of bytes in `range` > length of `string`, extra bytes in `range` are not modified. |
| /// |
| /// ```rhai |
| /// let b = blob(8); |
| /// |
| /// b.write_ascii(1..5, "hello, world!"); |
| /// |
| /// print(b); // prints "[0068656c6c000000]" |
| /// ``` |
| #[rhai_fn(name = "write_ascii")] |
| pub fn write_ascii_string_range(blob: &mut Blob, range: ExclusiveRange, string: &str) { |
| let start = INT::max(range.start, 0); |
| let end = INT::max(range.end, start); |
| write_string(blob, start, end - start, string, true); |
| } |
| /// Write an ASCII string to the bytes within an inclusive `range` in the BLOB. |
| /// |
| /// Each ASCII character encodes to one single byte in the BLOB. |
| /// Non-ASCII characters are ignored. |
| /// |
| /// * If number of bytes in `range` < length of `string`, extra bytes in `string` are not written. |
| /// * If number of bytes in `range` > length of `string`, extra bytes in `range` are not modified. |
| /// |
| /// ```rhai |
| /// let b = blob(8); |
| /// |
| /// b.write_ascii(1..=5, "hello, world!"); |
| /// |
| /// print(b); // prints "[0068656c6c6f0000]" |
| /// ``` |
| #[rhai_fn(name = "write_ascii")] |
| pub fn write_ascii_string_range_inclusive( |
| blob: &mut Blob, |
| range: InclusiveRange, |
| string: &str, |
| ) { |
| let start = INT::max(*range.start(), 0); |
| let end = INT::max(*range.end(), start); |
| write_string(blob, start, end - start + 1, string, true); |
| } |
| /// Write an ASCII string to the bytes within an exclusive `range` in the BLOB. |
| /// |
| /// * If `start` < 0, position counts from the end of the BLOB (`-1` is the last byte). |
| /// * If `start` < -length of BLOB, position counts from the beginning of the BLOB. |
| /// * If `start` ≥ length of BLOB, the BLOB is not modified. |
| /// * If `len` ≤ 0, the BLOB is not modified. |
| /// * If `start` position + `len` ≥ length of BLOB, only the portion of the BLOB after the `start` position is modified. |
| /// |
| /// * If number of bytes in `range` < length of `string`, extra bytes in `string` are not written. |
| /// * If number of bytes in `range` > length of `string`, extra bytes in `range` are not modified. |
| /// |
| /// ```rhai |
| /// let b = blob(8); |
| /// |
| /// b.write_ascii(1, 5, "hello, world!"); |
| /// |
| /// print(b); // prints "[0068656c6c6f0000]" |
| /// ``` |
| #[rhai_fn(name = "write_ascii")] |
| pub fn write_ascii_string(blob: &mut Blob, start: INT, len: INT, string: &str) { |
| write_string(blob, start, len, string, true); |
| } |
| } |