mirror of
https://github.com/gorules/zen.git
synced 2026-10-04 00:02:18 +00:00
remove unsafe usages, further improve perf
This commit is contained in:
Generated
+14
@@ -4026,6 +4026,12 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "self_cell"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2ab42ca02749e120097e328d91d415325bdf43b1c72c4c8badf37375fe40a813"
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.28"
|
||||
@@ -5162,6 +5168,12 @@ version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
|
||||
|
||||
[[package]]
|
||||
name = "typed-arena"
|
||||
version = "2.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a"
|
||||
|
||||
[[package]]
|
||||
name = "typed-path"
|
||||
version = "0.12.3"
|
||||
@@ -6023,6 +6035,7 @@ dependencies = [
|
||||
"reqwest 0.12.28",
|
||||
"rquickjs",
|
||||
"rust_decimal",
|
||||
"self_cell",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_path_to_error",
|
||||
@@ -6035,6 +6048,7 @@ dependencies = [
|
||||
"thiserror 1.0.69",
|
||||
"tokio",
|
||||
"toml 0.8.23",
|
||||
"typed-arena",
|
||||
"zen-expression",
|
||||
"zen-tmpl",
|
||||
"zen-types",
|
||||
|
||||
@@ -38,6 +38,8 @@ swc_ts_fast_strip = "53.0.0"
|
||||
swc_common = "23"
|
||||
swc_ecma_parser = "41"
|
||||
swc_ecma_ast = "25"
|
||||
typed-arena = "2"
|
||||
self_cell = "1"
|
||||
|
||||
[dev-dependencies]
|
||||
tokio = { workspace = true, features = ["rt-multi-thread", "macros"] }
|
||||
|
||||
@@ -120,7 +120,7 @@
|
||||
|
||||
// `deny` rather than `forbid` so `nodes::variable_json` can opt in: reading a
|
||||
// `RefCell` without a guard is the only way to hand jsonschema `'a` borrows.
|
||||
#![deny(unsafe_code)]
|
||||
#![forbid(unsafe_code)]
|
||||
#![deny(clippy::unwrap_used)]
|
||||
#![allow(clippy::module_inception)]
|
||||
|
||||
|
||||
@@ -54,15 +54,25 @@ impl DecisionLoader for CachedLoader {
|
||||
key: &'a str,
|
||||
) -> Pin<Box<dyn Future<Output = LoaderResponse> + 'a + Send>> {
|
||||
Box::pin(async move {
|
||||
if let Ok(cache) = self.cache.read() {
|
||||
if let Some(content) = cache.get(key) {
|
||||
return Ok(content.clone());
|
||||
}
|
||||
}
|
||||
let cached = self
|
||||
.cache
|
||||
.read()
|
||||
.ok()
|
||||
.and_then(|cache| cache.get(key).cloned());
|
||||
|
||||
let decision_content = prepared(&self.loader, self.loader.load(key).await?).await;
|
||||
if let Ok(mut cache) = self.cache.write() {
|
||||
cache.insert(key.to_string(), decision_content.clone());
|
||||
let loaded = match &cached {
|
||||
Some(content) => content.clone(),
|
||||
None => self.loader.load(key).await?,
|
||||
};
|
||||
|
||||
let decision_content = prepared(&self.loader, loaded).await;
|
||||
let unchanged = cached
|
||||
.as_ref()
|
||||
.is_some_and(|content| Arc::ptr_eq(content, &decision_content));
|
||||
if !unchanged {
|
||||
if let Ok(mut cache) = self.cache.write() {
|
||||
cache.insert(key.to_string(), decision_content.clone());
|
||||
}
|
||||
}
|
||||
Ok(decision_content)
|
||||
})
|
||||
|
||||
@@ -2,6 +2,7 @@ use crate::nodes::definition::{NodeDataType, TraceDataType};
|
||||
use crate::nodes::extensions::NodeHandlerExtensions;
|
||||
use crate::nodes::function::v2::function::Function;
|
||||
use crate::nodes::result::{NodeResponse, NodeResult};
|
||||
use crate::nodes::variable_json::{Guards, VariableNode};
|
||||
use crate::nodes::NodeError;
|
||||
use crate::ZEN_CONFIG;
|
||||
use ahash::AHasher;
|
||||
@@ -39,6 +40,19 @@ where
|
||||
NodeData: NodeDataType,
|
||||
TraceData: TraceDataType,
|
||||
{
|
||||
pub fn input_with_nodes(&self) -> Variable {
|
||||
let Some(nodes) = &self.nodes else {
|
||||
return self.input.shallow_clone();
|
||||
};
|
||||
let Variable::Object(object) = &self.input else {
|
||||
return self.input.shallow_clone();
|
||||
};
|
||||
|
||||
let mut map = object.borrow().clone();
|
||||
map.insert(Variable::nodes_key(), nodes.clone());
|
||||
Variable::from_object(map)
|
||||
}
|
||||
|
||||
pub fn from_base(base: NodeContextBase, data: NodeData) -> Self {
|
||||
Self {
|
||||
id: base.id,
|
||||
@@ -103,8 +117,9 @@ where
|
||||
.get_or_insert(hash, schema)
|
||||
.node_context(self)?;
|
||||
|
||||
let guards = Guards::default();
|
||||
validator
|
||||
.validate(value)
|
||||
.validate(VariableNode::new(value, &guards))
|
||||
.map_err(|err| ValidationErrorJson::from(err))
|
||||
.node_context(self)?;
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ impl NodeHandler for CustomNodeHandler {
|
||||
|
||||
async fn handle(&self, ctx: NodeContext<Self::NodeData, Self::TraceData>) -> NodeResult {
|
||||
let custom_node_request = CustomNodeRequest {
|
||||
input: ctx.input.clone(),
|
||||
input: ctx.input_with_nodes(),
|
||||
node: CustomDecisionNode {
|
||||
id: ctx.id.clone(),
|
||||
name: ctx.name.clone(),
|
||||
|
||||
@@ -30,7 +30,7 @@ impl NodeHandler for FunctionV1NodeHandler {
|
||||
runtime.set_interrupt_handler(Some(interrupt_handler));
|
||||
|
||||
let mut script = Script::new(runtime.clone());
|
||||
let result_response = script.call(ctx.node.deref(), &ctx.input).await;
|
||||
let result_response = script.call(ctx.node.deref(), &ctx.input_with_nodes()).await;
|
||||
|
||||
runtime.set_interrupt_handler(None);
|
||||
|
||||
|
||||
@@ -1,20 +1,66 @@
|
||||
// Safety: `Variable` is `Rc`-based, so reading through a `RefCell` without a
|
||||
// guard is the only way to hand out `'a` borrows. Scoped to this module.
|
||||
#![allow(unsafe_code)]
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::cell::Ref;
|
||||
|
||||
use jsonschema::json::{Array, Json, JsonNumber, Node, NodeIdentity, Object};
|
||||
use jsonschema::JsonType;
|
||||
use rust_decimal::prelude::ToPrimitive;
|
||||
use rust_decimal::Decimal;
|
||||
use self_cell::self_cell;
|
||||
use serde_json::Value;
|
||||
use zen_types::variable::{Variable, VariableMap};
|
||||
use typed_arena::Arena;
|
||||
use zen_types::variable::{MapIter, RcCell, Variable, VariableMap};
|
||||
|
||||
pub struct VariableJson;
|
||||
|
||||
type MapRef<'a> = Ref<'a, VariableMap>;
|
||||
type VecRef<'a> = Ref<'a, Vec<Variable>>;
|
||||
|
||||
self_cell!(
|
||||
struct MapGuard {
|
||||
owner: RcCell<VariableMap>,
|
||||
|
||||
#[covariant]
|
||||
dependent: MapRef,
|
||||
}
|
||||
);
|
||||
|
||||
self_cell!(
|
||||
struct VecGuard {
|
||||
owner: RcCell<Vec<Variable>>,
|
||||
|
||||
#[covariant]
|
||||
dependent: VecRef,
|
||||
}
|
||||
);
|
||||
|
||||
pub struct Guards {
|
||||
objects: Arena<MapGuard>,
|
||||
arrays: Arena<VecGuard>,
|
||||
}
|
||||
|
||||
impl Default for Guards {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
objects: Arena::new(),
|
||||
arrays: Arena::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct VariableNode<'a> {
|
||||
var: &'a Variable,
|
||||
guards: &'a Guards,
|
||||
}
|
||||
|
||||
impl<'a> VariableNode<'a> {
|
||||
pub fn new(var: &'a Variable, guards: &'a Guards) -> Self {
|
||||
Self { var, guards }
|
||||
}
|
||||
}
|
||||
|
||||
impl Json for VariableJson {
|
||||
type Node<'a> = &'a Variable;
|
||||
type Node<'a> = VariableNode<'a>;
|
||||
type PreparedKey = Box<str>;
|
||||
type StringBuffer = Variable;
|
||||
|
||||
@@ -25,10 +71,11 @@ impl Json for VariableJson {
|
||||
fn with_string_node<T>(
|
||||
buffer: &mut Variable,
|
||||
string: &str,
|
||||
f: impl FnOnce(&Variable) -> T,
|
||||
f: impl FnOnce(VariableNode<'_>) -> T,
|
||||
) -> T {
|
||||
*buffer = Variable::String((string).into());
|
||||
f(buffer)
|
||||
let guards = Guards::default();
|
||||
f(VariableNode::new(buffer, &guards))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,11 +83,11 @@ pub struct VariableNumber(Decimal);
|
||||
|
||||
impl JsonNumber for VariableNumber {
|
||||
fn as_u64(&self) -> Option<u64> {
|
||||
self.0.to_u64()
|
||||
self.0.is_integer().then(|| self.0.to_u64()).flatten()
|
||||
}
|
||||
|
||||
fn as_i64(&self) -> Option<i64> {
|
||||
self.0.to_i64()
|
||||
self.0.is_integer().then(|| self.0.to_i64()).flatten()
|
||||
}
|
||||
|
||||
fn as_f64(&self) -> Option<f64> {
|
||||
@@ -61,66 +108,69 @@ impl JsonNumber for VariableNumber {
|
||||
}
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// Validation is a pure read: nothing in `jsonschema` holds a `Variable`, and no
|
||||
/// node handler mutates the input while its schema is being checked. The `Rc`
|
||||
/// keeps the cell alive for at least `'a`, so reading through the cell's pointer
|
||||
/// cannot outlive the allocation and cannot race a `borrow_mut`.
|
||||
#[inline]
|
||||
unsafe fn cell_ref<'a, T: zen_types::rccell::Recycle>(
|
||||
cell: &zen_types::rccell::RcCell<T>,
|
||||
) -> &'a T {
|
||||
unsafe { cell.get_ref() }
|
||||
}
|
||||
|
||||
impl<'a> Node<'a, VariableJson> for &'a Variable {
|
||||
type Object = &'a VariableMap;
|
||||
type Array = &'a [Variable];
|
||||
impl<'a> Node<'a, VariableJson> for VariableNode<'a> {
|
||||
type Object = ObjectNode<'a>;
|
||||
type Array = ArrayNode<'a>;
|
||||
type Number = VariableNumber;
|
||||
|
||||
fn as_object(&self) -> Option<&'a VariableMap> {
|
||||
match self {
|
||||
Variable::Object(map) => Some(unsafe { cell_ref(map) }),
|
||||
_ => None,
|
||||
}
|
||||
fn as_object(&self) -> Option<ObjectNode<'a>> {
|
||||
let Variable::Object(cell) = self.var else {
|
||||
return None;
|
||||
};
|
||||
|
||||
let guard = self
|
||||
.guards
|
||||
.objects
|
||||
.alloc(MapGuard::new(cell.clone(), |cell| cell.borrow()));
|
||||
Some(ObjectNode {
|
||||
map: guard.borrow_dependent(),
|
||||
guards: self.guards,
|
||||
})
|
||||
}
|
||||
|
||||
fn as_array(&self) -> Option<&'a [Variable]> {
|
||||
match self {
|
||||
Variable::Array(items) => Some(unsafe { cell_ref(items) }.as_slice()),
|
||||
_ => None,
|
||||
}
|
||||
fn as_array(&self) -> Option<ArrayNode<'a>> {
|
||||
let Variable::Array(cell) = self.var else {
|
||||
return None;
|
||||
};
|
||||
|
||||
let guard = self
|
||||
.guards
|
||||
.arrays
|
||||
.alloc(VecGuard::new(cell.clone(), |cell| cell.borrow()));
|
||||
Some(ArrayNode {
|
||||
items: guard.borrow_dependent().as_slice(),
|
||||
guards: self.guards,
|
||||
})
|
||||
}
|
||||
|
||||
fn as_string(&self) -> Option<Cow<'a, str>> {
|
||||
match self {
|
||||
Variable::String(string) => Some(Cow::Borrowed(string)),
|
||||
match self.var {
|
||||
Variable::String(string) => Some(Cow::Borrowed(string.as_str())),
|
||||
Variable::Dynamic(dynamic) => Some(Cow::Owned(dynamic.to_string())),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn as_number(&self) -> Option<VariableNumber> {
|
||||
match self {
|
||||
match self.var {
|
||||
Variable::Number(number) => Some(VariableNumber(*number)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn as_boolean(&self) -> Option<bool> {
|
||||
match self {
|
||||
match self.var {
|
||||
Variable::Bool(boolean) => Some(*boolean),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn is_null(&self) -> bool {
|
||||
matches!(self, Variable::Null)
|
||||
matches!(self.var, Variable::Null)
|
||||
}
|
||||
|
||||
fn json_type(&self) -> JsonType {
|
||||
match self {
|
||||
match self.var {
|
||||
Variable::Null => JsonType::Null,
|
||||
Variable::Bool(_) => JsonType::Boolean,
|
||||
Variable::Number(_) => JsonType::Number,
|
||||
@@ -132,53 +182,91 @@ impl<'a> Node<'a, VariableJson> for &'a Variable {
|
||||
}
|
||||
|
||||
fn to_value(&self) -> Cow<'a, Value> {
|
||||
Cow::Owned(Variable::to_value(self))
|
||||
Cow::Owned(Variable::to_value(self.var))
|
||||
}
|
||||
|
||||
fn identity(&self) -> Option<NodeIdentity> {
|
||||
Some(NodeIdentity::new(
|
||||
std::ptr::from_ref::<Variable>(*self) as usize
|
||||
std::ptr::from_ref::<Variable>(self.var) as usize
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Object<'a, VariableJson> for &'a VariableMap {
|
||||
type Node = &'a Variable;
|
||||
pub struct ObjectNode<'a> {
|
||||
map: &'a VariableMap,
|
||||
guards: &'a Guards,
|
||||
}
|
||||
|
||||
impl<'a> Object<'a, VariableJson> for ObjectNode<'a> {
|
||||
type Node = VariableNode<'a>;
|
||||
type MemberName = &'a str;
|
||||
type MembersIter = VariableMembersIter<'a>;
|
||||
|
||||
fn len(&self) -> usize {
|
||||
VariableMap::len(self)
|
||||
self.map.len()
|
||||
}
|
||||
|
||||
fn get(&self, key: &Box<str>) -> Option<&'a Variable> {
|
||||
VariableMap::get_str(self, key)
|
||||
fn get(&self, key: &Box<str>) -> Option<VariableNode<'a>> {
|
||||
self.map
|
||||
.get_str(key)
|
||||
.map(|var| VariableNode::new(var, self.guards))
|
||||
}
|
||||
|
||||
fn members(&self) -> VariableMembersIter<'a> {
|
||||
VariableMembersIter(self.iter())
|
||||
VariableMembersIter {
|
||||
iter: self.map.iter(),
|
||||
guards: self.guards,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct VariableMembersIter<'a>(zen_types::variable::MapIter<'a>);
|
||||
pub struct VariableMembersIter<'a> {
|
||||
iter: MapIter<'a>,
|
||||
guards: &'a Guards,
|
||||
}
|
||||
|
||||
impl<'a> Iterator for VariableMembersIter<'a> {
|
||||
type Item = (&'a str, &'a Variable);
|
||||
type Item = (&'a str, VariableNode<'a>);
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.0.next().map(|(name, value)| (name.as_str(), value))
|
||||
self.iter
|
||||
.next()
|
||||
.map(|(name, value)| (name.as_str(), VariableNode::new(value, self.guards)))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Array<'a, VariableJson> for &'a [Variable] {
|
||||
type Node = &'a Variable;
|
||||
type ElementsIter = std::slice::Iter<'a, Variable>;
|
||||
pub struct ArrayNode<'a> {
|
||||
items: &'a [Variable],
|
||||
guards: &'a Guards,
|
||||
}
|
||||
|
||||
impl<'a> Array<'a, VariableJson> for ArrayNode<'a> {
|
||||
type Node = VariableNode<'a>;
|
||||
type ElementsIter = VariableElementsIter<'a>;
|
||||
|
||||
fn len(&self) -> usize {
|
||||
<[Variable]>::len(self)
|
||||
self.items.len()
|
||||
}
|
||||
|
||||
fn elements(&self) -> std::slice::Iter<'a, Variable> {
|
||||
self.iter()
|
||||
fn elements(&self) -> VariableElementsIter<'a> {
|
||||
VariableElementsIter {
|
||||
iter: self.items.iter(),
|
||||
guards: self.guards,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct VariableElementsIter<'a> {
|
||||
iter: std::slice::Iter<'a, Variable>,
|
||||
guards: &'a Guards,
|
||||
}
|
||||
|
||||
impl<'a> Iterator for VariableElementsIter<'a> {
|
||||
type Item = VariableNode<'a>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.iter
|
||||
.next()
|
||||
.map(|var| VariableNode::new(var, self.guards))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ use std::sync::Arc;
|
||||
|
||||
use ahash::{HashMap, HashMapExt, HashSet, HashSetExt};
|
||||
use petgraph::stable_graph::StableDiGraph;
|
||||
use zen_expression::variable::VariableType;
|
||||
use zen_expression::variable::{Variable, VariableType};
|
||||
|
||||
use crate::policy::blocks::InstanceSource;
|
||||
use crate::policy::ir::{DataModelIr, DictionaryIr, ParsedPolicy, Property, PropertyTypeIr};
|
||||
@@ -765,7 +765,7 @@ impl VariableTypeScope for VariableType {
|
||||
return self.shallow_clone();
|
||||
};
|
||||
let mut fields: HashMap<Rc<str>, VariableType> = obj.borrow().clone();
|
||||
fields.insert(Rc::from("$"), field_type.shallow_clone());
|
||||
fields.insert(Variable::dollar_key_rc(), field_type.shallow_clone());
|
||||
VariableType::Object(Rc::new(RefCell::new(fields)))
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use crate::functions::{ClosureFunction, FunctionKind};
|
||||
use crate::lexer::{LogicalOperator, Operator};
|
||||
use crate::parser::{Node, NodeMetadata};
|
||||
use crate::variable::Variable;
|
||||
use ahash::HashSet;
|
||||
use nohash_hasher::BuildNoHashHasher;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -292,7 +293,7 @@ impl<'a> DependencyResolutionWalker<'a> {
|
||||
fn extract_path_with_spans(&self, node: &Node) -> Option<(Vec<Rc<str>>, Vec<(u32, u32)>)> {
|
||||
match node {
|
||||
Node::Identifier(name) => Some((vec![Rc::from(*name)], vec![self.node_span(node)])),
|
||||
Node::Root => Some((vec![Rc::<str>::from("$root")], vec![self.node_span(node)])),
|
||||
Node::Root => Some((vec![Variable::root_key_rc()], vec![self.node_span(node)])),
|
||||
Node::Member { node: n, property } => {
|
||||
let (mut path, mut spans) = self.extract_path_with_spans(n)?;
|
||||
match property {
|
||||
@@ -311,7 +312,7 @@ impl<'a> DependencyResolutionWalker<'a> {
|
||||
fn collection_source_path(node: &Node) -> Option<Vec<Rc<str>>> {
|
||||
match node {
|
||||
Node::Identifier(name) => Some(vec![Rc::from(*name)]),
|
||||
Node::Root => Some(vec![Rc::<str>::from("$root")]),
|
||||
Node::Root => Some(vec![Variable::root_key_rc()]),
|
||||
Node::Member { .. } => Self::extract_read_path(node),
|
||||
Node::Parenthesized(inner) => Self::collection_source_path(inner),
|
||||
Node::Binary {
|
||||
@@ -374,7 +375,7 @@ impl<'a> DependencyResolutionWalker<'a> {
|
||||
return;
|
||||
}
|
||||
Node::Identifier(name) => Rc::from(*name),
|
||||
Node::Root => Rc::<str>::from("$root"),
|
||||
Node::Root => Variable::root_key_rc(),
|
||||
Node::Pointer => {
|
||||
self.resolve_pointer_chain(&chain, scope);
|
||||
return;
|
||||
@@ -487,7 +488,7 @@ impl<'a> DependencyResolutionWalker<'a> {
|
||||
}
|
||||
|
||||
fn reference_dollar_chain(&mut self, chain: &FlatChain, scope: &mut Scope) {
|
||||
let mut path: Vec<Rc<str>> = vec![Rc::from("$")];
|
||||
let mut path: Vec<Rc<str>> = vec![Variable::dollar_key_rc()];
|
||||
let mut spans = vec![self.node_span(chain.root)];
|
||||
let mut grouping = true;
|
||||
for segment in &chain.segments {
|
||||
@@ -673,7 +674,7 @@ impl<'a> DependencyResolutionWalker<'a> {
|
||||
let (alias, inner_reads, inner_refs) = match closure_node {
|
||||
Node::Closure { body, alias } => {
|
||||
let mut inner_scope = scope.clone();
|
||||
inner_scope.locals.insert(Rc::<str>::from("$"));
|
||||
inner_scope.locals.insert(Variable::dollar_key_rc());
|
||||
inner_scope.pointer_collection = collection_source
|
||||
.as_ref()
|
||||
.filter(|source| !scope.is_local(source))
|
||||
@@ -762,7 +763,7 @@ impl<'a> DependencyResolutionWalker<'a> {
|
||||
|
||||
Node::Closure { body, alias: _ } => {
|
||||
let mut inner = scope.clone();
|
||||
inner.locals.insert(Rc::<str>::from("$"));
|
||||
inner.locals.insert(Variable::dollar_key_rc());
|
||||
inner.pointer_collection = None;
|
||||
self.resolve(body, &mut inner);
|
||||
}
|
||||
@@ -836,7 +837,7 @@ impl<'a> DependencyResolutionWalker<'a> {
|
||||
}
|
||||
|
||||
Node::Root => {
|
||||
let path: Vec<Rc<str>> = vec![Rc::<str>::from("$root")];
|
||||
let path: Vec<Rc<str>> = vec![Variable::root_key_rc()];
|
||||
let span = self.node_span(node);
|
||||
self.reads.push(ReadDependency::Direct {
|
||||
path: path.clone(),
|
||||
@@ -865,7 +866,7 @@ impl<'a> DependencyResolutionWalker<'a> {
|
||||
fn extract_read_path(node: &Node) -> Option<Vec<Rc<str>>> {
|
||||
match node {
|
||||
Node::Identifier(name) => Some(vec![Rc::from(*name)]),
|
||||
Node::Root => Some(vec![Rc::<str>::from("$root")]),
|
||||
Node::Root => Some(vec![Variable::root_key_rc()]),
|
||||
Node::Member { node, property } => {
|
||||
let mut path = Self::extract_read_path(node)?;
|
||||
match property {
|
||||
|
||||
@@ -44,10 +44,6 @@ impl Scope {
|
||||
}
|
||||
|
||||
pub fn set_local(&mut self, name: Symbol, value: Variable) {
|
||||
if !matches!(self.base, Variable::Object(_)) {
|
||||
self.base = Variable::empty_object();
|
||||
}
|
||||
|
||||
match self
|
||||
.locals
|
||||
.iter_mut()
|
||||
@@ -100,12 +96,7 @@ impl Scope {
|
||||
}
|
||||
|
||||
let Variable::Object(base) = &self.base else {
|
||||
return Variable::from_object(
|
||||
self.locals
|
||||
.iter()
|
||||
.map(|(key, value)| (key.clone(), value.clone()))
|
||||
.collect(),
|
||||
);
|
||||
return self.base.shallow_clone();
|
||||
};
|
||||
|
||||
let mut map = base.borrow().clone();
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
pub use zen_types::rcvalue::*;
|
||||
pub use zen_types::symbol::Symbol;
|
||||
pub use zen_types::variable::*;
|
||||
pub use zen_types::variable_type::*;
|
||||
|
||||
|
||||
@@ -47,9 +47,7 @@ pub fn to_variable_impl(input: TokenStream) -> TokenStream {
|
||||
const _: () = {
|
||||
extern crate zen_expression as _zen_expression;
|
||||
|
||||
use _zen_expression::variable::{Variable as _Variable, VariableMap as _VariableMap, VariableMapExt, ToVariable as _ToVariable};
|
||||
use ::std::rc::Rc as _Rc;
|
||||
use ::zen_types::symbol::Symbol as _Symbol;
|
||||
use _zen_expression::variable::{Variable as _Variable, VariableMap as _VariableMap, VariableMapExt, ToVariable as _ToVariable, Symbol as _Symbol};
|
||||
|
||||
#impl_block
|
||||
};
|
||||
@@ -225,7 +223,7 @@ fn generate_variant_arm(
|
||||
serde_derive_internals::ast::Style::Unit => {
|
||||
quote! {
|
||||
#enum_ident::#variant_ident => {
|
||||
_Variable::String(_Rc::from(#variant_name))
|
||||
_Variable::String(_Symbol::from_static(#variant_name))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -234,7 +232,7 @@ fn generate_variant_arm(
|
||||
quote! {
|
||||
#enum_ident::#variant_ident(value) => {
|
||||
let mut map = _VariableMap::with_capacity(2);
|
||||
map.insert(_Symbol::from(#type_key), _Variable::String(_Rc::from(#variant_name)));
|
||||
map.insert(_Symbol::from(#type_key), _Variable::String(_Symbol::from_static(#variant_name)));
|
||||
map.insert(_Symbol::from(#value_key), value.to_variable());
|
||||
_Variable::from_object(map)
|
||||
}
|
||||
@@ -251,7 +249,7 @@ fn generate_variant_arm(
|
||||
quote! {
|
||||
#enum_ident::#variant_ident(#(#field_patterns),*) => {
|
||||
let mut map = _VariableMap::with_capacity(2);
|
||||
map.insert(_Symbol::from(#type_key), _Variable::String(_Rc::from(#variant_name)));
|
||||
map.insert(_Symbol::from(#type_key), _Variable::String(_Symbol::from_static(#variant_name)));
|
||||
map.insert(_Symbol::from(#value_key), (#(#field_patterns)*).to_variable());
|
||||
_Variable::from_object(map)
|
||||
}
|
||||
@@ -267,7 +265,7 @@ fn generate_variant_arm(
|
||||
quote! {
|
||||
#enum_ident::#variant_ident(#(#field_patterns),*) => {
|
||||
let mut map = _VariableMap::with_capacity(#field_count + 1);
|
||||
map.insert(_Symbol::from(#type_key), _Variable::String(_Rc::from(#variant_name)));
|
||||
map.insert(_Symbol::from(#type_key), _Variable::String(_Symbol::from_static(#variant_name)));
|
||||
#(#field_mappings)*
|
||||
_Variable::from_object(map)
|
||||
}
|
||||
@@ -304,7 +302,7 @@ fn generate_variant_arm(
|
||||
quote! {
|
||||
#enum_ident::#variant_ident { #(#field_patterns),* } => {
|
||||
let mut map = _VariableMap::with_capacity(#field_count);
|
||||
map.insert(_Symbol::from(#type_key), _Variable::String(_Rc::from(#variant_name)));
|
||||
map.insert(_Symbol::from(#type_key), _Variable::String(_Symbol::from_static(#variant_name)));
|
||||
#(#field_mappings)*
|
||||
_Variable::from_object(map)
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#![deny(unsafe_code)]
|
||||
#![forbid(unsafe_code)]
|
||||
|
||||
mod constant;
|
||||
pub mod decision;
|
||||
|
||||
+3
-313
@@ -1,314 +1,4 @@
|
||||
#![allow(unsafe_code)]
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
|
||||
use std::cell::{Cell, RefCell, UnsafeCell};
|
||||
use std::fmt::{Debug, Formatter};
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::ptr::NonNull;
|
||||
|
||||
const POOL_CAP: usize = 512;
|
||||
const RETAINED_CAPACITY: usize = 64;
|
||||
|
||||
pub struct PoolStore {
|
||||
slots: Vec<NonNull<u8>>,
|
||||
free: unsafe fn(NonNull<u8>),
|
||||
}
|
||||
|
||||
impl PoolStore {
|
||||
pub fn new(free: unsafe fn(NonNull<u8>)) -> Self {
|
||||
Self {
|
||||
slots: Vec::new(),
|
||||
free,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for PoolStore {
|
||||
fn drop(&mut self) {
|
||||
for slot in self.slots.drain(..) {
|
||||
unsafe { (self.free)(slot) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Recycle: Sized + Default + 'static {
|
||||
fn with_pool<R>(f: impl FnOnce(&mut PoolStore) -> R) -> Option<R>;
|
||||
|
||||
fn recycle(&mut self);
|
||||
}
|
||||
|
||||
struct Inner<T> {
|
||||
strong: Cell<usize>,
|
||||
borrow: Cell<isize>,
|
||||
value: UnsafeCell<T>,
|
||||
}
|
||||
|
||||
unsafe fn free_slot<T>(ptr: NonNull<u8>) {
|
||||
unsafe { drop(Box::from_raw(ptr.cast::<Inner<T>>().as_ptr())) }
|
||||
}
|
||||
|
||||
pub struct RcCell<T: Recycle> {
|
||||
ptr: NonNull<Inner<T>>,
|
||||
}
|
||||
|
||||
impl<T: Recycle> RcCell<T> {
|
||||
pub fn new(value: T) -> Self {
|
||||
match Self::take_slot() {
|
||||
Some(ptr) => {
|
||||
unsafe { *ptr.as_ref().value.get() = value }
|
||||
Self { ptr }
|
||||
}
|
||||
None => Self::boxed(value),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn recycled() -> Self {
|
||||
match Self::take_slot() {
|
||||
Some(ptr) => Self { ptr },
|
||||
None => Self::boxed(T::default()),
|
||||
}
|
||||
}
|
||||
|
||||
fn boxed(value: T) -> Self {
|
||||
let inner = Box::new(Inner {
|
||||
strong: Cell::new(1),
|
||||
borrow: Cell::new(0),
|
||||
value: UnsafeCell::new(value),
|
||||
});
|
||||
|
||||
Self {
|
||||
ptr: unsafe { NonNull::new_unchecked(Box::into_raw(inner)) },
|
||||
}
|
||||
}
|
||||
|
||||
fn take_slot() -> Option<NonNull<Inner<T>>> {
|
||||
let slot = T::with_pool(|pool| pool.slots.pop())??;
|
||||
let ptr = slot.cast::<Inner<T>>();
|
||||
unsafe {
|
||||
ptr.as_ref().strong.set(1);
|
||||
ptr.as_ref().borrow.set(0);
|
||||
}
|
||||
|
||||
Some(ptr)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn inner(&self) -> &Inner<T> {
|
||||
unsafe { self.ptr.as_ref() }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn borrow(&self) -> Ref<'_, T> {
|
||||
let inner = self.inner();
|
||||
let state = inner.borrow.get();
|
||||
if state < 0 {
|
||||
panic!("RcCell is already mutably borrowed");
|
||||
}
|
||||
|
||||
inner.borrow.set(state + 1);
|
||||
Ref { inner }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn borrow_mut(&self) -> RefMut<'_, T> {
|
||||
let inner = self.inner();
|
||||
if inner.borrow.get() != 0 {
|
||||
panic!("RcCell is already borrowed");
|
||||
}
|
||||
|
||||
inner.borrow.set(-1);
|
||||
RefMut { inner }
|
||||
}
|
||||
|
||||
pub fn try_borrow(&self) -> Option<Ref<'_, T>> {
|
||||
let inner = self.inner();
|
||||
let state = inner.borrow.get();
|
||||
if state < 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
inner.borrow.set(state + 1);
|
||||
Some(Ref { inner })
|
||||
}
|
||||
|
||||
pub fn try_borrow_mut(&self) -> Option<RefMut<'_, T>> {
|
||||
let inner = self.inner();
|
||||
if inner.borrow.get() != 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
inner.borrow.set(-1);
|
||||
Some(RefMut { inner })
|
||||
}
|
||||
|
||||
/// Reads through the cell without tracking a borrow. The caller guarantees
|
||||
/// no `borrow_mut` overlaps the returned reference, and that the `RcCell`
|
||||
/// outlives it.
|
||||
pub unsafe fn get_ref<'b>(&self) -> &'b T {
|
||||
unsafe { &*self.inner().value.get() }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn ptr_eq(a: &Self, b: &Self) -> bool {
|
||||
a.ptr == b.ptr
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn as_ptr(this: &Self) -> *const T {
|
||||
this.inner().value.get()
|
||||
}
|
||||
|
||||
pub fn strong_count(this: &Self) -> usize {
|
||||
this.inner().strong.get()
|
||||
}
|
||||
|
||||
pub fn try_unwrap(this: Self) -> Result<T, Self> {
|
||||
if this.inner().strong.get() != 1 {
|
||||
return Err(this);
|
||||
}
|
||||
|
||||
Ok(unsafe { std::mem::take(&mut *this.inner().value.get()) })
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Recycle> Clone for RcCell<T> {
|
||||
#[inline]
|
||||
fn clone(&self) -> Self {
|
||||
let inner = self.inner();
|
||||
inner.strong.set(inner.strong.get() + 1);
|
||||
|
||||
Self { ptr: self.ptr }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Recycle> Drop for RcCell<T> {
|
||||
fn drop(&mut self) {
|
||||
let inner = self.inner();
|
||||
let strong = inner.strong.get() - 1;
|
||||
inner.strong.set(strong);
|
||||
if strong != 0 {
|
||||
return;
|
||||
}
|
||||
|
||||
// Runs before the pool is touched: recycling drops nested cells, which
|
||||
// re-enter the pool for their own type.
|
||||
unsafe { (*inner.value.get()).recycle() }
|
||||
inner.borrow.set(0);
|
||||
|
||||
let ptr = self.ptr;
|
||||
let pooled = T::with_pool(|pool| {
|
||||
if pool.slots.len() >= POOL_CAP {
|
||||
return false;
|
||||
}
|
||||
|
||||
pool.slots.push(ptr.cast::<u8>());
|
||||
true
|
||||
});
|
||||
|
||||
if pooled != Some(true) {
|
||||
unsafe { drop(Box::from_raw(ptr.as_ptr())) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Recycle> Default for RcCell<T> {
|
||||
fn default() -> Self {
|
||||
Self::recycled()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Recycle + PartialEq> PartialEq for RcCell<T> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
Self::ptr_eq(self, other) || *self.borrow() == *other.borrow()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Recycle + Eq> Eq for RcCell<T> {}
|
||||
|
||||
impl<T: Recycle + serde::Serialize> serde::Serialize for RcCell<T> {
|
||||
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
self.borrow().serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Recycle + Debug> Debug for RcCell<T> {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
Debug::fmt(&*self.borrow(), f)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Ref<'a, T: Recycle> {
|
||||
inner: &'a Inner<T>,
|
||||
}
|
||||
|
||||
impl<'a, T: Recycle> Deref for Ref<'a, T> {
|
||||
type Target = T;
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &T {
|
||||
unsafe { &*self.inner.value.get() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Recycle> Drop for Ref<'a, T> {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
self.inner.borrow.set(self.inner.borrow.get() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RefMut<'a, T: Recycle> {
|
||||
inner: &'a Inner<T>,
|
||||
}
|
||||
|
||||
impl<'a, T: Recycle> Deref for RefMut<'a, T> {
|
||||
type Target = T;
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &T {
|
||||
unsafe { &*self.inner.value.get() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Recycle> DerefMut for RefMut<'a, T> {
|
||||
#[inline]
|
||||
fn deref_mut(&mut self) -> &mut T {
|
||||
unsafe { &mut *self.inner.value.get() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Recycle> Drop for RefMut<'a, T> {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
self.inner.borrow.set(0);
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! recyclable {
|
||||
($t:ty, $tls:ident, $reset:expr) => {
|
||||
thread_local! {
|
||||
static $tls: RefCell<PoolStore> = RefCell::new(PoolStore::new(free_slot::<$t>));
|
||||
}
|
||||
|
||||
impl Recycle for $t {
|
||||
#[inline]
|
||||
fn with_pool<R>(f: impl FnOnce(&mut PoolStore) -> R) -> Option<R> {
|
||||
$tls.try_with(|pool| f(&mut pool.borrow_mut())).ok()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn recycle(&mut self) {
|
||||
let reset: fn(&mut $t) = $reset;
|
||||
reset(self)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
recyclable!(Vec<crate::variable::Variable>, ARRAY_POOL, |vec| {
|
||||
vec.clear();
|
||||
if vec.capacity() > RETAINED_CAPACITY {
|
||||
*vec = Vec::new();
|
||||
}
|
||||
});
|
||||
|
||||
recyclable!(crate::variable::VariableMap, OBJECT_POOL, |map| map.reset());
|
||||
pub type RcCell<T> = Rc<RefCell<T>>;
|
||||
|
||||
@@ -119,18 +119,22 @@ impl From<Variable> for Value {
|
||||
}
|
||||
Variable::String(s) => Value::String(s.to_string()),
|
||||
Variable::Array(arr) => {
|
||||
let vec = RcCell::try_unwrap(arr).unwrap_or_else(|s| {
|
||||
let borrowed = s.borrow();
|
||||
borrowed.clone()
|
||||
});
|
||||
let vec = RcCell::try_unwrap(arr)
|
||||
.map(|cell| cell.into_inner())
|
||||
.unwrap_or_else(|s| {
|
||||
let borrowed = s.borrow();
|
||||
borrowed.clone()
|
||||
});
|
||||
|
||||
Value::Array(vec.into_iter().map(Value::from).collect())
|
||||
}
|
||||
Variable::Object(obj) => {
|
||||
let hmap = RcCell::try_unwrap(obj).unwrap_or_else(|s| {
|
||||
let borrowed = s.borrow();
|
||||
borrowed.clone()
|
||||
});
|
||||
let hmap = RcCell::try_unwrap(obj)
|
||||
.map(|cell| cell.into_inner())
|
||||
.unwrap_or_else(|s| {
|
||||
let borrowed = s.borrow();
|
||||
borrowed.clone()
|
||||
});
|
||||
|
||||
Value::Object(
|
||||
hmap.into_iter()
|
||||
|
||||
@@ -49,18 +49,6 @@ impl VariableMap {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reset(&mut self) {
|
||||
match &mut self.0 {
|
||||
Repr::Small(entries) => {
|
||||
entries.clear();
|
||||
if entries.spilled() {
|
||||
*entries = Entries::new();
|
||||
}
|
||||
}
|
||||
Repr::Large(_) => self.0 = Repr::Small(Entries::new()),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get(&self, key: &Symbol) -> Option<&Variable> {
|
||||
match &self.0 {
|
||||
|
||||
@@ -5,6 +5,7 @@ use rust_decimal::Decimal;
|
||||
use rust_decimal::prelude::Zero;
|
||||
use serde_json::Value;
|
||||
use std::any::Any;
|
||||
use std::cell::RefCell;
|
||||
use std::fmt::{Debug, Display, Formatter};
|
||||
use std::ops::Deref;
|
||||
use std::rc::Rc;
|
||||
@@ -27,6 +28,11 @@ pub use crate::rccell::RcCell;
|
||||
|
||||
pub use crate::variable::map::{Iter as MapIter, VariableMap};
|
||||
|
||||
thread_local! {
|
||||
static DOLLAR_KEY_RC: Rc<str> = Rc::from("$");
|
||||
static ROOT_KEY_RC: Rc<str> = Rc::from("$root");
|
||||
}
|
||||
|
||||
pub enum Variable {
|
||||
Null,
|
||||
Bool(bool),
|
||||
@@ -62,8 +68,16 @@ impl Variable {
|
||||
Symbol::from(name)
|
||||
}
|
||||
|
||||
pub fn dollar_key_rc() -> Rc<str> {
|
||||
DOLLAR_KEY_RC.with(Rc::clone)
|
||||
}
|
||||
|
||||
pub fn root_key_rc() -> Rc<str> {
|
||||
ROOT_KEY_RC.with(Rc::clone)
|
||||
}
|
||||
|
||||
pub fn from_array(arr: Vec<Self>) -> Self {
|
||||
Self::Array(RcCell::new(arr))
|
||||
Self::Array(Rc::new(RefCell::new(arr)))
|
||||
}
|
||||
|
||||
pub fn serialize_ref(&self) -> RcValue {
|
||||
@@ -75,7 +89,7 @@ impl Variable {
|
||||
}
|
||||
|
||||
pub fn from_object(obj: VariableMap) -> Self {
|
||||
Self::Object(RcCell::new(obj))
|
||||
Self::Object(Rc::new(RefCell::new(obj)))
|
||||
}
|
||||
|
||||
pub fn empty_object() -> Self {
|
||||
@@ -413,7 +427,7 @@ fn merge_variables(
|
||||
// Only update doc if changes were made
|
||||
if changed {
|
||||
if let Some(new_map) = new_map {
|
||||
*doc = Variable::Object(RcCell::new(new_map));
|
||||
*doc = Variable::Object(Rc::new(RefCell::new(new_map)));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
use crate::rccell::RcCell;
|
||||
use crate::rcvalue::RcValue;
|
||||
use crate::variable::Variable;
|
||||
use crate::variable::VariableMap;
|
||||
@@ -27,10 +26,10 @@ impl RefDeserializer {
|
||||
for (i, _) in refs_array.iter().enumerate() {
|
||||
match &refs_array[i] {
|
||||
RcValue::Array(_) => {
|
||||
self.refs[i] = Some(Variable::Array(RcCell::new(Vec::new())));
|
||||
self.refs[i] = Some(Variable::from_array(Vec::new()));
|
||||
}
|
||||
RcValue::Object(_) => {
|
||||
self.refs[i] = Some(Variable::Object(RcCell::new(VariableMap::default())));
|
||||
self.refs[i] = Some(Variable::from_object(VariableMap::default()));
|
||||
}
|
||||
_ => {
|
||||
self.refs[i] = Some(self.deserialize_value(&refs_array[i])?);
|
||||
@@ -113,7 +112,7 @@ impl RefDeserializer {
|
||||
for item in arr {
|
||||
items.push(self.deserialize_value(item)?);
|
||||
}
|
||||
Ok(Variable::Array(RcCell::new(items)))
|
||||
Ok(Variable::from_array(items))
|
||||
}
|
||||
RcValue::Object(obj) => {
|
||||
let mut map = VariableMap::with_capacity(obj.len());
|
||||
@@ -122,7 +121,7 @@ impl RefDeserializer {
|
||||
let value_var = self.deserialize_value(value)?;
|
||||
map.insert(key_var, value_var);
|
||||
}
|
||||
Ok(Variable::Object(RcCell::new(map)))
|
||||
Ok(Variable::from_object(map))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ impl RefSerializer {
|
||||
result.insert(Rc::from("$refs"), RcValue::Array(self.ref_data));
|
||||
}
|
||||
|
||||
result.insert(Rc::from("$root"), data);
|
||||
result.insert(Variable::root_key_rc(), data);
|
||||
RcValue::Object(result)
|
||||
}
|
||||
|
||||
|
||||
+10
-10
@@ -1,8 +1,8 @@
|
||||
use rust_decimal_macros::dec;
|
||||
use serde_json::json;
|
||||
use std::cell::RefCell;
|
||||
use std::error::Error;
|
||||
use std::rc::Rc;
|
||||
use zen_types::rccell::RcCell;
|
||||
use zen_types::rcvalue::RcValue;
|
||||
use zen_types::variable::Variable;
|
||||
use zen_types::variable::VariableMap as HashMap;
|
||||
@@ -145,7 +145,7 @@ fn no_refs_when_below_threshold() -> TestResult {
|
||||
#[test]
|
||||
fn serialize_circular_references() -> TestResult {
|
||||
// Create a shared object that will be referenced multiple times
|
||||
let shared_obj = RcCell::new({
|
||||
let shared_obj = Rc::new(RefCell::new({
|
||||
let mut map = HashMap::new();
|
||||
map.insert(
|
||||
zen_types::symbol::Symbol::from("shared_data"),
|
||||
@@ -156,7 +156,7 @@ fn serialize_circular_references() -> TestResult {
|
||||
Variable::Number(dec!(42.0)),
|
||||
);
|
||||
map
|
||||
});
|
||||
}));
|
||||
|
||||
// Create a structure where the same object appears in multiple places
|
||||
let mut root_map = HashMap::new();
|
||||
@@ -173,7 +173,7 @@ fn serialize_circular_references() -> TestResult {
|
||||
Variable::Object(shared_obj),
|
||||
);
|
||||
|
||||
let var = Variable::Object(RcCell::new(root_map));
|
||||
let var = Variable::Object(Rc::new(RefCell::new(root_map)));
|
||||
|
||||
let serialized = var.serialize_ref();
|
||||
let deserialized = Variable::deserialize_ref(serialized)?;
|
||||
@@ -186,11 +186,11 @@ fn serialize_circular_references() -> TestResult {
|
||||
#[test]
|
||||
fn serialize_same_array_multiple_locations() -> TestResult {
|
||||
// Create a shared array
|
||||
let shared_array = RcCell::new(vec![
|
||||
let shared_array = Rc::new(RefCell::new(vec![
|
||||
Variable::String(("item_one").into()),
|
||||
Variable::String(("item_two").into()),
|
||||
Variable::Number(dec!(123.0)),
|
||||
]);
|
||||
]));
|
||||
|
||||
let var = Variable::from(json!({
|
||||
"list1": shared_array.clone(),
|
||||
@@ -223,7 +223,7 @@ fn serialize_mixed_shared_references() -> TestResult {
|
||||
Variable::String(shared_string.as_ref().into()),
|
||||
);
|
||||
|
||||
let shared_obj = RcCell::new(obj_map);
|
||||
let shared_obj = Rc::new(RefCell::new(obj_map));
|
||||
|
||||
// Use the shared object in multiple places
|
||||
let var = Variable::from(json!({
|
||||
@@ -247,11 +247,11 @@ fn serialize_shared_array_with_shared_strings() -> TestResult {
|
||||
let shared_string: Rc<str> = Rc::from("shared_string_value");
|
||||
|
||||
// Create a shared array containing the shared string
|
||||
let shared_array = RcCell::new(vec![
|
||||
let shared_array = Rc::new(RefCell::new(vec![
|
||||
Variable::String(shared_string.as_ref().into()),
|
||||
Variable::Number(dec!(42.0)),
|
||||
Variable::String(shared_string.as_ref().into()),
|
||||
]);
|
||||
]));
|
||||
|
||||
// Use the shared array in multiple places
|
||||
let mut root_map = HashMap::new();
|
||||
@@ -268,7 +268,7 @@ fn serialize_shared_array_with_shared_strings() -> TestResult {
|
||||
Variable::Array(shared_array),
|
||||
);
|
||||
|
||||
let var = Variable::Object(RcCell::new(root_map));
|
||||
let var = Variable::Object(Rc::new(RefCell::new(root_map)));
|
||||
|
||||
let serialized = var.serialize_ref();
|
||||
let deserialized = Variable::deserialize_ref(serialized)?;
|
||||
|
||||
Reference in New Issue
Block a user