git.delta.rocks / jrsonnet / refs/commits / e70291dbc65e

difftreelog

source

crates/jrsonnet-evaluator/src/arr/spec.rs26.5 KiBsourcehistory
1//! Those implementations are a bit sketchy, as this is mostly performance experiments2//! of not yet finished nightly rust features34use std::{cell::RefCell, iter, mem::replace};56use jrsonnet_gcmodule::{Cc, Trace};7use jrsonnet_interner::IBytes;8use jrsonnet_parser::LocExpr;910use super::ArrValue;11use crate::{12	error::ErrorKind::InfiniteRecursionDetected, evaluate, function::FuncVal, val::ThunkValue,13	Context, Error, Result, Thunk, Val,14};1516pub trait ArrayLike: Sized + Into<ArrValue> {17	#[cfg(feature = "nightly")]18	type Iter<'t>19	where20		Self: 't;21	#[cfg(feature = "nightly")]22	type IterLazy<'t>23	where24		Self: 't;25	#[cfg(feature = "nightly")]26	type IterCheap<'t>27	where28		Self: 't;2930	fn len(&self) -> usize;31	fn is_empty(&self) -> bool {32		self.len() == 033	}34	fn get(&self, index: usize) -> Result<Option<Val>>;35	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>>;36	fn get_cheap(&self, index: usize) -> Option<Val>;37	#[cfg(feature = "nightly")]38	#[allow(clippy::iter_not_returning_iterator)]39	fn iter(&self) -> Self::Iter<'_>;40	#[cfg(feature = "nightly")]41	fn iter_lazy(&self) -> Self::IterLazy<'_>;42	#[cfg(feature = "nightly")]43	fn iter_cheap(&self) -> Option<Self::IterCheap<'_>>;4445	fn reverse(self) -> ArrValue {46		ArrValue::Reverse(Cc::new(ReverseArray(self.into())))47	}48}4950#[derive(Debug, Clone, Trace)]51pub struct SliceArray {52	pub(crate) inner: ArrValue,53	pub(crate) from: u32,54	pub(crate) to: u32,55	pub(crate) step: u32,56}5758impl SliceArray {59	#[cfg(not(feature = "nightly"))]60	fn iter(&self) -> impl Iterator<Item = Result<Val>> + '_ {61		self.inner62			.iter()63			.skip(self.from as usize)64			.take((self.to - self.from) as usize)65			.step_by(self.step as usize)66	}6768	#[cfg(not(feature = "nightly"))]69	fn iter_lazy(&self) -> impl Iterator<Item = Thunk<Val>> + '_ {70		self.inner71			.iter_lazy()72			.skip(self.from as usize)73			.take((self.to - self.from) as usize)74			.step_by(self.step as usize)75	}7677	#[cfg(not(feature = "nightly"))]78	fn iter_cheap(&self) -> Option<impl crate::arr::ArrayLikeIter<Val> + '_> {79		Some(80			self.inner81				.iter_cheap()?82				.skip(self.from as usize)83				.take((self.to - self.from) as usize)84				.step_by(self.step as usize),85		)86	}87}88#[cfg(feature = "nightly")]89type SliceArrayIter<'t> = impl DoubleEndedIterator<Item = Result<Val>> + ExactSizeIterator + 't;90#[cfg(feature = "nightly")]91type SliceArrayLazyIter<'t> = impl DoubleEndedIterator<Item = Thunk<Val>> + ExactSizeIterator + 't;92#[cfg(feature = "nightly")]93type SliceArrayCheapIter<'t> = impl DoubleEndedIterator<Item = Val> + ExactSizeIterator + 't;94impl ArrayLike for SliceArray {95	#[cfg(feature = "nightly")]96	type Iter<'t> = SliceArrayIter<'t>;97	#[cfg(feature = "nightly")]98	type IterLazy<'t> = SliceArrayLazyIter<'t>;99	#[cfg(feature = "nightly")]100	type IterCheap<'t> = SliceArrayCheapIter<'t>;101102	fn len(&self) -> usize {103		iter::repeat(())104			.take((self.to - self.from) as usize)105			.step_by(self.step as usize)106			.count()107	}108109	fn get(&self, index: usize) -> Result<Option<Val>> {110		self.iter().nth(index).transpose()111	}112113	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {114		self.iter_lazy().nth(index)115	}116117	fn get_cheap(&self, index: usize) -> Option<Val> {118		self.iter_cheap()?.nth(index)119	}120121	#[cfg(feature = "nightly")]122	fn iter(&self) -> SliceArrayIter<'_> {123		self.inner124			.iter()125			.skip(self.from as usize)126			.take((self.to - self.from) as usize)127			.step_by(self.step as usize)128	}129130	#[cfg(feature = "nightly")]131	fn iter_lazy(&self) -> SliceArrayLazyIter<'_> {132		self.inner133			.iter_lazy()134			.skip(self.from as usize)135			.take((self.to - self.from) as usize)136			.step_by(self.step as usize)137	}138139	#[cfg(feature = "nightly")]140	fn iter_cheap(&self) -> Option<SliceArrayCheapIter<'_>> {141		Some(142			self.inner143				.iter_cheap()?144				.skip(self.from as usize)145				.take((self.to - self.from) as usize)146				.step_by(self.step as usize),147		)148	}149}150impl From<SliceArray> for ArrValue {151	fn from(value: SliceArray) -> Self {152		Self::Slice(Cc::new(value))153	}154}155156#[derive(Trace, Debug, Clone)]157pub struct BytesArray(pub IBytes);158#[cfg(feature = "nightly")]159type BytesArrayIter<'t> = impl DoubleEndedIterator<Item = Result<Val>> + ExactSizeIterator + 't;160#[cfg(feature = "nightly")]161type BytesArrayLazyIter<'t> = impl DoubleEndedIterator<Item = Thunk<Val>> + ExactSizeIterator + 't;162#[cfg(feature = "nightly")]163type BytesArrayCheapIter<'t> = impl DoubleEndedIterator<Item = Val> + ExactSizeIterator + 't;164impl ArrayLike for BytesArray {165	#[cfg(feature = "nightly")]166	type Iter<'t> = BytesArrayIter<'t>;167	#[cfg(feature = "nightly")]168	type IterLazy<'t> = BytesArrayLazyIter<'t>;169	#[cfg(feature = "nightly")]170	type IterCheap<'t> = BytesArrayCheapIter<'t>;171172	fn len(&self) -> usize {173		self.0.len()174	}175176	fn get(&self, index: usize) -> Result<Option<Val>> {177		Ok(self.get_cheap(index))178	}179180	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {181		self.get_cheap(index).map(Thunk::evaluated)182	}183184	fn get_cheap(&self, index: usize) -> Option<Val> {185		self.0.get(index).map(|v| Val::Num(f64::from(*v)))186	}187188	#[cfg(feature = "nightly")]189	fn iter(&self) -> BytesArrayIter<'_> {190		self.0.iter().map(|v| Ok(Val::Num(f64::from(*v))))191	}192193	#[cfg(feature = "nightly")]194	fn iter_lazy(&self) -> BytesArrayLazyIter<'_> {195		self.0196			.iter()197			.map(|v| Thunk::evaluated(Val::Num(f64::from(*v))))198	}199200	#[cfg(feature = "nightly")]201	fn iter_cheap(&self) -> Option<BytesArrayCheapIter<'_>> {202		Some(self.0.iter().map(|v| Val::Num(f64::from(*v))))203	}204}205impl From<BytesArray> for ArrValue {206	fn from(value: BytesArray) -> Self {207		ArrValue::Bytes(value)208	}209}210211#[derive(Debug, Trace, Clone)]212enum ArrayThunk<T: 'static + Trace> {213	Computed(Val),214	Errored(Error),215	Waiting(T),216	Pending,217}218219#[derive(Debug, Trace)]220pub struct ExprArrayInner {221	ctx: Context,222	cached: RefCell<Vec<ArrayThunk<LocExpr>>>,223}224#[derive(Debug, Trace, Clone)]225pub struct ExprArray(pub Cc<ExprArrayInner>);226impl ExprArray {227	pub fn new(ctx: Context, items: impl IntoIterator<Item = LocExpr>) -> Self {228		Self(Cc::new(ExprArrayInner {229			ctx,230			cached: RefCell::new(items.into_iter().map(ArrayThunk::Waiting).collect()),231		}))232	}233}234#[cfg(feature = "nightly")]235type ExprArrayIter<'t> = impl DoubleEndedIterator<Item = Result<Val>> + ExactSizeIterator + 't;236#[cfg(feature = "nightly")]237type ExprArrayLazyIter<'t> = impl DoubleEndedIterator<Item = Thunk<Val>> + ExactSizeIterator + 't;238#[cfg(feature = "nightly")]239type ExprArrayCheapIter<'t> = iter::Empty<Val>;240impl ArrayLike for ExprArray {241	#[cfg(feature = "nightly")]242	type Iter<'t> = ExprArrayIter<'t>;243	#[cfg(feature = "nightly")]244	type IterLazy<'t> = ExprArrayLazyIter<'t>;245	#[cfg(feature = "nightly")]246	type IterCheap<'t> = ExprArrayCheapIter<'t>;247248	fn len(&self) -> usize {249		self.0.cached.borrow().len()250	}251	fn get(&self, index: usize) -> Result<Option<Val>> {252		if index >= self.len() {253			return Ok(None);254		}255		match &self.0.cached.borrow()[index] {256			ArrayThunk::Computed(c) => return Ok(Some(c.clone())),257			ArrayThunk::Errored(e) => return Err(e.clone()),258			ArrayThunk::Pending => return Err(InfiniteRecursionDetected.into()),259			ArrayThunk::Waiting(..) => {}260		};261262		let ArrayThunk::Waiting(expr) = replace(&mut self.0.cached.borrow_mut()[index], ArrayThunk::Pending) else {263			unreachable!()264		};265266		let new_value = match evaluate(self.0.ctx.clone(), &expr) {267			Ok(v) => v,268			Err(e) => {269				self.0.cached.borrow_mut()[index] = ArrayThunk::Errored(e.clone());270				return Err(e);271			}272		};273		self.0.cached.borrow_mut()[index] = ArrayThunk::Computed(new_value.clone());274		Ok(Some(new_value))275	}276	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {277		#[derive(Trace)]278		struct ArrayElement {279			arr_thunk: ExprArray,280			index: usize,281		}282283		impl ThunkValue for ArrayElement {284			type Output = Val;285286			fn get(self: Box<Self>) -> Result<Self::Output> {287				self.arr_thunk288					.get(self.index)289					.transpose()290					.expect("index checked")291			}292		}293294		if index >= self.len() {295			return None;296		}297		match &self.0.cached.borrow()[index] {298			ArrayThunk::Computed(c) => return Some(Thunk::evaluated(c.clone())),299			ArrayThunk::Errored(e) => return Some(Thunk::errored(e.clone())),300			ArrayThunk::Waiting(_) | ArrayThunk::Pending => {}301		};302303		Some(Thunk::new(ArrayElement {304			arr_thunk: self.clone(),305			index,306		}))307	}308	fn get_cheap(&self, _index: usize) -> Option<Val> {309		None310	}311312	#[cfg(feature = "nightly")]313	fn iter(&self) -> ExprArrayIter<'_> {314		(0..self.len()).map(|i| self.get(i).transpose().expect("index checked"))315	}316	#[cfg(feature = "nightly")]317	fn iter_lazy(&self) -> ExprArrayLazyIter<'_> {318		(0..self.len()).map(|i| self.get_lazy(i).expect("index checked"))319	}320	#[cfg(feature = "nightly")]321	fn iter_cheap(&self) -> Option<Self::IterCheap<'_>> {322		None323	}324}325impl From<ExprArray> for ArrValue {326	fn from(value: ExprArray) -> Self {327		Self::Expr(value)328	}329}330331#[derive(Trace, Debug, Clone)]332pub struct ExtendedArray {333	pub a: ArrValue,334	pub b: ArrValue,335	split: usize,336	len: usize,337}338#[cfg(feature = "nightly")]339340type ExtendedArrayIter<'t> = impl DoubleEndedIterator<Item = Result<Val>> + ExactSizeIterator + 't;341#[cfg(feature = "nightly")]342type ExtendedArrayLazyIter<'t> =343	impl DoubleEndedIterator<Item = Thunk<Val>> + ExactSizeIterator + 't;344#[cfg(feature = "nightly")]345type ExtendedArrayCheapIter<'t> = impl DoubleEndedIterator<Item = Val> + ExactSizeIterator + 't;346impl ExtendedArray {347	pub fn new(a: ArrValue, b: ArrValue) -> Self {348		let a_len = a.len();349		let b_len = b.len();350		Self {351			a,352			b,353			split: a_len,354			len: a_len.checked_add(b_len).expect("too large array value"),355		}356	}357}358359struct WithExactSize<I>(I, usize);360impl<I, T> Iterator for WithExactSize<I>361where362	I: Iterator<Item = T>,363{364	type Item = T;365366	fn next(&mut self) -> Option<Self::Item> {367		self.0.next()368	}369	fn nth(&mut self, n: usize) -> Option<Self::Item> {370		self.0.nth(n)371	}372	fn size_hint(&self) -> (usize, Option<usize>) {373		(self.1, Some(self.1))374	}375}376impl<I> DoubleEndedIterator for WithExactSize<I>377where378	I: DoubleEndedIterator,379{380	fn next_back(&mut self) -> Option<Self::Item> {381		self.0.next_back()382	}383	fn nth_back(&mut self, n: usize) -> Option<Self::Item> {384		self.0.nth_back(n)385	}386}387impl<I> ExactSizeIterator for WithExactSize<I>388where389	I: Iterator,390{391	fn len(&self) -> usize {392		self.1393	}394}395impl ArrayLike for ExtendedArray {396	#[cfg(feature = "nightly")]397	type Iter<'t> = ExtendedArrayIter<'t>;398	#[cfg(feature = "nightly")]399	type IterLazy<'t> = ExtendedArrayLazyIter<'t>;400	#[cfg(feature = "nightly")]401	type IterCheap<'t> = ExtendedArrayCheapIter<'t>;402403	fn get(&self, index: usize) -> Result<Option<Val>> {404		if self.split > index {405			self.a.get(index)406		} else {407			self.b.get(index - self.split)408		}409	}410	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {411		if self.split > index {412			self.a.get_lazy(index)413		} else {414			self.b.get_lazy(index - self.split)415		}416	}417418	fn len(&self) -> usize {419		self.len420	}421422	fn get_cheap(&self, index: usize) -> Option<Val> {423		if self.split > index {424			self.a.get_cheap(index)425		} else {426			self.b.get_cheap(index - self.split)427		}428	}429430	#[cfg(feature = "nightly")]431	fn iter(&self) -> ExtendedArrayIter<'_> {432		WithExactSize(self.a.iter().chain(self.b.iter()), self.len)433	}434	#[cfg(feature = "nightly")]435	fn iter_lazy(&self) -> ExtendedArrayLazyIter<'_> {436		WithExactSize(self.a.iter_lazy().chain(self.b.iter_lazy()), self.len)437	}438	#[cfg(feature = "nightly")]439	fn iter_cheap(&self) -> Option<ExtendedArrayCheapIter<'_>> {440		let a = self.a.iter_cheap()?;441		let b = self.b.iter_cheap()?;442		Some(WithExactSize(a.chain(b), self.len))443	}444}445impl From<ExtendedArray> for ArrValue {446	fn from(value: ExtendedArray) -> Self {447		Self::Extended(Cc::new(value))448	}449}450451#[derive(Trace, Debug, Clone)]452pub struct LazyArray(pub Cc<Vec<Thunk<Val>>>);453#[cfg(feature = "nightly")]454type LazyArrayIter<'t> = impl DoubleEndedIterator<Item = Result<Val>> + ExactSizeIterator + 't;455#[cfg(feature = "nightly")]456type LazyArrayLazyIter<'t> = impl DoubleEndedIterator<Item = Thunk<Val>> + ExactSizeIterator + 't;457#[cfg(feature = "nightly")]458type LazyArrayCheapIter<'t> = iter::Empty<Val>;459impl ArrayLike for LazyArray {460	#[cfg(feature = "nightly")]461	type Iter<'t> = LazyArrayIter<'t>;462463	#[cfg(feature = "nightly")]464	type IterLazy<'t> = LazyArrayLazyIter<'t>;465466	#[cfg(feature = "nightly")]467	type IterCheap<'t> = LazyArrayCheapIter<'t>;468469	fn len(&self) -> usize {470		self.0.len()471	}472	fn get(&self, index: usize) -> Result<Option<Val>> {473		let Some(v) = self.0.get(index) else {474			return Ok(None);475		};476		v.evaluate().map(Some)477	}478	fn get_cheap(&self, _index: usize) -> Option<Val> {479		None480	}481	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {482		self.0.get(index).cloned()483	}484	#[cfg(feature = "nightly")]485	fn iter(&self) -> LazyArrayIter<'_> {486		self.0.iter().map(Thunk::evaluate)487	}488	#[cfg(feature = "nightly")]489	fn iter_lazy(&self) -> LazyArrayLazyIter<'_> {490		self.0.iter().cloned()491	}492	#[cfg(feature = "nightly")]493	fn iter_cheap(&self) -> Option<LazyArrayCheapIter<'_>> {494		None495	}496}497impl From<LazyArray> for ArrValue {498	fn from(value: LazyArray) -> Self {499		Self::Lazy(value)500	}501}502503#[derive(Trace, Debug, Clone)]504pub struct EagerArray(pub Cc<Vec<Val>>);505#[cfg(feature = "nightly")]506type EagerArrayIter<'t> = impl DoubleEndedIterator<Item = Result<Val>> + ExactSizeIterator + 't;507#[cfg(feature = "nightly")]508type EagerArrayLazyIter<'t> = impl DoubleEndedIterator<Item = Thunk<Val>> + ExactSizeIterator + 't;509#[cfg(feature = "nightly")]510type EagerArrayCheapIter<'t> = impl DoubleEndedIterator<Item = Val> + ExactSizeIterator + 't;511impl ArrayLike for EagerArray {512	#[cfg(feature = "nightly")]513	type Iter<'t> = EagerArrayIter<'t>;514515	#[cfg(feature = "nightly")]516	type IterLazy<'t> = EagerArrayLazyIter<'t>;517518	#[cfg(feature = "nightly")]519	type IterCheap<'t> = EagerArrayCheapIter<'t>;520521	fn len(&self) -> usize {522		self.0.len()523	}524525	fn get(&self, index: usize) -> Result<Option<Val>> {526		Ok(self.0.get(index).cloned())527	}528529	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {530		self.0.get(index).cloned().map(Thunk::evaluated)531	}532533	fn get_cheap(&self, index: usize) -> Option<Val> {534		self.0.get(index).cloned()535	}536537	#[cfg(feature = "nightly")]538	fn iter(&self) -> EagerArrayIter<'_> {539		self.0.iter().cloned().map(Ok)540	}541542	#[cfg(feature = "nightly")]543	fn iter_lazy(&self) -> EagerArrayLazyIter<'_> {544		self.0.iter().cloned().map(Thunk::evaluated)545	}546547	#[cfg(feature = "nightly")]548	fn iter_cheap(&self) -> Option<EagerArrayCheapIter<'_>> {549		Some(self.0.iter().cloned())550	}551}552impl From<EagerArray> for ArrValue {553	fn from(value: EagerArray) -> Self {554		Self::Eager(value)555	}556}557558/// Inclusive range type559#[derive(Debug, Trace, Clone, PartialEq, Eq)]560pub struct RangeArray {561	start: i32,562	end: i32,563}564impl RangeArray {565	pub fn empty() -> Self {566		Self::new_exclusive(0, 0)567	}568	pub fn new_exclusive(start: i32, end: i32) -> Self {569		end.checked_sub(1)570			.map_or_else(Self::empty, |end| Self { start, end })571	}572	pub fn new_inclusive(start: i32, end: i32) -> Self {573		Self { start, end }574	}575	fn range(&self) -> impl Iterator<Item = i32> + ExactSizeIterator + DoubleEndedIterator {576		WithExactSize(577			self.start..=self.end,578			(self.end as usize)579				.wrapping_sub(self.start as usize)580				.wrapping_add(1),581		)582	}583}584585#[cfg(feature = "nightly")]586type RangeArrayIter<'t> = impl DoubleEndedIterator<Item = Result<Val>> + ExactSizeIterator + 't;587#[cfg(feature = "nightly")]588type RangeArrayLazyIter<'t> = impl DoubleEndedIterator<Item = Thunk<Val>> + ExactSizeIterator + 't;589#[cfg(feature = "nightly")]590type RangeArrayCheapIter<'t> = impl DoubleEndedIterator<Item = Val> + ExactSizeIterator + 't;591impl ArrayLike for RangeArray {592	#[cfg(feature = "nightly")]593	type Iter<'t> = RangeArrayIter<'t>;594595	#[cfg(feature = "nightly")]596	type IterLazy<'t> = RangeArrayLazyIter<'t>;597598	#[cfg(feature = "nightly")]599	type IterCheap<'t> = RangeArrayCheapIter<'t>;600601	fn len(&self) -> usize {602		self.range().len()603	}604	fn is_empty(&self) -> bool {605		self.range().len() == 0606	}607608	fn get(&self, index: usize) -> Result<Option<Val>> {609		Ok(self.get_cheap(index))610	}611612	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {613		self.get_cheap(index).map(Thunk::evaluated)614	}615616	fn get_cheap(&self, index: usize) -> Option<Val> {617		self.range().nth(index).map(|i| Val::Num(f64::from(i)))618	}619620	#[cfg(feature = "nightly")]621	fn iter(&self) -> RangeArrayIter<'_> {622		self.range().map(|i| Ok(Val::Num(f64::from(i))))623	}624625	#[cfg(feature = "nightly")]626	fn iter_lazy(&self) -> RangeArrayLazyIter<'_> {627		self.range()628			.map(|i| Thunk::evaluated(Val::Num(f64::from(i))))629	}630631	#[cfg(feature = "nightly")]632	fn iter_cheap(&self) -> Option<RangeArrayCheapIter<'_>> {633		Some(self.range().map(|i| Val::Num(f64::from(i))))634	}635}636impl From<RangeArray> for ArrValue {637	fn from(value: RangeArray) -> Self {638		Self::Range(value)639	}640}641642#[derive(Debug, Trace, Clone)]643pub struct ReverseArray(pub ArrValue);644impl ArrayLike for ReverseArray {645	#[cfg(feature = "nightly")]646	type Iter<'t> = iter::Rev<UnknownArrayIter<'t>>;647648	#[cfg(feature = "nightly")]649	type IterLazy<'t> = iter::Rev<UnknownArrayIterLazy<'t>>;650651	#[cfg(feature = "nightly")]652	type IterCheap<'t> = iter::Rev<UnknownArrayIterCheap<'t>>;653654	fn len(&self) -> usize {655		self.0.len()656	}657658	fn get(&self, index: usize) -> Result<Option<Val>> {659		self.0.get(self.0.len() - index - 1)660	}661662	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {663		self.0.get_lazy(self.0.len() - index - 1)664	}665666	fn get_cheap(&self, index: usize) -> Option<Val> {667		self.0.get_cheap(self.0.len() - index - 1)668	}669670	#[cfg(feature = "nightly")]671	fn iter(&self) -> iter::Rev<UnknownArrayIter<'_>> {672		self.0.iter().rev()673	}674675	#[cfg(feature = "nightly")]676	fn iter_lazy(&self) -> iter::Rev<UnknownArrayIterLazy<'_>> {677		self.0.iter_lazy().rev()678	}679680	#[cfg(feature = "nightly")]681	fn iter_cheap(&self) -> Option<iter::Rev<UnknownArrayIterCheap<'_>>> {682		Some(self.0.iter_cheap()?.rev())683	}684	fn reverse(self) -> ArrValue {685		self.0686	}687}688impl From<ReverseArray> for ArrValue {689	fn from(value: ReverseArray) -> Self {690		Self::Reverse(Cc::new(value))691	}692}693694#[derive(Trace, Debug)]695pub struct MappedArrayInner {696	inner: ArrValue,697	cached: RefCell<Vec<ArrayThunk<()>>>,698	mapper: FuncVal,699}700#[derive(Trace, Debug, Clone)]701pub struct MappedArray(Cc<MappedArrayInner>);702impl MappedArray {703	pub fn new(inner: ArrValue, mapper: FuncVal) -> Self {704		let len = inner.len();705		Self(Cc::new(MappedArrayInner {706			inner,707			cached: RefCell::new(vec![ArrayThunk::Waiting(()); len]),708			mapper,709		}))710	}711}712#[cfg(feature = "nightly")]713type MappedArrayIter<'t> = impl DoubleEndedIterator<Item = Result<Val>> + ExactSizeIterator + 't;714#[cfg(feature = "nightly")]715type MappedArrayLazyIter<'t> = impl DoubleEndedIterator<Item = Thunk<Val>> + ExactSizeIterator + 't;716#[cfg(feature = "nightly")]717type MappedArrayCheapIter<'t> = iter::Empty<Val>;718impl ArrayLike for MappedArray {719	#[cfg(feature = "nightly")]720	type Iter<'t> = MappedArrayIter<'t>;721	#[cfg(feature = "nightly")]722	type IterLazy<'t> = MappedArrayLazyIter<'t>;723	#[cfg(feature = "nightly")]724	type IterCheap<'t> = MappedArrayCheapIter<'t>;725726	fn len(&self) -> usize {727		self.0.cached.borrow().len()728	}729730	fn get(&self, index: usize) -> Result<Option<Val>> {731		if index >= self.len() {732			return Ok(None);733		}734		match &self.0.cached.borrow()[index] {735			ArrayThunk::Computed(c) => return Ok(Some(c.clone())),736			ArrayThunk::Errored(e) => return Err(e.clone()),737			ArrayThunk::Pending => return Err(InfiniteRecursionDetected.into()),738			ArrayThunk::Waiting(..) => {}739		};740741		let ArrayThunk::Waiting(_) = replace(&mut self.0.cached.borrow_mut()[index], ArrayThunk::Pending) else {742			unreachable!()743		};744745		let val = self746			.0747			.inner748			.get(index)749			.transpose()750			.expect("index checked")751			.and_then(|r| self.0.mapper.evaluate_simple(&(r,)));752753		let new_value = match val {754			Ok(v) => v,755			Err(e) => {756				self.0.cached.borrow_mut()[index] = ArrayThunk::Errored(e.clone());757				return Err(e);758			}759		};760		self.0.cached.borrow_mut()[index] = ArrayThunk::Computed(new_value.clone());761		Ok(Some(new_value))762	}763	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {764		#[derive(Trace)]765		struct ArrayElement {766			arr_thunk: MappedArray,767			index: usize,768		}769770		impl ThunkValue for ArrayElement {771			type Output = Val;772773			fn get(self: Box<Self>) -> Result<Self::Output> {774				self.arr_thunk775					.get(self.index)776					.transpose()777					.expect("index checked")778			}779		}780781		if index >= self.len() {782			return None;783		}784		match &self.0.cached.borrow()[index] {785			ArrayThunk::Computed(c) => return Some(Thunk::evaluated(c.clone())),786			ArrayThunk::Errored(e) => return Some(Thunk::errored(e.clone())),787			ArrayThunk::Waiting(_) | ArrayThunk::Pending => {}788		};789790		Some(Thunk::new(ArrayElement {791			arr_thunk: self.clone(),792			index,793		}))794	}795796	fn get_cheap(&self, _index: usize) -> Option<Val> {797		None798	}799800	#[cfg(feature = "nightly")]801	fn iter(&self) -> MappedArrayIter<'_> {802		(0..self.len()).map(|i| self.get(i).transpose().expect("length checked"))803	}804805	#[cfg(feature = "nightly")]806	fn iter_lazy(&self) -> MappedArrayLazyIter<'_> {807		(0..self.len()).map(|i| self.get_lazy(i).expect("length checked"))808	}809810	#[cfg(feature = "nightly")]811	fn iter_cheap(&self) -> Option<Self::IterCheap<'_>> {812		None813	}814}815impl From<MappedArray> for ArrValue {816	fn from(value: MappedArray) -> Self {817		Self::Mapped(value)818	}819}820821#[derive(Trace, Debug)]822pub struct RepeatedArrayInner {823	data: ArrValue,824	repeats: usize,825	total_len: usize,826}827#[derive(Trace, Debug, Clone)]828pub struct RepeatedArray(Cc<RepeatedArrayInner>);829impl RepeatedArray {830	pub fn new(data: ArrValue, repeats: usize) -> Option<Self> {831		let total_len = data.len().checked_mul(repeats)?;832		Some(Self(Cc::new(RepeatedArrayInner {833			data,834			repeats,835			total_len,836		})))837	}838	pub fn is_cheap(&self) -> bool {839		self.0.data.is_cheap()840	}841}842843#[cfg(feature = "nightly")]844type RepeatedArrayIter<'t> = impl DoubleEndedIterator<Item = Result<Val>> + ExactSizeIterator + 't;845#[cfg(feature = "nightly")]846type RepeatedArrayLazyIter<'t> =847	impl DoubleEndedIterator<Item = Thunk<Val>> + ExactSizeIterator + 't;848#[cfg(feature = "nightly")]849type RepeatedArrayCheapIter<'t> = impl DoubleEndedIterator<Item = Val> + ExactSizeIterator + 't;850impl ArrayLike for RepeatedArray {851	#[cfg(feature = "nightly")]852	type Iter<'t> = RepeatedArrayIter<'t>;853	#[cfg(feature = "nightly")]854	type IterLazy<'t> = RepeatedArrayLazyIter<'t>;855	#[cfg(feature = "nightly")]856	type IterCheap<'t> = RepeatedArrayCheapIter<'t>;857858	fn len(&self) -> usize {859		self.0.total_len860	}861862	fn get(&self, index: usize) -> Result<Option<Val>> {863		if index > self.0.total_len {864			return Ok(None);865		}866		self.0.data.get(index % self.0.data.len())867	}868869	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {870		if index > self.0.total_len {871			return None;872		}873		self.0.data.get_lazy(index % self.0.data.len())874	}875876	fn get_cheap(&self, index: usize) -> Option<Val> {877		if index > self.0.total_len {878			return None;879		}880		self.0.data.get_cheap(index % self.0.data.len())881	}882883	#[cfg(feature = "nightly")]884	fn iter(&self) -> RepeatedArrayIter<'_> {885		(0..self.0.total_len)886			.map(|i| self.get(i))887			.map(Result::transpose)888			.map(Option::unwrap)889	}890891	#[cfg(feature = "nightly")]892	fn iter_lazy(&self) -> RepeatedArrayLazyIter<'_> {893		(0..self.0.total_len)894			.map(|i| self.get_lazy(i))895			.map(Option::unwrap)896	}897898	#[cfg(feature = "nightly")]899	fn iter_cheap(&self) -> Option<RepeatedArrayCheapIter<'_>> {900		if !self.0.data.is_cheap() {901			return None;902		}903		Some(904			(0..self.0.total_len)905				.map(|i| self.get_cheap(i))906				.map(Option::unwrap),907		)908	}909}910impl From<RepeatedArray> for ArrValue {911	fn from(value: RepeatedArray) -> Self {912		Self::Repeated(value)913	}914}915916#[cfg(feature = "nightly")]917macro_rules! impl_iter_enum {918	($n:ident => $v:ident) => {919		pub enum $n<'t> {920			Bytes(<BytesArray as ArrayLike>::$v<'t>),921			Expr(<ExprArray as ArrayLike>::$v<'t>),922			Lazy(<LazyArray as ArrayLike>::$v<'t>),923			Eager(<EagerArray as ArrayLike>::$v<'t>),924			Range(<RangeArray as ArrayLike>::$v<'t>),925			Slice(Box<<SliceArray as ArrayLike>::$v<'t>>),926			Extended(Box<<ExtendedArray as ArrayLike>::$v<'t>>),927			Reverse(Box<<ReverseArray as ArrayLike>::$v<'t>>),928			Mapped(Box<<MappedArray as ArrayLike>::$v<'t>>),929			Repeated(Box<<RepeatedArray as ArrayLike>::$v<'t>>),930		}931	};932}933934macro_rules! pass {935	($t:ident.$m:ident($($ident:ident),*)) => {936		match $t {937			Self::Bytes(e) => e.$m($($ident)*),938			Self::Expr(e) => e.$m($($ident)*),939			Self::Lazy(e) => e.$m($($ident)*),940			Self::Eager(e) => e.$m($($ident)*),941			Self::Range(e) => e.$m($($ident)*),942			Self::Slice(e) => e.$m($($ident)*),943			Self::Extended(e) => e.$m($($ident)*),944			Self::Reverse(e) => e.$m($($ident)*),945			Self::Mapped(e) => e.$m($($ident)*),946			Self::Repeated(e) => e.$m($($ident)*),947		}948	};949}950pub(super) use pass;951952#[cfg(feature = "nightly")]953macro_rules! pass_iter_call {954	($t:ident.$c:ident $(in $wrap:ident)? => $e:ident) => {955		match $t {956			ArrValue::Bytes(e) => $e::Bytes($($wrap!)?(e.$c())),957			ArrValue::Lazy(e) => $e::Lazy($($wrap!)?(e.$c())),958			ArrValue::Expr(e) => $e::Expr($($wrap!)?(e.$c())),959			ArrValue::Eager(e) => $e::Eager($($wrap!)?(e.$c())),960			ArrValue::Range(e) => $e::Range($($wrap!)?(e.$c())),961			ArrValue::Slice(e) => $e::Slice(Box::new($($wrap!)?(e.$c()))),962			ArrValue::Extended(e) => $e::Extended(Box::new($($wrap!)?(e.$c()))),963			ArrValue::Reverse(e) => $e::Reverse(Box::new($($wrap!)?(e.$c()))),964			ArrValue::Mapped(e) => $e::Mapped(Box::new($($wrap!)?(e.$c()))),965			ArrValue::Repeated(e) => $e::Repeated(Box::new($($wrap!)?(e.$c()))),966		}967	};968}969#[cfg(feature = "nightly")]970pub(super) use pass_iter_call;971972#[cfg(feature = "nightly")]973macro_rules! impl_iter {974	($t:ident => $out:ty) => {975		impl Iterator for $t<'_> {976			type Item = $out;977978			fn next(&mut self) -> Option<Self::Item> {979				pass!(self.next())980			}981			fn nth(&mut self, count: usize) -> Option<Self::Item> {982				pass!(self.nth(count))983			}984			fn size_hint(&self) -> (usize, Option<usize>) {985				pass!(self.size_hint())986			}987		}988		impl DoubleEndedIterator for $t<'_> {989			fn next_back(&mut self) -> Option<Self::Item> {990				pass!(self.next_back())991			}992			fn nth_back(&mut self, count: usize) -> Option<Self::Item> {993				pass!(self.nth_back(count))994			}995		}996		impl ExactSizeIterator for $t<'_> {997			fn len(&self) -> usize {998				match self {999					Self::Bytes(e) => e.len(),1000					Self::Expr(e) => e.len(),1001					Self::Lazy(e) => e.len(),1002					Self::Eager(e) => e.len(),1003					Self::Range(e) => e.len(),1004					Self::Slice(e) => e.len(),1005					Self::Extended(e) => {1006						e.size_hint().1.expect("overflow is checked in constructor")1007					}1008					Self::Reverse(e) => e.len(),1009					Self::Mapped(e) => e.len(),1010					Self::Repeated(e) => e.len(),1011				}1012			}1013		}1014	};1015}10161017#[cfg(feature = "nightly")]1018impl_iter_enum!(UnknownArrayIter => Iter);1019#[cfg(feature = "nightly")]1020impl_iter!(UnknownArrayIter => Result<Val>);1021#[cfg(feature = "nightly")]1022impl_iter_enum!(UnknownArrayIterLazy => IterLazy);1023#[cfg(feature = "nightly")]1024impl_iter!(UnknownArrayIterLazy => Thunk<Val>);1025#[cfg(feature = "nightly")]1026impl_iter_enum!(UnknownArrayIterCheap => IterCheap);1027#[cfg(feature = "nightly")]1028impl_iter!(UnknownArrayIterCheap => Val);