Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ getrandom = { version = "0.2.0", optional = true }
rustyline = { version = "12.0.0", optional = true }
document-features = { version = "0.2.0", optional = true }
arbitrary = { version = "1.3.2", optional = true, features = ["derive"] }
indexmap = { version = "2.1.0", optional = true }

[dev-dependencies]
rmp-serde = "1.1.0"
Expand All @@ -54,6 +55,8 @@ std = ["once_cell/std", "ahash/std", "num-traits/std", "smartstring/std"]

## Require that all data types implement `Send + Sync` (for multi-threaded usage).
sync = []
## Use IndexMap as Map type
indexmap = ["dep:indexmap"]
## Add support for the [`Decimal`](https://crates.io/crates/rust_decimal) data type (acts as the system floating-point type under `no_float`).
decimal = ["rust_decimal"]
## Enable serialization/deserialization of Rhai data types via [`serde`](https://crates.io/crates/serde).
Expand Down
13 changes: 13 additions & 0 deletions src/ast/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -476,6 +476,7 @@ impl Expr {
}

#[cfg(not(feature = "no_object"))]
#[cfg(not(feature = "indexmap"))]
Self::Map(x, ..) if self.is_constant() => {
let mut map = x.1.clone();

Expand All @@ -486,6 +487,18 @@ impl Expr {
Dynamic::from_map(map)
}

#[cfg(not(feature = "no_object"))]
#[cfg(feature = "indexmap")]
Self::Map(x, ..) if self.is_constant() => {
Copy link
Copy Markdown
Author

@juchiast juchiast Nov 26, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here and in eval.rs, the code use BTreeMap directly instead of crate::Map, I don't know if changing it would have any side-effects, so I just add new code for indexmap feature.

let mut map = crate::Map::with_capacity(x.0.len());

for (k, v) in &x.0 {
map.insert(k.as_str().into(), v.get_literal_value().unwrap());
}

Dynamic::from_map(map)
}

// Interpolated string
Self::InterpolatedString(x, ..) if self.is_constant() => {
let mut s = SmartString::new_const();
Expand Down
32 changes: 32 additions & 0 deletions src/eval/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,7 @@ impl Engine {
}

#[cfg(not(feature = "no_object"))]
#[cfg(not(feature = "indexmap"))]
Expr::Map(x, ..) => {
let mut map = x.1.clone();

Expand Down Expand Up @@ -344,6 +345,37 @@ impl Engine {
Ok(Dynamic::from_map(map))
}

#[cfg(not(feature = "no_object"))]
#[cfg(feature = "indexmap")]
Expr::Map(x, ..) => {
let mut map = crate::Map::with_capacity(x.0.len());

#[cfg(not(feature = "unchecked"))]
let mut total_data_sizes = (0, 0, 0);

for (key, value_expr) in &x.0 {
let value = self
.eval_expr(global, caches, scope, this_ptr.as_deref_mut(), value_expr)?
.flatten();

#[cfg(not(feature = "unchecked"))]
if self.has_data_size_limit() {
let delta = crate::eval::calc_data_sizes(&value, true);
total_data_sizes = (
total_data_sizes.0 + delta.0,
total_data_sizes.1 + delta.1 + 1,
total_data_sizes.2 + delta.2,
);
self.throw_on_size(total_data_sizes)
.map_err(|err| err.fill_position(value_expr.position()))?;
}

map.insert(key.as_str().into(), value);
}

Ok(Dynamic::from_map(map))
}

Expr::And(x, ..) => Ok((self
.eval_expr(global, caches, scope, this_ptr.as_deref_mut(), &x.lhs)?
.as_bool()
Expand Down
11 changes: 11 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,17 @@ pub type Blob = Vec<u8>;
/// [`SmartString`](https://crates.io/crates/smartstring) is used as the key type because most
/// property names are ASCII and short, fewer than 23 characters, so they can be stored inline.
#[cfg(not(feature = "no_object"))]
#[cfg(feature = "indexmap")]
pub type Map = indexmap::IndexMap<Identifier, Dynamic>;

/// A dictionary of [`Dynamic`] values with string keys.
///
/// Not available under `no_object`.
///
/// [`SmartString`](https://crates.io/crates/smartstring) is used as the key type because most
/// property names are ASCII and short, fewer than 23 characters, so they can be stored inline.
#[cfg(not(feature = "no_object"))]
#[cfg(not(feature = "indexmap"))]
pub type Map = std::collections::BTreeMap<Identifier, Dynamic>;

#[cfg(not(feature = "no_object"))]
Expand Down
29 changes: 29 additions & 0 deletions src/types/dynamic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,14 @@ impl Dynamic {
}
}

fn hash_indexmap<H: Hasher>(m: &Box<crate::Map>, state: &mut H) {
let mut m = m.iter().collect::<Vec<_>>();
m.sort_unstable_by_key(|x| x.0);
for kv in m.into_iter() {
kv.hash(state);
}
}

impl Hash for Dynamic {
/// Hash the [`Dynamic`] value.
///
Expand All @@ -410,7 +418,11 @@ impl Hash for Dynamic {
#[cfg(not(feature = "no_index"))]
Union::Blob(ref a, ..) => a.hash(state),
#[cfg(not(feature = "no_object"))]
#[cfg(not(feature = "indexmap"))]
Union::Map(ref m, ..) => m.hash(state),
#[cfg(not(feature = "no_object"))]
#[cfg(feature = "indexmap")]
Union::Map(ref m, ..) => hash_indexmap(m, state),
Union::FnPtr(ref f, ..) => f.hash(state),

#[cfg(not(feature = "no_closure"))]
Expand Down Expand Up @@ -2217,6 +2229,23 @@ impl<K: Into<crate::Identifier>, T: Variant + Clone> From<std::collections::BTre
}
}
#[cfg(not(feature = "no_object"))]
#[cfg(feature = "indexmap")]
impl<K: Into<crate::Identifier>, T: Variant + Clone> From<indexmap::IndexMap<K, T>> for Dynamic {
#[inline]
fn from(value: indexmap::IndexMap<K, T>) -> Self {
Self(Union::Map(
Box::new(
value
.into_iter()
.map(|(k, v)| (k.into(), Self::from(v)))
.collect(),
),
DEFAULT_TAG_VALUE,
ReadWrite,
))
}
}
#[cfg(not(feature = "no_object"))]
impl<K: Into<crate::Identifier>> From<std::collections::BTreeSet<K>> for Dynamic {
#[inline]
fn from(value: std::collections::BTreeSet<K>) -> Self {
Expand Down