Skip to content

Commit

Permalink
rune: Store Bytes in AnyObj instead of Mutable (relates #844)
Browse files Browse the repository at this point in the history
  • Loading branch information
udoprog committed Oct 28, 2024
1 parent b9fca9a commit 2d69cb3
Show file tree
Hide file tree
Showing 10 changed files with 168 additions and 90 deletions.
18 changes: 11 additions & 7 deletions crates/rune/src/compile/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -303,24 +303,29 @@ impl Context {
pub fn with_config(#[allow(unused)] stdio: bool) -> Result<Self, ContextError> {
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()?)?;
#[cfg(feature = "alloc")]
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()?)?;
Expand All @@ -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()?)?;
Expand Down
129 changes: 128 additions & 1 deletion crates/rune/src/modules/bytes.rs
Original file line number Diff line number Diff line change
@@ -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.
Expand Down Expand Up @@ -32,6 +35,22 @@ pub fn module() -> Result<Module, ContextError> {
m.function_meta(clone__meta)?;
m.implement_trait::<Bytes>(rune::item!(::std::clone::Clone))?;

m.function_meta(partial_eq__meta)?;
m.implement_trait::<Bytes>(rune::item!(::std::cmp::PartialEq))?;

m.function_meta(eq__meta)?;
m.implement_trait::<Bytes>(rune::item!(::std::cmp::Eq))?;

m.function_meta(partial_cmp__meta)?;
m.implement_trait::<Bytes>(rune::item!(::std::cmp::PartialOrd))?;

m.function_meta(cmp__meta)?;
m.implement_trait::<Bytes>(rune::item!(::std::cmp::Ord))?;

m.function_meta(hash__meta)?;

m.function_meta(string_debug__meta)?;

Ok(m)
}

Expand Down Expand Up @@ -317,6 +336,114 @@ fn clone(this: &Bytes) -> VmResult<Bytes> {
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<Ordering> {
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
Expand Down
3 changes: 3 additions & 0 deletions crates/rune/src/modules/collections/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,19 @@ use crate::{ContextError, Module};
pub fn module() -> Result<Module, ContextError> {
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),
Expand Down
4 changes: 2 additions & 2 deletions crates/rune/src/runtime/borrow_mut.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,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_ref::<Bytes>()?;
///
/// let mut bytes: BorrowMut<[u8]> = BorrowMut::map(bytes, |bytes| &mut bytes[0..2]);
///
Expand All @@ -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_ref::<Bytes>()?;
///
/// let Ok(mut bytes) = BorrowMut::try_map(bytes, |bytes| bytes.get_mut(0..2)) else {
/// panic!("Conversion failed");
Expand Down
48 changes: 12 additions & 36 deletions crates/rune/src/runtime/bytes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
//!
//! [Value::Bytes]: crate::Value::Bytes.

use core::cmp;
use core::fmt;
use core::ops;

Expand All @@ -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<u8>,
bytes: Vec<u8>,
}

impl Bytes {
Expand Down Expand Up @@ -266,6 +263,7 @@ impl Bytes {
}

impl TryClone for Bytes {
#[inline]
fn try_clone(&self) -> alloc::Result<Self> {
Ok(Self {
bytes: self.bytes.try_clone()?,
Expand Down Expand Up @@ -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::<Bytes>(value.type_info());
}
ValueRepr::Mutable(value) => vm_try!(value.into_ref()),
ValueRepr::Any(value) => {
return VmResult::expected::<Bytes>(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::<Bytes>(actual.type_info())),
}
let (value, guard) = Ref::into_raw(vm_try!(value.into_any_ref::<Bytes>()));
VmResult::Ok((value.as_ref().as_slice(), guard))
}
}

impl<const N: usize> cmp::PartialEq<[u8; N]> for Bytes {
impl<const N: usize> PartialEq<[u8; N]> for Bytes {
#[inline]
fn eq(&self, other: &[u8; N]) -> bool {
self.bytes == other[..]
}
}

impl<const N: usize> cmp::PartialEq<&[u8; N]> for Bytes {
impl<const N: usize> PartialEq<&[u8; N]> for Bytes {
#[inline]
fn eq(&self, other: &&[u8; N]) -> bool {
self.bytes == other[..]
}
}

impl<const N: usize> cmp::PartialEq<Bytes> for [u8; N] {
impl<const N: usize> PartialEq<Bytes> for [u8; N] {
#[inline]
fn eq(&self, other: &Bytes) -> bool {
self[..] == other.bytes
}
}

impl<const N: usize> cmp::PartialEq<Bytes> for &[u8; N] {
impl<const N: usize> PartialEq<Bytes> 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<Bytes> for [u8] {
impl PartialEq<Bytes> for [u8] {
#[inline]
fn eq(&self, other: &Bytes) -> bool {
self == other.bytes
Expand Down
5 changes: 4 additions & 1 deletion crates/rune/src/runtime/const_value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()));

Expand Down Expand Up @@ -100,6 +99,10 @@ impl ConstValue {
let s = vm_try!(value.borrow_ref::<String>());
Self::String(vm_try!(s.try_to_owned()))
}
Bytes::HASH => {
let s = vm_try!(value.borrow_ref::<Bytes>());
Self::Bytes(vm_try!(s.try_to_owned()))
}
_ => {
return VmResult::err(VmErrorKind::ConstNotSupported {
actual: value.type_info(),
Expand Down
2 changes: 1 addition & 1 deletion crates/rune/src/runtime/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::<Bytes>().unwrap();
let out = shared.try_clone().unwrap();
out
};
Expand Down
Loading

0 comments on commit 2d69cb3

Please sign in to comment.