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

difftreelog

source

crates/jrsonnet-evaluator/src/arr/mod.rs6.7 KiBsourcehistory
1use jrsonnet_gcmodule::{Cc, Trace};2use jrsonnet_interner::IBytes;3use jrsonnet_parser::LocExpr;45use crate::{function::FuncVal, Context, Result, Thunk, Val};67mod spec;8use spec::*;910/// Represents a Jsonnet array value.11#[derive(Debug, Clone, Trace)]12// may contrain other ArrValue13#[trace(tracking(force))]14pub enum ArrValue {15	/// Layout optimized byte array.16	Bytes(BytesArray),17	/// Every element is lazy evaluated.18	Lazy(LazyArray),19	/// Every element is defined somewhere in source code20	Expr(ExprArray),21	/// Every field is already evaluated.22	Eager(EagerArray),23	/// Concatenation of two arrays of any kind.24	Extended(Cc<ExtendedArray>),25	/// Represents a integer array in form `[start, start + 1, ... end - 1, end]`.26	/// This kind of arrays is generated by `std.range(start, end)` call, and used for loops.27	Range(RangeArray),28	/// Sliced array view.29	Slice(Cc<SliceArray>),30	/// Reversed array view.31	/// Returned by `std.reverse(other)` call32	Reverse(Cc<ReverseArray>),33	/// Returned by `std.map` call34	Mapped(MappedArray),35	/// Returned by `std.repeat` call36	Repeated(RepeatedArray),37}3839pub trait ArrayLikeIter<T>: Iterator<Item = T> + DoubleEndedIterator + ExactSizeIterator {}40impl<I, T> ArrayLikeIter<T> for I where41	I: Iterator<Item = T> + DoubleEndedIterator + ExactSizeIterator42{43}4445impl ArrValue {46	pub fn empty() -> Self {47		Self::Range(RangeArray::empty())48	}4950	pub fn expr(ctx: Context, exprs: impl IntoIterator<Item = LocExpr>) -> Self {51		Self::Expr(ExprArray::new(ctx, exprs))52	}5354	pub fn lazy(thunks: Cc<Vec<Thunk<Val>>>) -> Self {55		Self::Lazy(LazyArray(thunks))56	}5758	pub fn eager(values: Cc<Vec<Val>>) -> Self {59		Self::Eager(EagerArray(values))60	}6162	pub fn repeated(data: ArrValue, repeats: usize) -> Option<Self> {63		Some(Self::Repeated(RepeatedArray::new(data, repeats)?))64	}6566	pub fn bytes(bytes: IBytes) -> Self {67		Self::Bytes(BytesArray(bytes))68	}6970	#[must_use]71	pub fn map(self, mapper: FuncVal) -> Self {72		Self::Mapped(MappedArray::new(self, mapper))73	}7475	pub fn filter(self, filter: impl Fn(&Val) -> Result<bool>) -> Result<Self> {76		// TODO: ArrValue::Picked(inner, indexes) for large arrays77		let mut out = Vec::new();78		for i in self.iter() {79			let i = i?;80			if filter(&i)? {81				out.push(i);82			};83		}84		Ok(Self::eager(Cc::new(out)))85	}8687	pub fn extended(a: ArrValue, b: ArrValue) -> Self {88		// TODO: benchmark for an optimal value, currently just a arbitrary choice89		const ARR_EXTEND_THRESHOLD: usize = 100;9091		if a.is_empty() {92			b93		} else if b.is_empty() {94			a95		} else if a.len() + b.len() > ARR_EXTEND_THRESHOLD {96			Self::Extended(Cc::new(ExtendedArray::new(a, b)))97		} else if let (Some(a), Some(b)) = (a.iter_cheap(), b.iter_cheap()) {98			let mut out = Vec::with_capacity(a.len() + b.len());99			out.extend(a);100			out.extend(b);101			Self::eager(Cc::new(out))102		} else {103			let mut out = Vec::with_capacity(a.len() + b.len());104			out.extend(a.iter_lazy());105			out.extend(b.iter_lazy());106			Self::lazy(Cc::new(out))107		}108	}109110	pub fn range_exclusive(a: i32, b: i32) -> Self {111		Self::Range(RangeArray::new_exclusive(a, b))112	}113	pub fn range_inclusive(a: i32, b: i32) -> Self {114		Self::Range(RangeArray::new_inclusive(a, b))115	}116117	#[must_use]118	pub fn slice(119		self,120		from: Option<usize>,121		to: Option<usize>,122		step: Option<usize>,123	) -> Option<Self> {124		let len = self.len();125		let from = from.unwrap_or(0);126		let to = to.unwrap_or(len).min(len);127		let step = step.unwrap_or(1);128		if from >= to || step == 0 {129			return None;130		}131132		Some(Self::Slice(Cc::new(SliceArray {133			inner: self,134			from: from as u32,135			to: to as u32,136			step: step as u32,137		})))138	}139140	/// Array length.141	pub fn len(&self) -> usize {142		pass!(self.len())143	}144145	/// Is array contains no elements?146	pub fn is_empty(&self) -> bool {147		pass!(self.is_empty())148	}149150	/// Get array element by index, evaluating it, if it is lazy.151	///152	/// Returns `None` on out-of-bounds condition.153	pub fn get(&self, index: usize) -> Result<Option<Val>> {154		pass!(self.get(index))155	}156157	/// Returns None if get is either non cheap, or out of bounds158	fn get_cheap(&self, index: usize) -> Option<Val> {159		pass!(self.get_cheap(index))160	}161162	/// Get array element by index, without evaluation.163	///164	/// Returns `None` on out-of-bounds condition.165	pub fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {166		pass!(self.get_lazy(index))167	}168169	#[cfg(feature = "nightly")]170	pub fn iter(&self) -> UnknownArrayIter<'_> {171		pass_iter_call!(self.iter => UnknownArrayIter)172	}173	#[cfg(not(feature = "nightly"))]174	pub fn iter(&self) -> impl ArrayLikeIter<Result<Val>> + '_ {175		(0..self.len()).map(|i| self.get(i).transpose().expect("length checked"))176	}177178	/// Iterate over elements, returning lazy values.179	#[cfg(feature = "nightly")]180	pub fn iter_lazy(&self) -> UnknownArrayIterLazy<'_> {181		pass_iter_call!(self.iter_lazy => UnknownArrayIterLazy)182	}183	#[cfg(not(feature = "nightly"))]184	pub fn iter_lazy(&self) -> impl ArrayLikeIter<Thunk<Val>> + '_ {185		(0..self.len()).map(|i| self.get_lazy(i).expect("length checked"))186	}187188	#[cfg(feature = "nightly")]189	pub fn iter_cheap(&self) -> Option<UnknownArrayIterCheap<'_>> {190		macro_rules! question {191			($v:expr) => {192				$v?193			};194		}195		Some(pass_iter_call!(self.iter_cheap in question => UnknownArrayIterCheap))196	}197198	#[cfg(not(feature = "nightly"))]199	pub fn iter_cheap(&self) -> Option<impl ArrayLikeIter<Val> + '_> {200		if self.is_cheap() {201			Some((0..self.len()).map(|i| self.get_cheap(i).expect("length and is_cheap checked")))202		} else {203			None204		}205	}206207	/// Return a reversed view on current array.208	#[must_use]209	pub fn reversed(self) -> Self {210		Self::Reverse(Cc::new(ReverseArray(self)))211	}212213	pub fn ptr_eq(a: &Self, b: &Self) -> bool {214		match (a, b) {215			(ArrValue::Bytes(a), ArrValue::Bytes(b)) => a.0 == b.0,216			(ArrValue::Lazy(a), ArrValue::Lazy(b)) => Cc::ptr_eq(&a.0, &b.0),217			(ArrValue::Expr(a), ArrValue::Expr(b)) => Cc::ptr_eq(&a.0, &b.0),218			(ArrValue::Eager(a), ArrValue::Eager(b)) => Cc::ptr_eq(&a.0, &b.0),219			(ArrValue::Extended(a), ArrValue::Extended(b)) => Cc::ptr_eq(a, b),220			(ArrValue::Range(a), ArrValue::Range(b)) => a == b,221			_ => false,222		}223	}224225	pub fn is_cheap(&self) -> bool {226		match self {227			ArrValue::Eager(_) | ArrValue::Range(..) | ArrValue::Bytes(_) => true,228			ArrValue::Extended(v) => v.a.is_cheap() && v.b.is_cheap(),229			ArrValue::Slice(r) => r.inner.is_cheap(),230			ArrValue::Reverse(i) => i.0.is_cheap(),231			ArrValue::Repeated(v) => v.is_cheap(),232			ArrValue::Expr(_) | ArrValue::Lazy(_) | ArrValue::Mapped(_) => false,233		}234	}235}236impl From<Vec<Val>> for ArrValue {237	fn from(value: Vec<Val>) -> Self {238		Self::eager(Cc::new(value))239	}240}241impl From<Vec<Thunk<Val>>> for ArrValue {242	fn from(value: Vec<Thunk<Val>>) -> Self {243		Self::lazy(Cc::new(value))244	}245}246247#[cfg(target_pointer_width = "64")]248static_assertions::assert_eq_size!(ArrValue, [u8; 16]);