diff --git a/crates/rune/src/compile/context.rs b/crates/rune/src/compile/context.rs index 80175768f..be7dd7f4d 100644 --- a/crates/rune/src/compile/context.rs +++ b/crates/rune/src/compile/context.rs @@ -303,17 +303,20 @@ impl Context { pub fn with_config(#[allow(unused)] stdio: bool) -> Result { let mut this = Self::new(); + // NB: Order is important, since later modules might use types defined + // in previous modules. + this.install(crate::modules::iter::module()?)?; - // This must go first, because it includes types which are used in other modules. this.install(crate::modules::core::module()?)?; - + this.install(crate::modules::cmp::module()?)?; + this.install(crate::modules::any::module()?)?; this.install(crate::modules::clone::module()?)?; this.install(crate::modules::num::module()?)?; - this.install(crate::modules::any::module()?)?; - this.install(crate::modules::bytes::module()?)?; - this.install(crate::modules::char::module()?)?; this.install(crate::modules::hash::module()?)?; - this.install(crate::modules::cmp::module()?)?; + + this.install(crate::modules::string::module()?)?; + this.install(crate::modules::bytes::module()?)?; + this.install(crate::modules::collections::module()?)?; #[cfg(feature = "alloc")] this.install(crate::modules::collections::hash_map::module()?)?; @@ -321,6 +324,8 @@ impl Context { this.install(crate::modules::collections::hash_set::module()?)?; #[cfg(feature = "alloc")] this.install(crate::modules::collections::vec_deque::module()?)?; + + this.install(crate::modules::char::module()?)?; this.install(crate::modules::f64::module()?)?; this.install(crate::modules::tuple::module()?)?; this.install(crate::modules::fmt::module()?)?; @@ -336,7 +341,6 @@ impl Context { this.install(crate::modules::option::module()?)?; this.install(crate::modules::result::module()?)?; this.install(crate::modules::stream::module()?)?; - this.install(crate::modules::string::module()?)?; this.install(crate::modules::test::module()?)?; this.install(crate::modules::vec::module()?)?; this.install(crate::modules::slice::module()?)?; diff --git a/crates/rune/src/modules/bytes.rs b/crates/rune/src/modules/bytes.rs index 0880ae49f..a45810024 100644 --- a/crates/rune/src/modules/bytes.rs +++ b/crates/rune/src/modules/bytes.rs @@ -1,9 +1,12 @@ //! The bytes module. +use core::cmp::Ordering; + use crate as rune; +use crate::alloc::fmt::TryWrite; use crate::alloc::prelude::*; use crate::alloc::Vec; -use crate::runtime::{Bytes, VmResult}; +use crate::runtime::{Bytes, Formatter, Hasher, VmResult}; use crate::{ContextError, Module}; /// The bytes module. @@ -32,6 +35,22 @@ pub fn module() -> Result { m.function_meta(clone__meta)?; m.implement_trait::(rune::item!(::std::clone::Clone))?; + m.function_meta(partial_eq__meta)?; + m.implement_trait::(rune::item!(::std::cmp::PartialEq))?; + + m.function_meta(eq__meta)?; + m.implement_trait::(rune::item!(::std::cmp::Eq))?; + + m.function_meta(partial_cmp__meta)?; + m.implement_trait::(rune::item!(::std::cmp::PartialOrd))?; + + m.function_meta(cmp__meta)?; + m.implement_trait::(rune::item!(::std::cmp::Ord))?; + + m.function_meta(hash__meta)?; + + m.function_meta(string_debug__meta)?; + Ok(m) } @@ -317,6 +336,114 @@ fn clone(this: &Bytes) -> VmResult { VmResult::Ok(vm_try!(this.try_clone())) } +/// Test two byte arrays for partial equality. +/// +/// # Examples +/// +/// ```rune +/// use std::ops::partial_eq; +/// +/// assert_eq!(partial_eq(b"a", b"a"), true); +/// assert_eq!(partial_eq(b"a", b"ab"), false); +/// assert_eq!(partial_eq(b"ab", b"a"), false); +/// ``` +#[rune::function(keep, instance, protocol = PARTIAL_EQ)] +#[inline] +fn partial_eq(this: &[u8], rhs: &[u8]) -> bool { + this.eq(rhs) +} + +/// Test two byte arrays for total equality. +/// +/// # Examples +/// +/// ```rune +/// use std::ops::eq; +/// +/// assert_eq!(eq(b"a", b"a"), true); +/// assert_eq!(eq(b"a", b"ab"), false); +/// assert_eq!(eq(b"ab", b"a"), false); +/// ``` +#[rune::function(keep, instance, protocol = EQ)] +#[inline] +fn eq(this: &[u8], rhs: &[u8]) -> bool { + this.eq(rhs) +} + +/// Perform a partial ordered comparison between two byte arrays. +/// +/// # Examples +/// +/// ```rune +/// assert!(b"a" < b"ab"); +/// assert!(b"ab" > b"a"); +/// assert!(b"a" == b"a"); +/// ``` +/// +/// Using explicit functions: +/// +/// ```rune +/// use std::cmp::Ordering; +/// use std::ops::partial_cmp; +/// +/// assert_eq!(partial_cmp(b"a", b"ab"), Some(Ordering::Less)); +/// assert_eq!(partial_cmp(b"ab", b"a"), Some(Ordering::Greater)); +/// assert_eq!(partial_cmp(b"a", b"a"), Some(Ordering::Equal)); +/// ``` +#[rune::function(keep, instance, protocol = PARTIAL_CMP)] +#[inline] +fn partial_cmp(this: &[u8], rhs: &[u8]) -> Option { + this.partial_cmp(rhs) +} + +/// Perform a totally ordered comparison between two byte arrays. +/// +/// # Examples +/// +/// ```rune +/// use std::cmp::Ordering; +/// use std::ops::cmp; +/// +/// assert_eq!(cmp(b"a", b"ab"), Ordering::Less); +/// assert_eq!(cmp(b"ab", b"a"), Ordering::Greater); +/// assert_eq!(cmp(b"a", b"a"), Ordering::Equal); +/// ``` +#[rune::function(keep, instance, protocol = CMP)] +#[inline] +fn cmp(this: &[u8], rhs: &[u8]) -> Ordering { + this.cmp(rhs) +} + +/// Hash the string. +/// +/// # Examples +/// +/// ```rune +/// use std::ops::hash; +/// +/// let a = "hello"; +/// let b = "hello"; +/// +/// assert_eq!(hash(a), hash(b)); +/// ``` +#[rune::function(keep, instance, protocol = HASH)] +fn hash(this: &[u8], hasher: &mut Hasher) { + hasher.write(this); +} + +/// Write a debug representation of a byte array. +/// +/// # Examples +/// +/// ```rune +/// println!("{:?}", b"Hello"); +/// ``` +#[rune::function(keep, instance, protocol = STRING_DEBUG)] +#[inline] +fn string_debug(this: &[u8], f: &mut Formatter) -> VmResult<()> { + rune::vm_write!(f, "{this:?}") +} + /// Shrinks the capacity of the byte array as much as possible. /// /// It will drop down as close as possible to the length but the allocator may diff --git a/crates/rune/src/modules/collections/mod.rs b/crates/rune/src/modules/collections/mod.rs index d13e1e241..240fef742 100644 --- a/crates/rune/src/modules/collections/mod.rs +++ b/crates/rune/src/modules/collections/mod.rs @@ -23,16 +23,19 @@ use crate::{ContextError, Module}; pub fn module() -> Result { let mut m = Module::from_meta(self::module_meta)?; + #[cfg(feature = "alloc")] m.reexport( ["HashMap"], rune::item!(::std::collections::hash_map::HashMap), )?; + #[cfg(feature = "alloc")] m.reexport( ["HashSet"], rune::item!(::std::collections::hash_set::HashSet), )?; + #[cfg(feature = "alloc")] m.reexport( ["VecDeque"], rune::item!(::std::collections::vec_deque::VecDeque), diff --git a/crates/rune/src/runtime/borrow_mut.rs b/crates/rune/src/runtime/borrow_mut.rs index 763dd101e..08913b737 100644 --- a/crates/rune/src/runtime/borrow_mut.rs +++ b/crates/rune/src/runtime/borrow_mut.rs @@ -44,9 +44,9 @@ impl<'a, T: ?Sized> BorrowMut<'a, T> { /// use rune::alloc::try_vec; /// /// let bytes = rune::to_value(Bytes::from_vec(try_vec![1, 2, 3, 4]))?; - /// let bytes = bytes.borrow_bytes_mut()?; + /// let bytes = bytes.borrow_any_mut::()?; /// - /// let mut bytes: BorrowMut<[u8]> = BorrowMut::map(bytes, |bytes| &mut bytes[0..2]); + /// let mut bytes = BorrowMut::map(bytes, |bytes| &mut bytes[0..2]); /// /// assert_eq!(&mut bytes[..], &mut [1u8, 2u8][..]); /// # Ok::<_, rune::support::Error>(()) @@ -71,7 +71,7 @@ impl<'a, T: ?Sized> BorrowMut<'a, T> { /// use rune::alloc::try_vec; /// /// let bytes = rune::to_value(Bytes::from_vec(try_vec![1, 2, 3, 4]))?; - /// let bytes = bytes.borrow_bytes_mut()?; + /// let bytes = bytes.borrow_any_mut::()?; /// /// let Ok(mut bytes) = BorrowMut::try_map(bytes, |bytes| bytes.get_mut(0..2)) else { /// panic!("Conversion failed"); diff --git a/crates/rune/src/runtime/borrow_ref.rs b/crates/rune/src/runtime/borrow_ref.rs index 95410863d..8d5687872 100644 --- a/crates/rune/src/runtime/borrow_ref.rs +++ b/crates/rune/src/runtime/borrow_ref.rs @@ -41,7 +41,7 @@ impl<'a, T: ?Sized> BorrowRef<'a, T> { /// use rune::alloc::try_vec; /// /// let bytes = rune::to_value(Bytes::from_vec(try_vec![1, 2, 3, 4]))?; - /// let bytes = bytes.borrow_bytes_ref()?; + /// let bytes = bytes.borrow_any_ref::()?; /// /// let bytes: BorrowRef<[u8]> = BorrowRef::map(bytes, |bytes| &bytes[0..2]); /// @@ -67,7 +67,7 @@ impl<'a, T: ?Sized> BorrowRef<'a, T> { /// use rune::alloc::try_vec; /// /// let bytes = rune::to_value(Bytes::from_vec(try_vec![1, 2, 3, 4]))?; - /// let bytes = bytes.borrow_bytes_ref()?; + /// let bytes = bytes.borrow_any_ref::()?; /// /// let Ok(bytes) = BorrowRef::try_map(bytes, |bytes| bytes.get(0..2)) else { /// panic!("Conversion failed"); diff --git a/crates/rune/src/runtime/bytes.rs b/crates/rune/src/runtime/bytes.rs index c764dc582..9ae89dbd0 100644 --- a/crates/rune/src/runtime/bytes.rs +++ b/crates/rune/src/runtime/bytes.rs @@ -2,7 +2,6 @@ //! //! [Value::Bytes]: crate::Value::Bytes. -use core::cmp; use core::fmt; use core::ops; @@ -12,16 +11,14 @@ use serde::ser; use crate as rune; use crate::alloc::prelude::*; use crate::alloc::{self, Box, Vec}; -use crate::runtime::{ - Mutable, RawAnyGuard, Ref, UnsafeToRef, Value, ValueRepr, VmErrorKind, VmResult, -}; +use crate::runtime::{RawAnyGuard, Ref, UnsafeToRef, Value, VmResult}; use crate::Any; /// A vector of bytes. #[derive(Default, Any, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[rune(builtin, static_type = BYTES)] +#[rune(static_type = BYTES)] pub struct Bytes { - pub(crate) bytes: Vec, + bytes: Vec, } impl Bytes { @@ -266,6 +263,7 @@ impl Bytes { } impl TryClone for Bytes { + #[inline] fn try_clone(&self) -> alloc::Result { Ok(Self { bytes: self.bytes.try_clone()?, @@ -355,73 +353,51 @@ impl AsRef<[u8]> for Bytes { } } -from_value2!(Bytes, into_bytes_ref, into_bytes_mut, into_bytes); - impl UnsafeToRef for [u8] { type Guard = RawAnyGuard; unsafe fn unsafe_to_ref<'a>(value: Value) -> VmResult<(&'a Self, Self::Guard)> { - let value = match vm_try!(value.into_repr()) { - ValueRepr::Inline(value) => { - return VmResult::expected::(value.type_info()); - } - ValueRepr::Mutable(value) => vm_try!(value.into_ref()), - ValueRepr::Any(value) => { - return VmResult::expected::(value.type_info()); - } - }; - - let result = Ref::try_map(value, |value| match value { - Mutable::Bytes(bytes) => Some(bytes.as_slice()), - _ => None, - }); - - match result { - Ok(bytes) => { - let (value, guard) = Ref::into_raw(bytes); - VmResult::Ok((value.as_ref(), guard)) - } - Err(actual) => VmResult::err(VmErrorKind::expected::(actual.type_info())), - } + let (value, guard) = Ref::into_raw(vm_try!(value.into_any_ref::())); + VmResult::Ok((value.as_ref().as_slice(), guard)) } } -impl cmp::PartialEq<[u8; N]> for Bytes { +impl PartialEq<[u8; N]> for Bytes { #[inline] fn eq(&self, other: &[u8; N]) -> bool { self.bytes == other[..] } } -impl cmp::PartialEq<&[u8; N]> for Bytes { +impl PartialEq<&[u8; N]> for Bytes { #[inline] fn eq(&self, other: &&[u8; N]) -> bool { self.bytes == other[..] } } -impl cmp::PartialEq for [u8; N] { +impl PartialEq for [u8; N] { #[inline] fn eq(&self, other: &Bytes) -> bool { self[..] == other.bytes } } -impl cmp::PartialEq for &[u8; N] { +impl PartialEq for &[u8; N] { #[inline] fn eq(&self, other: &Bytes) -> bool { self[..] == other.bytes } } -impl cmp::PartialEq<[u8]> for Bytes { +impl PartialEq<[u8]> for Bytes { #[inline] fn eq(&self, other: &[u8]) -> bool { self.bytes == other } } -impl cmp::PartialEq for [u8] { +impl PartialEq for [u8] { #[inline] fn eq(&self, other: &Bytes) -> bool { self == other.bytes diff --git a/crates/rune/src/runtime/const_value.rs b/crates/rune/src/runtime/const_value.rs index b7931e6ca..c2d6c5240 100644 --- a/crates/rune/src/runtime/const_value.rs +++ b/crates/rune/src/runtime/const_value.rs @@ -59,7 +59,6 @@ impl ConstValue { Some(some) => Some(vm_try!(Box::try_new(vm_try!(Self::from_value_ref(some))))), None => None, }), - Mutable::Bytes(ref bytes) => Self::Bytes(vm_try!(bytes.try_clone())), Mutable::Vec(ref vec) => { let mut const_vec = vm_try!(Vec::try_with_capacity(vec.len())); @@ -100,6 +99,10 @@ impl ConstValue { let s = vm_try!(value.borrow_ref::()); Self::String(vm_try!(s.try_to_owned())) } + Bytes::HASH => { + let s = vm_try!(value.borrow_ref::()); + Self::Bytes(vm_try!(s.try_to_owned())) + } _ => { return VmResult::err(VmErrorKind::ConstNotSupported { actual: value.type_info(), diff --git a/crates/rune/src/runtime/tests.rs b/crates/rune/src/runtime/tests.rs index 7af4e6e2f..2c54c3215 100644 --- a/crates/rune/src/runtime/tests.rs +++ b/crates/rune/src/runtime/tests.rs @@ -549,7 +549,7 @@ fn test_clone_issue() { let shared = Value::try_from(Bytes::new()).unwrap(); let _ = { - let shared = shared.into_bytes_ref().unwrap(); + let shared = shared.into_any_ref::().unwrap(); let out = shared.try_clone().unwrap(); out }; diff --git a/crates/rune/src/runtime/value.rs b/crates/rune/src/runtime/value.rs index e6962f4e0..ff94040a0 100644 --- a/crates/rune/src/runtime/value.rs +++ b/crates/rune/src/runtime/value.rs @@ -425,7 +425,6 @@ impl Value { }); } ValueBorrowRef::Mutable(value) => match &*value { - Mutable::Bytes(value) => Mutable::Bytes(vm_try!(value.try_clone())), Mutable::Vec(value) => Mutable::Vec(vm_try!(value.try_clone())), Mutable::Tuple(value) => Mutable::Tuple(vm_try!(value.try_clone())), Mutable::Object(value) => Mutable::Object(vm_try!(value.try_clone())), @@ -499,9 +498,6 @@ impl Value { }; match &*vm_try!(value.borrow_ref()) { - Mutable::Bytes(value) => { - vm_try!(vm_write!(f, "{value:?}")); - } Mutable::Vec(value) => { vm_try!(Vec::string_debug_with(value, f, caller)); } @@ -843,16 +839,6 @@ impl Value { into_vec, } - into! { - /// Coerce into [`Bytes`]. - Bytes(Bytes), - into_bytes_ref, - into_bytes_mut, - borrow_bytes_ref, - borrow_bytes_mut, - into_bytes, - } - into! { /// Coerce into a [`ControlFlow`]. ControlFlow(ControlFlow), @@ -1184,9 +1170,6 @@ impl Value { }); } (ValueBorrowRef::Mutable(a), ValueBorrowRef::Mutable(b2)) => match (&**a, &*b2) { - (Mutable::Bytes(a), Mutable::Bytes(b)) => { - return VmResult::Ok(*a == *b); - } (Mutable::ControlFlow(a), Mutable::ControlFlow(b)) => { return ControlFlow::partial_eq_with(a, b, caller); } @@ -1313,10 +1296,6 @@ impl Value { } }, ValueBorrowRef::Mutable(value) => match &*value { - Mutable::Bytes(bytes) => { - hasher.write(bytes); - return VmResult::Ok(()); - } Mutable::Tuple(tuple) => { return Tuple::hash_with(tuple, hasher, caller); } @@ -1373,9 +1352,6 @@ impl Value { }); } (ValueBorrowRef::Mutable(a), ValueBorrowRef::Mutable(b)) => match (&*a, &*b) { - (Mutable::Bytes(a), Mutable::Bytes(b)) => { - return VmResult::Ok(*a == *b); - } (Mutable::Vec(a), Mutable::Vec(b)) => { return Vec::eq_with(a, b, Value::eq_with, caller); } @@ -1476,9 +1452,6 @@ impl Value { }) } (ValueBorrowRef::Mutable(a), ValueBorrowRef::Mutable(b)) => match (&*a, &*b) { - (Mutable::Bytes(a), Mutable::Bytes(b)) => { - return VmResult::Ok(a.partial_cmp(b)); - } (Mutable::Vec(a), Mutable::Vec(b)) => { return Vec::partial_cmp_with(a, b, caller); } @@ -1571,9 +1544,6 @@ impl Value { match (vm_try!(self.borrow_ref()), vm_try!(b.borrow_ref())) { (ValueBorrowRef::Inline(a), ValueBorrowRef::Inline(b)) => return a.cmp(b), (ValueBorrowRef::Mutable(a), ValueBorrowRef::Mutable(b)) => match (&*a, &*b) { - (Mutable::Bytes(a), Mutable::Bytes(b)) => { - return VmResult::Ok(a.cmp(b)); - } (Mutable::Vec(a), Mutable::Vec(b)) => { return Vec::cmp_with(a, b, caller); } @@ -1962,7 +1932,6 @@ inline_from! { } from! { - Bytes => Bytes, ControlFlow => ControlFlow, Function => Function, GeneratorState => GeneratorState, @@ -1981,6 +1950,7 @@ from! { any_from! { String, + Bytes, } from_container! { @@ -2253,8 +2223,6 @@ impl Inline { } pub(crate) enum Mutable { - /// A byte string. - Bytes(Bytes), /// A vector containing any values. Vec(Vec), /// A tuple. @@ -2292,7 +2260,6 @@ pub(crate) enum Mutable { impl Mutable { pub(crate) fn type_info(&self) -> TypeInfo { match self { - Mutable::Bytes(..) => TypeInfo::static_type(static_type::BYTES), Mutable::Vec(..) => TypeInfo::static_type(static_type::VEC), Mutable::Tuple(..) => TypeInfo::static_type(static_type::TUPLE), Mutable::Object(..) => TypeInfo::static_type(static_type::OBJECT), @@ -2318,7 +2285,6 @@ impl Mutable { /// *enum*, and not the type hash of the variant itself. pub(crate) fn type_hash(&self) -> Hash { match self { - Mutable::Bytes(..) => static_type::BYTES.hash, Mutable::Vec(..) => static_type::VEC.hash, Mutable::Tuple(..) => static_type::TUPLE.hash, Mutable::Object(..) => static_type::OBJECT.hash, diff --git a/crates/rune/src/runtime/value/serde.rs b/crates/rune/src/runtime/value/serde.rs index ab32893ad..6e4a93d10 100644 --- a/crates/rune/src/runtime/value/serde.rs +++ b/crates/rune/src/runtime/value/serde.rs @@ -38,7 +38,6 @@ impl ser::Serialize for Value { Inline::Ordering(..) => Err(ser::Error::custom("cannot serialize orderings")), }, ValueBorrowRef::Mutable(value) => match &*value { - Mutable::Bytes(bytes) => serializer.serialize_bytes(bytes), Mutable::Vec(vec) => { let mut serializer = serializer.serialize_seq(Some(vec.len()))?; @@ -97,6 +96,10 @@ impl ser::Serialize for Value { let string = value.borrow_ref::().map_err(S::Error::custom)?; serializer.serialize_str(string.as_str()) } + Bytes::HASH => { + let bytes = value.borrow_ref::().map_err(S::Error::custom)?; + serializer.serialize_bytes(bytes.as_slice()) + } _ => Err(ser::Error::custom("cannot serialize external references")), }, } diff --git a/crates/rune/src/runtime/vm.rs b/crates/rune/src/runtime/vm.rs index 920a3eafe..441a91699 100644 --- a/crates/rune/src/runtime/vm.rs +++ b/crates/rune/src/runtime/vm.rs @@ -3095,16 +3095,12 @@ impl Vm { let v = vm_try!(self.stack.at(addr)); let is_match = 'out: { - let ValueBorrowRef::Mutable(value) = vm_try!(v.borrow_ref()) else { - break 'out false; - }; - - let Mutable::Bytes(actual) = &*value else { + let Some(value) = vm_try!(v.try_borrow_ref::()) else { break 'out false; }; let bytes = vm_try!(self.unit.lookup_bytes(slot)); - *actual == *bytes + value.as_slice() == bytes }; vm_try!(out.store(&mut self.stack, is_match));