git.delta.rocks / jrsonnet / refs/commits / 23d571a0df03

difftreelog

refactor move arrays to use dyn ArrayLike

Yaroslav Bolyukin2023-08-06parent: #09dae32.patch.diff
in: master

8 files changed

modifiedcrates/jrsonnet-evaluator/src/arr/mod.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/arr/mod.rs
+++ b/crates/jrsonnet-evaluator/src/arr/mod.rs
@@ -1,44 +1,20 @@
-use std::rc::Rc;
+use std::any::Any;
 
 use jrsonnet_gcmodule::{Cc, Trace};
 use jrsonnet_interner::IBytes;
 use jrsonnet_parser::LocExpr;
 
-use crate::{function::FuncVal, Context, Result, Thunk, Val};
+use crate::{function::FuncVal, gc::TraceBox, tb, Context, Result, Thunk, Val};
 
 mod spec;
-use spec::*;
+pub use spec::ArrayLike;
+pub(crate) use spec::*;
 
 /// Represents a Jsonnet array value.
 #[derive(Debug, Clone, Trace)]
 // may contrain other ArrValue
 #[trace(tracking(force))]
-pub enum ArrValue {
-	/// Layout optimized byte array.
-	Bytes(BytesArray),
-	/// Layout optimized char array.
-	Chars(CharArray),
-	/// Every element is lazy evaluated.
-	Lazy(LazyArray),
-	/// Every element is defined somewhere in source code
-	Expr(ExprArray),
-	/// Every field is already evaluated.
-	Eager(EagerArray),
-	/// Concatenation of two arrays of any kind.
-	Extended(Cc<ExtendedArray>),
-	/// Represents a integer array in form `[start, start + 1, ... end - 1, end]`.
-	/// This kind of arrays is generated by `std.range(start, end)` call, and used for loops.
-	Range(RangeArray),
-	/// Sliced array view.
-	Slice(Cc<SliceArray>),
-	/// Reversed array view.
-	/// Returned by `std.reverse(other)` call
-	Reverse(Cc<ReverseArray>),
-	/// Returned by `std.map` call
-	Mapped(MappedArray),
-	/// Returned by `std.repeat` call
-	Repeated(RepeatedArray),
-}
+pub struct ArrValue(Cc<TraceBox<dyn ArrayLike>>);
 
 pub trait ArrayLikeIter<T>: Iterator<Item = T> + DoubleEndedIterator + ExactSizeIterator {}
 impl<I, T> ArrayLikeIter<T> for I where
@@ -47,36 +23,39 @@
 }
 
 impl ArrValue {
+	pub fn new(v: impl ArrayLike) -> Self {
+		Self(Cc::new(tb!(v)))
+	}
 	pub fn empty() -> Self {
-		Self::Range(RangeArray::empty())
+		Self::new(RangeArray::empty())
 	}
 
 	pub fn expr(ctx: Context, exprs: impl IntoIterator<Item = LocExpr>) -> Self {
-		Self::Expr(ExprArray::new(ctx, exprs))
+		Self::new(ExprArray::new(ctx, exprs))
 	}
 
-	pub fn lazy(thunks: Cc<Vec<Thunk<Val>>>) -> Self {
-		Self::Lazy(LazyArray(thunks))
+	pub fn lazy(thunks: Vec<Thunk<Val>>) -> Self {
+		Self::new(LazyArray(thunks))
 	}
 
 	pub fn eager(values: Vec<Val>) -> Self {
-		Self::Eager(EagerArray(Cc::new(values)))
+		Self::new(EagerArray(values))
 	}
 
 	pub fn repeated(data: ArrValue, repeats: usize) -> Option<Self> {
-		Some(Self::Repeated(RepeatedArray::new(data, repeats)?))
+		Some(Self::new(RepeatedArray::new(data, repeats)?))
 	}
 
 	pub fn bytes(bytes: IBytes) -> Self {
-		Self::Bytes(BytesArray(bytes))
+		Self::new(BytesArray(bytes))
 	}
 	pub fn chars(chars: impl Iterator<Item = char>) -> Self {
-		Self::Chars(CharArray(Rc::new(chars.collect())))
+		Self::new(CharArray(chars.collect()))
 	}
 
 	#[must_use]
 	pub fn map(self, mapper: FuncVal) -> Self {
-		Self::Mapped(MappedArray::new(self, mapper))
+		Self::new(MappedArray::new(self, mapper))
 	}
 
 	pub fn filter(self, filter: impl Fn(&Val) -> Result<bool>) -> Result<Self> {
@@ -100,7 +79,7 @@
 		} else if b.is_empty() {
 			a
 		} else if a.len() + b.len() > ARR_EXTEND_THRESHOLD {
-			Self::Extended(Cc::new(ExtendedArray::new(a, b)))
+			Self::new(ExtendedArray::new(a, b))
 		} else if let (Some(a), Some(b)) = (a.iter_cheap(), b.iter_cheap()) {
 			let mut out = Vec::with_capacity(a.len() + b.len());
 			out.extend(a);
@@ -110,15 +89,15 @@
 			let mut out = Vec::with_capacity(a.len() + b.len());
 			out.extend(a.iter_lazy());
 			out.extend(b.iter_lazy());
-			Self::lazy(Cc::new(out))
+			Self::lazy(out)
 		}
 	}
 
 	pub fn range_exclusive(a: i32, b: i32) -> Self {
-		Self::Range(RangeArray::new_exclusive(a, b))
+		Self::new(RangeArray::new_exclusive(a, b))
 	}
 	pub fn range_inclusive(a: i32, b: i32) -> Self {
-		Self::Range(RangeArray::new_inclusive(a, b))
+		Self::new(RangeArray::new_inclusive(a, b))
 	}
 
 	#[must_use]
@@ -136,53 +115,42 @@
 		if from >= to || step == 0 {
 			return None;
 		}
-		// match self {
-		// 	ArrValue::Slice(slice) => {
-		// 		return Some(Self::Slice(Cc::new(SliceArray {
-		// 			inner: slice.inner.clone(),
-		// 			from: slice.from + slice.step * (from as u32),
-		// 			to: slice.from + (to as u32) * slice.step,
-		// 			step: slice.step * step as u32,
-		// 		})))
-		// 	}
-		// 	_ => {}
-		// }
 
-		Some(Self::Slice(Cc::new(SliceArray {
+		Some(Self::new(SliceArray {
 			inner: self,
 			from: from as u32,
 			to: to as u32,
 			step: step as u32,
-		})))
+		}))
 	}
 
 	/// Array length.
 	pub fn len(&self) -> usize {
-		pass!(self.len())
+		self.0.len()
 	}
 
 	/// Is array contains no elements?
 	pub fn is_empty(&self) -> bool {
-		pass!(self.is_empty())
+		self.0.is_empty()
 	}
 
 	/// Get array element by index, evaluating it, if it is lazy.
 	///
 	/// Returns `None` on out-of-bounds condition.
 	pub fn get(&self, index: usize) -> Result<Option<Val>> {
-		pass!(self.get(index))
+		self.0.get(index)
 	}
 
 	/// Returns None if get is either non cheap, or out of bounds
 	fn get_cheap(&self, index: usize) -> Option<Val> {
-		pass!(self.get_cheap(index))
+		self.0.get_cheap(index)
 	}
 
 	/// Get array element by index, without evaluation.
 	///
 	/// Returns `None` on out-of-bounds condition.
 	pub fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {
-		pass!(self.get_lazy(index))
+		self.0.get_lazy(index)
 	}
 
 	pub fn iter(&self) -> impl ArrayLikeIter<Result<Val>> + '_ {
@@ -205,33 +173,20 @@
 	/// Return a reversed view on current array.
 	#[must_use]
 	pub fn reversed(self) -> Self {
-		Self::Reverse(Cc::new(ReverseArray(self)))
+		Self::new(ReverseArray(self))
 	}
 
 	pub fn ptr_eq(a: &Self, b: &Self) -> bool {
-		match (a, b) {
-			(ArrValue::Bytes(a), ArrValue::Bytes(b)) => a.0 == b.0,
-			(ArrValue::Lazy(a), ArrValue::Lazy(b)) => Cc::ptr_eq(&a.0, &b.0),
-			(ArrValue::Expr(a), ArrValue::Expr(b)) => Cc::ptr_eq(&a.0, &b.0),
-			(ArrValue::Eager(a), ArrValue::Eager(b)) => Cc::ptr_eq(&a.0, &b.0),
-			(ArrValue::Extended(a), ArrValue::Extended(b)) => Cc::ptr_eq(a, b),
-			(ArrValue::Range(a), ArrValue::Range(b)) => a == b,
-			_ => false,
-		}
+		Cc::ptr_eq(&a.0, &b.0)
 	}
 
 	/// Is this vec supports `.get_cheap()?`
 	pub fn is_cheap(&self) -> bool {
-		match self {
-			ArrValue::Eager(_) | ArrValue::Range(..) | ArrValue::Bytes(_) | ArrValue::Chars(_) => {
-				true
-			}
-			ArrValue::Extended(v) => v.a.is_cheap() && v.b.is_cheap(),
-			ArrValue::Slice(r) => r.inner.is_cheap(),
-			ArrValue::Reverse(i) => i.0.is_cheap(),
-			ArrValue::Repeated(v) => v.is_cheap(),
-			ArrValue::Expr(_) | ArrValue::Lazy(_) | ArrValue::Mapped(_) => false,
-		}
+		self.0.is_cheap()
+	}
+
+	pub fn as_any(&self) -> &dyn Any {
+		&self.0
 	}
 }
 impl From<Vec<Val>> for ArrValue {
@@ -241,7 +196,7 @@
 }
 impl From<Vec<Thunk<Val>>> for ArrValue {
 	fn from(value: Vec<Thunk<Val>>) -> Self {
-		Self::lazy(Cc::new(value))
+		Self::lazy(value)
 	}
 }
 impl FromIterator<Val> for ArrValue {
@@ -249,6 +204,27 @@
 		Self::eager(iter.into_iter().collect())
 	}
 }
+impl ArrayLike for ArrValue {
+	fn len(&self) -> usize {
+		self.0.len()
+	}
 
+	fn get(&self, index: usize) -> Result<Option<Val>> {
+		self.0.get(index)
+	}
+
+	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {
+		self.0.get_lazy(index)
+	}
+
+	fn get_cheap(&self, index: usize) -> Option<Val> {
+		self.0.get_cheap(index)
+	}
+
+	fn is_cheap(&self) -> bool {
+		self.0.is_cheap()
+	}
+}
+
 #[cfg(target_pointer_width = "64")]
-static_assertions::assert_eq_size!(ArrValue, [u8; 16]);
+static_assertions::assert_eq_size!(ArrValue, [u8; 8]);
modifiedcrates/jrsonnet-evaluator/src/arr/spec.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/arr/spec.rs
+++ b/crates/jrsonnet-evaluator/src/arr/spec.rs
@@ -1,4 +1,4 @@
-use std::{cell::RefCell, iter, mem::replace, rc::Rc};
+use std::{any::Any, cell::RefCell, fmt::Debug, iter, mem::replace};
 
 use jrsonnet_gcmodule::{Cc, Trace};
 use jrsonnet_interner::{IBytes, IStr};
@@ -13,7 +13,7 @@
 	Context, Error, Result, Thunk, Val,
 };
 
-pub trait ArrayLike: Sized + Into<ArrValue> {
+pub trait ArrayLike: Any + Trace + Debug {
 	fn len(&self) -> usize;
 	fn is_empty(&self) -> bool {
 		self.len() == 0
@@ -22,12 +22,10 @@
 	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>>;
 	fn get_cheap(&self, index: usize) -> Option<Val>;
 
-	fn reverse(self) -> ArrValue {
-		ArrValue::Reverse(Cc::new(ReverseArray(self.into())))
-	}
+	fn is_cheap(&self) -> bool;
 }
 
-#[derive(Debug, Clone, Trace)]
+#[derive(Debug, Trace)]
 pub struct SliceArray {
 	pub(crate) inner: ArrValue,
 	pub(crate) from: u32,
@@ -81,15 +79,13 @@
 	fn get_cheap(&self, index: usize) -> Option<Val> {
 		self.iter_cheap()?.nth(index)
 	}
-}
-impl From<SliceArray> for ArrValue {
-	fn from(value: SliceArray) -> Self {
-		Self::Slice(Cc::new(value))
+	fn is_cheap(&self) -> bool {
+		self.inner.is_cheap()
 	}
 }
 
-#[derive(Trace, Debug, Clone)]
-pub struct CharArray(pub Rc<Vec<char>>);
+#[derive(Trace, Debug)]
+pub struct CharArray(pub Vec<char>);
 impl ArrayLike for CharArray {
 	fn len(&self) -> usize {
 		self.0.len()
@@ -108,14 +104,12 @@
 			.get(index)
 			.map(|v| Val::Str(StrValue::Flat(IStr::from(*v))))
 	}
-}
-impl From<CharArray> for ArrValue {
-	fn from(value: CharArray) -> Self {
-		ArrValue::Chars(value)
+	fn is_cheap(&self) -> bool {
+		true
 	}
 }
 
-#[derive(Trace, Debug, Clone)]
+#[derive(Trace, Debug)]
 pub struct BytesArray(pub IBytes);
 impl ArrayLike for BytesArray {
 	fn len(&self) -> usize {
@@ -133,10 +127,8 @@
 	fn get_cheap(&self, index: usize) -> Option<Val> {
 		self.0.get(index).map(|v| Val::Num(f64::from(*v)))
 	}
-}
-impl From<BytesArray> for ArrValue {
-	fn from(value: BytesArray) -> Self {
-		ArrValue::Bytes(value)
+	fn is_cheap(&self) -> bool {
+		true
 	}
 }
 
@@ -148,30 +140,30 @@
 	Pending,
 }
 
-#[derive(Debug, Trace)]
-pub struct ExprArrayInner {
+#[derive(Debug, Trace, Clone)]
+pub struct ExprArray {
 	ctx: Context,
-	cached: RefCell<Vec<ArrayThunk<LocExpr>>>,
+	cached: Cc<RefCell<Vec<ArrayThunk<LocExpr>>>>,
 }
-#[derive(Debug, Trace, Clone)]
-pub struct ExprArray(pub Cc<ExprArrayInner>);
 impl ExprArray {
 	pub fn new(ctx: Context, items: impl IntoIterator<Item = LocExpr>) -> Self {
-		Self(Cc::new(ExprArrayInner {
+		Self {
 			ctx,
-			cached: RefCell::new(items.into_iter().map(ArrayThunk::Waiting).collect()),
-		}))
+			cached: Cc::new(RefCell::new(
+				items.into_iter().map(ArrayThunk::Waiting).collect(),
+			)),
+		}
 	}
 }
 impl ArrayLike for ExprArray {
 	fn len(&self) -> usize {
-		self.0.cached.borrow().len()
+		self.cached.borrow().len()
 	}
 	fn get(&self, index: usize) -> Result<Option<Val>> {
 		if index >= self.len() {
 			return Ok(None);
 		}
-		match &self.0.cached.borrow()[index] {
+		match &self.cached.borrow()[index] {
 			ArrayThunk::Computed(c) => return Ok(Some(c.clone())),
 			ArrayThunk::Errored(e) => return Err(e.clone()),
 			ArrayThunk::Pending => return Err(InfiniteRecursionDetected.into()),
@@ -179,19 +171,19 @@
 		};
 
 		let ArrayThunk::Waiting(expr) =
-			replace(&mut self.0.cached.borrow_mut()[index], ArrayThunk::Pending)
+			replace(&mut self.cached.borrow_mut()[index], ArrayThunk::Pending)
 		else {
 			unreachable!()
 		};
 
-		let new_value = match evaluate(self.0.ctx.clone(), &expr) {
+		let new_value = match evaluate(self.ctx.clone(), &expr) {
 			Ok(v) => v,
 			Err(e) => {
-				self.0.cached.borrow_mut()[index] = ArrayThunk::Errored(e.clone());
+				self.cached.borrow_mut()[index] = ArrayThunk::Errored(e.clone());
 				return Err(e);
 			}
 		};
-		self.0.cached.borrow_mut()[index] = ArrayThunk::Computed(new_value.clone());
+		self.cached.borrow_mut()[index] = ArrayThunk::Computed(new_value.clone());
 		Ok(Some(new_value))
 	}
 	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {
@@ -215,7 +207,7 @@
 		if index >= self.len() {
 			return None;
 		}
-		match &self.0.cached.borrow()[index] {
+		match &self.cached.borrow()[index] {
 			ArrayThunk::Computed(c) => return Some(Thunk::evaluated(c.clone())),
 			ArrayThunk::Errored(e) => return Some(Thunk::errored(e.clone())),
 			ArrayThunk::Waiting(_) | ArrayThunk::Pending => {}
@@ -229,14 +221,12 @@
 	fn get_cheap(&self, _index: usize) -> Option<Val> {
 		None
 	}
-}
-impl From<ExprArray> for ArrValue {
-	fn from(value: ExprArray) -> Self {
-		Self::Expr(value)
+	fn is_cheap(&self) -> bool {
+		false
 	}
 }
 
-#[derive(Trace, Debug, Clone)]
+#[derive(Trace, Debug)]
 pub struct ExtendedArray {
 	pub a: ArrValue,
 	pub b: ArrValue,
@@ -319,15 +309,13 @@
 			self.b.get_cheap(index - self.split)
 		}
 	}
-}
-impl From<ExtendedArray> for ArrValue {
-	fn from(value: ExtendedArray) -> Self {
-		Self::Extended(Cc::new(value))
+	fn is_cheap(&self) -> bool {
+		self.a.is_cheap() && self.b.is_cheap()
 	}
 }
 
-#[derive(Trace, Debug, Clone)]
-pub struct LazyArray(pub Cc<Vec<Thunk<Val>>>);
+#[derive(Trace, Debug)]
+pub struct LazyArray(pub Vec<Thunk<Val>>);
 impl ArrayLike for LazyArray {
 	fn len(&self) -> usize {
 		self.0.len()
@@ -344,15 +332,13 @@
 	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {
 		self.0.get(index).cloned()
 	}
-}
-impl From<LazyArray> for ArrValue {
-	fn from(value: LazyArray) -> Self {
-		Self::Lazy(value)
+	fn is_cheap(&self) -> bool {
+		false
 	}
 }
 
-#[derive(Trace, Debug, Clone)]
-pub struct EagerArray(pub Cc<Vec<Val>>);
+#[derive(Trace, Debug)]
+pub struct EagerArray(pub Vec<Val>);
 impl ArrayLike for EagerArray {
 	fn len(&self) -> usize {
 		self.0.len()
@@ -369,15 +355,13 @@
 	fn get_cheap(&self, index: usize) -> Option<Val> {
 		self.0.get(index).cloned()
 	}
-}
-impl From<EagerArray> for ArrValue {
-	fn from(value: EagerArray) -> Self {
-		Self::Eager(value)
+	fn is_cheap(&self) -> bool {
+		true
 	}
 }
 
 /// Inclusive range type
-#[derive(Debug, Trace, Clone, PartialEq, Eq)]
+#[derive(Debug, Trace, PartialEq, Eq)]
 pub struct RangeArray {
 	start: i32,
 	end: i32,
@@ -422,14 +406,12 @@
 	fn get_cheap(&self, index: usize) -> Option<Val> {
 		self.range().nth(index).map(|i| Val::Num(f64::from(i)))
 	}
-}
-impl From<RangeArray> for ArrValue {
-	fn from(value: RangeArray) -> Self {
-		Self::Range(value)
+	fn is_cheap(&self) -> bool {
+		true
 	}
 }
 
-#[derive(Debug, Trace, Clone)]
+#[derive(Debug, Trace)]
 pub struct ReverseArray(pub ArrValue);
 impl ArrayLike for ReverseArray {
 	fn len(&self) -> usize {
@@ -447,44 +429,37 @@
 	fn get_cheap(&self, index: usize) -> Option<Val> {
 		self.0.get_cheap(self.0.len() - index - 1)
 	}
-	fn reverse(self) -> ArrValue {
-		self.0
-	}
-}
-impl From<ReverseArray> for ArrValue {
-	fn from(value: ReverseArray) -> Self {
-		Self::Reverse(Cc::new(value))
+	fn is_cheap(&self) -> bool {
+		self.0.is_cheap()
 	}
 }
 
-#[derive(Trace, Debug)]
-pub struct MappedArrayInner {
+#[derive(Trace, Debug, Clone)]
+pub struct MappedArray {
 	inner: ArrValue,
-	cached: RefCell<Vec<ArrayThunk<()>>>,
+	cached: Cc<RefCell<Vec<ArrayThunk<()>>>>,
 	mapper: FuncVal,
 }
-#[derive(Trace, Debug, Clone)]
-pub struct MappedArray(Cc<MappedArrayInner>);
 impl MappedArray {
 	pub fn new(inner: ArrValue, mapper: FuncVal) -> Self {
 		let len = inner.len();
-		Self(Cc::new(MappedArrayInner {
+		Self {
 			inner,
-			cached: RefCell::new(vec![ArrayThunk::Waiting(()); len]),
+			cached: Cc::new(RefCell::new(vec![ArrayThunk::Waiting(()); len])),
 			mapper,
-		}))
+		}
 	}
 }
 impl ArrayLike for MappedArray {
 	fn len(&self) -> usize {
-		self.0.cached.borrow().len()
+		self.cached.borrow().len()
 	}
 
 	fn get(&self, index: usize) -> Result<Option<Val>> {
 		if index >= self.len() {
 			return Ok(None);
 		}
-		match &self.0.cached.borrow()[index] {
+		match &self.cached.borrow()[index] {
 			ArrayThunk::Computed(c) => return Ok(Some(c.clone())),
 			ArrayThunk::Errored(e) => return Err(e.clone()),
 			ArrayThunk::Pending => return Err(InfiniteRecursionDetected.into()),
@@ -492,27 +467,26 @@
 		};
 
 		let ArrayThunk::Waiting(_) =
-			replace(&mut self.0.cached.borrow_mut()[index], ArrayThunk::Pending)
+			replace(&mut self.cached.borrow_mut()[index], ArrayThunk::Pending)
 		else {
 			unreachable!()
 		};
 
 		let val = self
-			.0
 			.inner
 			.get(index)
 			.transpose()
 			.expect("index checked")
-			.and_then(|r| self.0.mapper.evaluate_simple(&(r,), false));
+			.and_then(|r| self.mapper.evaluate_simple(&(r,), false));
 
 		let new_value = match val {
 			Ok(v) => v,
 			Err(e) => {
-				self.0.cached.borrow_mut()[index] = ArrayThunk::Errored(e.clone());
+				self.cached.borrow_mut()[index] = ArrayThunk::Errored(e.clone());
 				return Err(e);
 			}
 		};
-		self.0.cached.borrow_mut()[index] = ArrayThunk::Computed(new_value.clone());
+		self.cached.borrow_mut()[index] = ArrayThunk::Computed(new_value.clone());
 		Ok(Some(new_value))
 	}
 	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {
@@ -536,7 +510,7 @@
 		if index >= self.len() {
 			return None;
 		}
-		match &self.0.cached.borrow()[index] {
+		match &self.cached.borrow()[index] {
 			ArrayThunk::Computed(c) => return Some(Thunk::evaluated(c.clone())),
 			ArrayThunk::Errored(e) => return Some(Thunk::errored(e.clone())),
 			ArrayThunk::Waiting(_) | ArrayThunk::Pending => {}
@@ -551,82 +525,54 @@
 	fn get_cheap(&self, _index: usize) -> Option<Val> {
 		None
 	}
-}
-impl From<MappedArray> for ArrValue {
-	fn from(value: MappedArray) -> Self {
-		Self::Mapped(value)
+	fn is_cheap(&self) -> bool {
+		false
 	}
 }
 
 #[derive(Trace, Debug)]
-pub struct RepeatedArrayInner {
+pub struct RepeatedArray {
 	data: ArrValue,
 	repeats: usize,
 	total_len: usize,
 }
-#[derive(Trace, Debug, Clone)]
-pub struct RepeatedArray(Cc<RepeatedArrayInner>);
 impl RepeatedArray {
 	pub fn new(data: ArrValue, repeats: usize) -> Option<Self> {
 		let total_len = data.len().checked_mul(repeats)?;
-		Some(Self(Cc::new(RepeatedArrayInner {
+		Some(Self {
 			data,
 			repeats,
 			total_len,
-		})))
-	}
-	pub fn is_cheap(&self) -> bool {
-		self.0.data.is_cheap()
+		})
 	}
 }
 
 impl ArrayLike for RepeatedArray {
 	fn len(&self) -> usize {
-		self.0.total_len
+		self.total_len
 	}
 
 	fn get(&self, index: usize) -> Result<Option<Val>> {
-		if index > self.0.total_len {
+		if index > self.total_len {
 			return Ok(None);
 		}
-		self.0.data.get(index % self.0.data.len())
+		self.data.get(index % self.data.len())
 	}
 
 	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {
-		if index > self.0.total_len {
+		if index > self.total_len {
 			return None;
 		}
-		self.0.data.get_lazy(index % self.0.data.len())
+		self.data.get_lazy(index % self.data.len())
 	}
 
 	fn get_cheap(&self, index: usize) -> Option<Val> {
-		if index > self.0.total_len {
+		if index > self.total_len {
 			return None;
 		}
-		self.0.data.get_cheap(index % self.0.data.len())
+		self.data.get_cheap(index % self.data.len())
 	}
-}
-impl From<RepeatedArray> for ArrValue {
-	fn from(value: RepeatedArray) -> Self {
-		Self::Repeated(value)
+	fn is_cheap(&self) -> bool {
+		self.data.is_cheap()
 	}
 }
-
-macro_rules! pass {
-	($t:ident.$m:ident($($ident:ident),*)) => {
-		match $t {
-			Self::Bytes(e) => e.$m($($ident)*),
-			Self::Chars(e) => e.$m($($ident)*),
-			Self::Expr(e) => e.$m($($ident)*),
-			Self::Lazy(e) => e.$m($($ident)*),
-			Self::Eager(e) => e.$m($($ident)*),
-			Self::Range(e) => e.$m($($ident)*),
-			Self::Slice(e) => e.$m($($ident)*),
-			Self::Extended(e) => e.$m($($ident)*),
-			Self::Reverse(e) => e.$m($($ident)*),
-			Self::Mapped(e) => e.$m($($ident)*),
-			Self::Repeated(e) => e.$m($($ident)*),
-		}
-	};
-}
-pub(super) use pass;
modifiedcrates/jrsonnet-evaluator/src/evaluate/mod.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/evaluate/mod.rs
+++ b/crates/jrsonnet-evaluator/src/evaluate/mod.rs
@@ -573,10 +573,10 @@
 						evaluate(self.ctx, &self.item)
 					}
 				}
-				Val::Arr(ArrValue::lazy(Cc::new(vec![Thunk::new(ArrayElement {
+				Val::Arr(ArrValue::lazy(vec![Thunk::new(ArrayElement {
 					ctx,
 					item: items[0].clone(),
-				})])))
+				})]))
 			} else {
 				Val::Arr(ArrValue::expr(ctx, items.iter().cloned()))
 			}
modifiedcrates/jrsonnet-evaluator/src/obj.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/obj.rs
+++ b/crates/jrsonnet-evaluator/src/obj.rs
@@ -17,7 +17,7 @@
 	operator::evaluate_add_op,
 	tb, throw,
 	val::ThunkValue,
-	MaybeUnbound, Result, ResultExt, State, Thunk, Unbound, Val,
+	MaybeUnbound, Result, State, Thunk, Unbound, Val,
 };
 
 #[cfg(not(feature = "exp-preserve-order"))]
modifiedcrates/jrsonnet-evaluator/src/typed/conversions.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/typed/conversions.rs
+++ b/crates/jrsonnet-evaluator/src/typed/conversions.rs
@@ -6,7 +6,7 @@
 use jrsonnet_types::{ComplexValType, ValType};
 
 use crate::{
-	arr::ArrValue,
+	arr::{ArrValue, BytesArray},
 	error::Result,
 	function::{native::NativeDesc, FuncDesc, FuncVal},
 	throw,
@@ -434,12 +434,13 @@
 	}
 
 	fn from_untyped(value: Val) -> Result<Self> {
-		if let Val::Arr(ArrValue::Bytes(bytes)) = value {
-			return Ok(bytes.0);
-		}
-		<Self as Typed>::TYPE.check(&value)?;
-		match value {
+		match &value {
 			Val::Arr(a) => {
+				if let Some(bytes) = a.as_any().downcast_ref::<BytesArray>() {
+					return Ok(bytes.0.as_slice().into());
+				};
+				<Self as Typed>::TYPE.check(&value)?;
+				// Any::downcast_ref::<ByteArray>(&a);
 				let mut out = Vec::with_capacity(a.len());
 				for e in a.iter() {
 					let r = e?;
@@ -447,7 +448,10 @@
 				}
 				Ok(out.as_slice().into())
 			}
-			_ => unreachable!(),
+			_ => {
+				<Self as Typed>::TYPE.check(&value)?;
+				unreachable!()
+			}
 		}
 	}
 }
modifiedcrates/jrsonnet-evaluator/src/val.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/val.rs
+++ b/crates/jrsonnet-evaluator/src/val.rs
@@ -9,7 +9,7 @@
 use jrsonnet_interner::IStr;
 use jrsonnet_types::ValType;
 
-pub use crate::arr::ArrValue;
+pub use crate::arr::{ArrValue, ArrayLike};
 use crate::{
 	error::{Error, ErrorKind::*},
 	function::FuncVal,
modifiedcrates/jrsonnet-stdlib/src/sets.rsdiffbeforeafterboth
--- a/crates/jrsonnet-stdlib/src/sets.rs
+++ b/crates/jrsonnet-stdlib/src/sets.rs
@@ -7,7 +7,6 @@
 	val::ArrValue,
 	Thunk, Val,
 };
-use jrsonnet_gcmodule::Cc;
 use jrsonnet_parser::BinaryOpType;
 
 #[builtin]
@@ -70,5 +69,5 @@
 			}
 		};
 	}
-	Ok(ArrValue::lazy(Cc::new(out)))
+	Ok(ArrValue::lazy(out))
 }
modifiedcrates/jrsonnet-stdlib/src/sort.rsdiffbeforeafterboth
before · crates/jrsonnet-stdlib/src/sort.rs
1#![allow(non_snake_case)]23use std::cmp::Ordering;45use jrsonnet_evaluator::{6	error::Result,7	function::{builtin, FuncVal},8	operator::evaluate_compare_op,9	throw,10	val::{equals, ArrValue},11	Thunk, Val,12};13use jrsonnet_gcmodule::Cc;14use jrsonnet_parser::BinaryOpType;1516use crate::eval_on_empty;1718#[derive(Copy, Clone)]19enum SortKeyType {20	Number,21	String,22	Unknown,23}2425#[derive(PartialEq)]26struct NonNaNf64(f64);27impl PartialOrd for NonNaNf64 {28	fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {29		Some(self.cmp(other))30	}31}32impl Eq for NonNaNf64 {}33impl Ord for NonNaNf64 {34	fn cmp(&self, other: &Self) -> std::cmp::Ordering {35		self.0.partial_cmp(&other.0).expect("non nan")36	}37}3839fn get_sort_type<T>(values: &[T], key_getter: impl Fn(&T) -> &Val) -> Result<SortKeyType> {40	let mut sort_type = SortKeyType::Unknown;41	for i in values.iter() {42		let i = key_getter(i);43		match (i, sort_type) {44			(Val::Str(_), SortKeyType::Unknown) => sort_type = SortKeyType::String,45			(Val::Num(_), SortKeyType::Unknown) => sort_type = SortKeyType::Number,46			(Val::Str(_), SortKeyType::String) | (Val::Num(_), SortKeyType::Number) => {}47			(Val::Str(_) | Val::Num(_), _) => {48				throw!("sort elements should have the same types")49			}50			_ => {}51		}52	}53	Ok(sort_type)54}5556fn sort_identity(mut values: Vec<Val>) -> Result<Vec<Val>> {57	// Fast path, identity key getter58	let sort_type = get_sort_type(&values, |k| k)?;59	match sort_type {60		SortKeyType::Number => values.sort_unstable_by_key(|v| match v {61			Val::Num(n) => NonNaNf64(*n),62			_ => unreachable!(),63		}),64		SortKeyType::String => values.sort_unstable_by_key(|v| match v {65			Val::Str(s) => s.clone(),66			_ => unreachable!(),67		}),68		SortKeyType::Unknown => {69			let mut err = None;70			// evaluate_compare_op will never return equal on types, which are different from71			// jsonnet perspective72			values.sort_unstable_by(|a, b| match evaluate_compare_op(a, b, BinaryOpType::Lt) {73				Ok(ord) => ord,74				Err(e) if err.is_none() => {75					let _ = err.insert(e);76					Ordering::Equal77				}78				Err(_) => Ordering::Equal,79			});80			if let Some(err) = err {81				return Err(err);82			}83		}84	};85	Ok(values)86}8788fn sort_keyf(values: ArrValue, keyf: FuncVal) -> Result<Vec<Thunk<Val>>> {89	// Slow path, user provided key getter90	let mut vk = Vec::with_capacity(values.len());91	for value in values.iter_lazy() {92		vk.push((93			value.clone(),94			keyf.evaluate_simple(&(value.clone(),), false)?,95		));96	}97	let sort_type = get_sort_type(&vk, |v| &v.1)?;98	match sort_type {99		SortKeyType::Number => vk.sort_by_key(|v| match v.1 {100			Val::Num(n) => NonNaNf64(n),101			_ => unreachable!(),102		}),103		SortKeyType::String => vk.sort_by_key(|v| match &v.1 {104			Val::Str(s) => s.clone(),105			_ => unreachable!(),106		}),107		SortKeyType::Unknown => {108			let mut err = None;109			// evaluate_compare_op will never return equal on types, which are different from110			// jsonnet perspective111			vk.sort_by(112				|(_a, ak), (_b, bk)| match evaluate_compare_op(ak, bk, BinaryOpType::Lt) {113					Ok(ord) => ord,114					Err(e) if err.is_none() => {115						let _ = err.insert(e);116						Ordering::Equal117					}118					Err(_) => Ordering::Equal,119				},120			);121			if let Some(err) = err {122				return Err(err);123			}124		}125	};126	Ok(vk.into_iter().map(|v| v.0).collect())127}128129/// * `key_getter` - None, if identity sort required130pub fn sort(values: ArrValue, key_getter: FuncVal) -> Result<ArrValue> {131	if values.len() <= 1 {132		return Ok(values);133	}134	if key_getter.is_identity() {135		Ok(ArrValue::eager(sort_identity(136			values.iter().collect::<Result<Vec<Val>>>()?,137		)?))138	} else {139		Ok(ArrValue::lazy(Cc::new(sort_keyf(values, key_getter)?)))140	}141}142143#[builtin]144pub fn builtin_sort(arr: ArrValue, keyF: Option<FuncVal>) -> Result<ArrValue> {145	super::sort::sort(arr, keyF.unwrap_or_else(FuncVal::identity))146}147148fn uniq_identity(arr: Vec<Val>) -> Result<Vec<Val>> {149	let mut out = Vec::new();150	let mut last = arr[0].clone();151	out.push(last.clone());152	for next in arr.into_iter().skip(1) {153		if !equals(&last, &next)? {154			out.push(next.clone());155		}156		last = next;157	}158	Ok(out)159}160161fn uniq_keyf(arr: ArrValue, keyf: FuncVal) -> Result<Vec<Thunk<Val>>> {162	let mut out = Vec::new();163	let last_value = arr.get_lazy(0).unwrap();164	let mut last_key = keyf.evaluate_simple(&(last_value.clone(),), false)?;165	out.push(last_value);166167	for next in arr.iter_lazy().skip(1) {168		let next_key = keyf.evaluate_simple(&(next.clone(),), false)?;169		if !equals(&last_key, &next_key)? {170			out.push(next.clone());171		}172		last_key = next_key;173	}174	Ok(out)175}176177#[builtin]178#[allow(non_snake_case)]179pub fn builtin_uniq(arr: ArrValue, keyF: Option<FuncVal>) -> Result<ArrValue> {180	if arr.len() <= 1 {181		return Ok(arr);182	}183	let keyF = keyF.unwrap_or(FuncVal::identity());184	if keyF.is_identity() {185		Ok(ArrValue::eager(uniq_identity(186			arr.iter().collect::<Result<Vec<Val>>>()?,187		)?))188	} else {189		Ok(ArrValue::lazy(Cc::new(uniq_keyf(arr, keyF)?)))190	}191}192193#[builtin]194#[allow(non_snake_case)]195pub fn builtin_set(arr: ArrValue, keyF: Option<FuncVal>) -> Result<ArrValue> {196	if arr.len() <= 1 {197		return Ok(arr);198	}199	let keyF = keyF.unwrap_or(FuncVal::identity());200	if keyF.is_identity() {201		let arr = arr.iter().collect::<Result<Vec<Val>>>()?;202		let arr = sort_identity(arr)?;203		let arr = uniq_identity(arr)?;204		Ok(ArrValue::eager(arr))205	} else {206		let arr = sort_keyf(arr, keyF.clone())?;207		let arr = uniq_keyf(ArrValue::lazy(Cc::new(arr)), keyF)?;208		Ok(ArrValue::lazy(Cc::new(arr)))209	}210}211212fn eval_keyf(val: Val, key_f: &Option<FuncVal>) -> Result<Val> {213	if let Some(key_f) = key_f {214		key_f.evaluate_simple(&(val,), false)215	} else {216		Ok(val)217	}218}219220fn array_top1(arr: ArrValue, key_f: Option<FuncVal>, ordering: Ordering) -> Result<Val> {221	let mut iter = arr.iter();222	let mut min = iter.next().expect("not empty")?;223	let mut min_key = eval_keyf(min.clone(), &key_f)?;224	for item in iter {225		let cur = item?;226		let cur_key = eval_keyf(cur.clone(), &key_f)?;227		if evaluate_compare_op(&cur_key, &min_key, BinaryOpType::Lt)? == ordering {228			min = cur;229			min_key = cur_key;230		}231	}232	Ok(min)233}234235#[builtin]236pub fn builtin_min_array(237	arr: ArrValue,238	keyF: Option<FuncVal>,239	onEmpty: Option<Thunk<Val>>,240) -> Result<Val> {241	if arr.is_empty() {242		return eval_on_empty(onEmpty);243	}244	array_top1(arr, keyF, Ordering::Less)245}246#[builtin]247pub fn builtin_max_array(248	arr: ArrValue,249	keyF: Option<FuncVal>,250	onEmpty: Option<Thunk<Val>>,251) -> Result<Val> {252	if arr.is_empty() {253		return eval_on_empty(onEmpty);254	}255	array_top1(arr, keyF, Ordering::Greater)256}
after · crates/jrsonnet-stdlib/src/sort.rs
1#![allow(non_snake_case)]23use std::cmp::Ordering;45use jrsonnet_evaluator::{6	error::Result,7	function::{builtin, FuncVal},8	operator::evaluate_compare_op,9	throw,10	val::{equals, ArrValue},11	Thunk, Val,12};13use jrsonnet_parser::BinaryOpType;1415use crate::eval_on_empty;1617#[derive(Copy, Clone)]18enum SortKeyType {19	Number,20	String,21	Unknown,22}2324#[derive(PartialEq)]25struct NonNaNf64(f64);26impl PartialOrd for NonNaNf64 {27	fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {28		Some(self.cmp(other))29	}30}31impl Eq for NonNaNf64 {}32impl Ord for NonNaNf64 {33	fn cmp(&self, other: &Self) -> std::cmp::Ordering {34		self.0.partial_cmp(&other.0).expect("non nan")35	}36}3738fn get_sort_type<T>(values: &[T], key_getter: impl Fn(&T) -> &Val) -> Result<SortKeyType> {39	let mut sort_type = SortKeyType::Unknown;40	for i in values.iter() {41		let i = key_getter(i);42		match (i, sort_type) {43			(Val::Str(_), SortKeyType::Unknown) => sort_type = SortKeyType::String,44			(Val::Num(_), SortKeyType::Unknown) => sort_type = SortKeyType::Number,45			(Val::Str(_), SortKeyType::String) | (Val::Num(_), SortKeyType::Number) => {}46			(Val::Str(_) | Val::Num(_), _) => {47				throw!("sort elements should have the same types")48			}49			_ => {}50		}51	}52	Ok(sort_type)53}5455fn sort_identity(mut values: Vec<Val>) -> Result<Vec<Val>> {56	// Fast path, identity key getter57	let sort_type = get_sort_type(&values, |k| k)?;58	match sort_type {59		SortKeyType::Number => values.sort_unstable_by_key(|v| match v {60			Val::Num(n) => NonNaNf64(*n),61			_ => unreachable!(),62		}),63		SortKeyType::String => values.sort_unstable_by_key(|v| match v {64			Val::Str(s) => s.clone(),65			_ => unreachable!(),66		}),67		SortKeyType::Unknown => {68			let mut err = None;69			// evaluate_compare_op will never return equal on types, which are different from70			// jsonnet perspective71			values.sort_unstable_by(|a, b| match evaluate_compare_op(a, b, BinaryOpType::Lt) {72				Ok(ord) => ord,73				Err(e) if err.is_none() => {74					let _ = err.insert(e);75					Ordering::Equal76				}77				Err(_) => Ordering::Equal,78			});79			if let Some(err) = err {80				return Err(err);81			}82		}83	};84	Ok(values)85}8687fn sort_keyf(values: ArrValue, keyf: FuncVal) -> Result<Vec<Thunk<Val>>> {88	// Slow path, user provided key getter89	let mut vk = Vec::with_capacity(values.len());90	for value in values.iter_lazy() {91		vk.push((92			value.clone(),93			keyf.evaluate_simple(&(value.clone(),), false)?,94		));95	}96	let sort_type = get_sort_type(&vk, |v| &v.1)?;97	match sort_type {98		SortKeyType::Number => vk.sort_by_key(|v| match v.1 {99			Val::Num(n) => NonNaNf64(n),100			_ => unreachable!(),101		}),102		SortKeyType::String => vk.sort_by_key(|v| match &v.1 {103			Val::Str(s) => s.clone(),104			_ => unreachable!(),105		}),106		SortKeyType::Unknown => {107			let mut err = None;108			// evaluate_compare_op will never return equal on types, which are different from109			// jsonnet perspective110			vk.sort_by(111				|(_a, ak), (_b, bk)| match evaluate_compare_op(ak, bk, BinaryOpType::Lt) {112					Ok(ord) => ord,113					Err(e) if err.is_none() => {114						let _ = err.insert(e);115						Ordering::Equal116					}117					Err(_) => Ordering::Equal,118				},119			);120			if let Some(err) = err {121				return Err(err);122			}123		}124	};125	Ok(vk.into_iter().map(|v| v.0).collect())126}127128/// * `key_getter` - None, if identity sort required129pub fn sort(values: ArrValue, key_getter: FuncVal) -> Result<ArrValue> {130	if values.len() <= 1 {131		return Ok(values);132	}133	if key_getter.is_identity() {134		Ok(ArrValue::eager(sort_identity(135			values.iter().collect::<Result<Vec<Val>>>()?,136		)?))137	} else {138		Ok(ArrValue::lazy(sort_keyf(values, key_getter)?))139	}140}141142#[builtin]143pub fn builtin_sort(arr: ArrValue, keyF: Option<FuncVal>) -> Result<ArrValue> {144	super::sort::sort(arr, keyF.unwrap_or_else(FuncVal::identity))145}146147fn uniq_identity(arr: Vec<Val>) -> Result<Vec<Val>> {148	let mut out = Vec::new();149	let mut last = arr[0].clone();150	out.push(last.clone());151	for next in arr.into_iter().skip(1) {152		if !equals(&last, &next)? {153			out.push(next.clone());154		}155		last = next;156	}157	Ok(out)158}159160fn uniq_keyf(arr: ArrValue, keyf: FuncVal) -> Result<Vec<Thunk<Val>>> {161	let mut out = Vec::new();162	let last_value = arr.get_lazy(0).unwrap();163	let mut last_key = keyf.evaluate_simple(&(last_value.clone(),), false)?;164	out.push(last_value);165166	for next in arr.iter_lazy().skip(1) {167		let next_key = keyf.evaluate_simple(&(next.clone(),), false)?;168		if !equals(&last_key, &next_key)? {169			out.push(next.clone());170		}171		last_key = next_key;172	}173	Ok(out)174}175176#[builtin]177#[allow(non_snake_case)]178pub fn builtin_uniq(arr: ArrValue, keyF: Option<FuncVal>) -> Result<ArrValue> {179	if arr.len() <= 1 {180		return Ok(arr);181	}182	let keyF = keyF.unwrap_or(FuncVal::identity());183	if keyF.is_identity() {184		Ok(ArrValue::eager(uniq_identity(185			arr.iter().collect::<Result<Vec<Val>>>()?,186		)?))187	} else {188		Ok(ArrValue::lazy(uniq_keyf(arr, keyF)?))189	}190}191192#[builtin]193#[allow(non_snake_case)]194pub fn builtin_set(arr: ArrValue, keyF: Option<FuncVal>) -> Result<ArrValue> {195	if arr.len() <= 1 {196		return Ok(arr);197	}198	let keyF = keyF.unwrap_or(FuncVal::identity());199	if keyF.is_identity() {200		let arr = arr.iter().collect::<Result<Vec<Val>>>()?;201		let arr = sort_identity(arr)?;202		let arr = uniq_identity(arr)?;203		Ok(ArrValue::eager(arr))204	} else {205		let arr = sort_keyf(arr, keyF.clone())?;206		let arr = uniq_keyf(ArrValue::lazy(arr), keyF)?;207		Ok(ArrValue::lazy(arr))208	}209}210211fn eval_keyf(val: Val, key_f: &Option<FuncVal>) -> Result<Val> {212	if let Some(key_f) = key_f {213		key_f.evaluate_simple(&(val,), false)214	} else {215		Ok(val)216	}217}218219fn array_top1(arr: ArrValue, key_f: Option<FuncVal>, ordering: Ordering) -> Result<Val> {220	let mut iter = arr.iter();221	let mut min = iter.next().expect("not empty")?;222	let mut min_key = eval_keyf(min.clone(), &key_f)?;223	for item in iter {224		let cur = item?;225		let cur_key = eval_keyf(cur.clone(), &key_f)?;226		if evaluate_compare_op(&cur_key, &min_key, BinaryOpType::Lt)? == ordering {227			min = cur;228			min_key = cur_key;229		}230	}231	Ok(min)232}233234#[builtin]235pub fn builtin_min_array(236	arr: ArrValue,237	keyF: Option<FuncVal>,238	onEmpty: Option<Thunk<Val>>,239) -> Result<Val> {240	if arr.is_empty() {241		return eval_on_empty(onEmpty);242	}243	array_top1(arr, keyF, Ordering::Less)244}245#[builtin]246pub fn builtin_max_array(247	arr: ArrValue,248	keyF: Option<FuncVal>,249	onEmpty: Option<Thunk<Val>>,250) -> Result<Val> {251	if arr.is_empty() {252		return eval_on_empty(onEmpty);253	}254	array_top1(arr, keyF, Ordering::Greater)255}