git.delta.rocks / jrsonnet / refs/commits / 1e2e6e2fa219

difftreelog

source

crates/jrsonnet-evaluator/src/arr/mod.rs7.4 KiBsourcehistory
1use std::rc::Rc;23use jrsonnet_gcmodule::{Cc, Trace};4use jrsonnet_interner::IBytes;5use jrsonnet_parser::LocExpr;67use crate::{function::FuncVal, Context, Result, Thunk, Val};89mod spec;10use spec::*;1112/// Represents a Jsonnet array value.13#[derive(Debug, Clone, Trace)]14// may contrain other ArrValue15#[trace(tracking(force))]16pub enum ArrValue {17	/// Layout optimized byte array.18	Bytes(BytesArray),19	/// Layout optimized char array.20	Chars(CharArray),21	/// Every element is lazy evaluated.22	Lazy(LazyArray),23	/// Every element is defined somewhere in source code24	Expr(ExprArray),25	/// Every field is already evaluated.26	Eager(EagerArray),27	/// Concatenation of two arrays of any kind.28	Extended(Cc<ExtendedArray>),29	/// Represents a integer array in form `[start, start + 1, ... end - 1, end]`.30	/// This kind of arrays is generated by `std.range(start, end)` call, and used for loops.31	Range(RangeArray),32	/// Sliced array view.33	Slice(Cc<SliceArray>),34	/// Reversed array view.35	/// Returned by `std.reverse(other)` call36	Reverse(Cc<ReverseArray>),37	/// Returned by `std.map` call38	Mapped(MappedArray),39	/// Returned by `std.repeat` call40	Repeated(RepeatedArray),41}4243pub trait ArrayLikeIter<T>: Iterator<Item = T> + DoubleEndedIterator + ExactSizeIterator {}44impl<I, T> ArrayLikeIter<T> for I where45	I: Iterator<Item = T> + DoubleEndedIterator + ExactSizeIterator46{47}4849impl ArrValue {50	pub fn empty() -> Self {51		Self::Range(RangeArray::empty())52	}5354	pub fn expr(ctx: Context, exprs: impl IntoIterator<Item = LocExpr>) -> Self {55		Self::Expr(ExprArray::new(ctx, exprs))56	}5758	pub fn lazy(thunks: Cc<Vec<Thunk<Val>>>) -> Self {59		Self::Lazy(LazyArray(thunks))60	}6162	pub fn eager(values: Vec<Val>) -> Self {63		Self::Eager(EagerArray(Cc::new(values)))64	}6566	pub fn repeated(data: ArrValue, repeats: usize) -> Option<Self> {67		Some(Self::Repeated(RepeatedArray::new(data, repeats)?))68	}6970	pub fn bytes(bytes: IBytes) -> Self {71		Self::Bytes(BytesArray(bytes))72	}73	pub fn chars(chars: impl Iterator<Item = char>) -> Self {74		Self::Chars(CharArray(Rc::new(chars.collect())))75	}7677	#[must_use]78	pub fn map(self, mapper: FuncVal) -> Self {79		Self::Mapped(MappedArray::new(self, mapper))80	}8182	pub fn filter(self, filter: impl Fn(&Val) -> Result<bool>) -> Result<Self> {83		// TODO: ArrValue::Picked(inner, indexes) for large arrays84		let mut out = Vec::new();85		for i in self.iter() {86			let i = i?;87			if filter(&i)? {88				out.push(i);89			};90		}91		Ok(Self::eager(out))92	}9394	pub fn extended(a: ArrValue, b: ArrValue) -> Self {95		// TODO: benchmark for an optimal value, currently just a arbitrary choice96		const ARR_EXTEND_THRESHOLD: usize = 100;9798		if a.is_empty() {99			b100		} else if b.is_empty() {101			a102		} else if a.len() + b.len() > ARR_EXTEND_THRESHOLD {103			Self::Extended(Cc::new(ExtendedArray::new(a, b)))104		} else if let (Some(a), Some(b)) = (a.iter_cheap(), b.iter_cheap()) {105			let mut out = Vec::with_capacity(a.len() + b.len());106			out.extend(a);107			out.extend(b);108			Self::eager(out)109		} else {110			let mut out = Vec::with_capacity(a.len() + b.len());111			out.extend(a.iter_lazy());112			out.extend(b.iter_lazy());113			Self::lazy(Cc::new(out))114		}115	}116117	pub fn range_exclusive(a: i32, b: i32) -> Self {118		Self::Range(RangeArray::new_exclusive(a, b))119	}120	pub fn range_inclusive(a: i32, b: i32) -> Self {121		Self::Range(RangeArray::new_inclusive(a, b))122	}123124	#[must_use]125	pub fn slice(126		self,127		from: Option<usize>,128		to: Option<usize>,129		step: Option<usize>,130	) -> Option<Self> {131		let len = self.len();132		let from = from.unwrap_or(0);133		let to = to.unwrap_or(len).min(len);134		let step = step.unwrap_or(1);135136		if from >= to || step == 0 {137			return None;138		}139		// match self {140		// 	ArrValue::Slice(slice) => {141		// 		return Some(Self::Slice(Cc::new(SliceArray {142		// 			inner: slice.inner.clone(),143		// 			from: slice.from + slice.step * (from as u32),144		// 			to: slice.from + (to as u32) * slice.step,145		// 			step: slice.step * step as u32,146		// 		})))147		// 	}148		// 	_ => {}149		// }150151		Some(Self::Slice(Cc::new(SliceArray {152			inner: self,153			from: from as u32,154			to: to as u32,155			step: step as u32,156		})))157	}158159	/// Array length.160	pub fn len(&self) -> usize {161		pass!(self.len())162	}163164	/// Is array contains no elements?165	pub fn is_empty(&self) -> bool {166		pass!(self.is_empty())167	}168169	/// Get array element by index, evaluating it, if it is lazy.170	///171	/// Returns `None` on out-of-bounds condition.172	pub fn get(&self, index: usize) -> Result<Option<Val>> {173		pass!(self.get(index))174	}175176	/// Returns None if get is either non cheap, or out of bounds177	fn get_cheap(&self, index: usize) -> Option<Val> {178		pass!(self.get_cheap(index))179	}180181	/// Get array element by index, without evaluation.182	///183	/// Returns `None` on out-of-bounds condition.184	pub fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {185		pass!(self.get_lazy(index))186	}187188	#[cfg(feature = "nightly")]189	pub fn iter(&self) -> UnknownArrayIter<'_> {190		pass_iter_call!(self.iter => UnknownArrayIter)191	}192	#[cfg(not(feature = "nightly"))]193	pub fn iter(&self) -> impl ArrayLikeIter<Result<Val>> + '_ {194		(0..self.len()).map(|i| self.get(i).transpose().expect("length checked"))195	}196197	/// Iterate over elements, returning lazy values.198	#[cfg(feature = "nightly")]199	pub fn iter_lazy(&self) -> UnknownArrayIterLazy<'_> {200		pass_iter_call!(self.iter_lazy => UnknownArrayIterLazy)201	}202	#[cfg(not(feature = "nightly"))]203	pub fn iter_lazy(&self) -> impl ArrayLikeIter<Thunk<Val>> + '_ {204		(0..self.len()).map(|i| self.get_lazy(i).expect("length checked"))205	}206207	#[cfg(feature = "nightly")]208	pub fn iter_cheap(&self) -> Option<UnknownArrayIterCheap<'_>> {209		macro_rules! question {210			($v:expr) => {211				$v?212			};213		}214		Some(pass_iter_call!(self.iter_cheap in question => UnknownArrayIterCheap))215	}216217	#[cfg(not(feature = "nightly"))]218	pub fn iter_cheap(&self) -> Option<impl ArrayLikeIter<Val> + '_> {219		if self.is_cheap() {220			Some((0..self.len()).map(|i| self.get_cheap(i).expect("length and is_cheap checked")))221		} else {222			None223		}224	}225226	/// Return a reversed view on current array.227	#[must_use]228	pub fn reversed(self) -> Self {229		Self::Reverse(Cc::new(ReverseArray(self)))230	}231232	pub fn ptr_eq(a: &Self, b: &Self) -> bool {233		match (a, b) {234			(ArrValue::Bytes(a), ArrValue::Bytes(b)) => a.0 == b.0,235			(ArrValue::Lazy(a), ArrValue::Lazy(b)) => Cc::ptr_eq(&a.0, &b.0),236			(ArrValue::Expr(a), ArrValue::Expr(b)) => Cc::ptr_eq(&a.0, &b.0),237			(ArrValue::Eager(a), ArrValue::Eager(b)) => Cc::ptr_eq(&a.0, &b.0),238			(ArrValue::Extended(a), ArrValue::Extended(b)) => Cc::ptr_eq(a, b),239			(ArrValue::Range(a), ArrValue::Range(b)) => a == b,240			_ => false,241		}242	}243244	/// Is this vec supports `.get_cheap()?`245	pub fn is_cheap(&self) -> bool {246		match self {247			ArrValue::Eager(_) | ArrValue::Range(..) | ArrValue::Bytes(_) | ArrValue::Chars(_) => {248				true249			}250			ArrValue::Extended(v) => v.a.is_cheap() && v.b.is_cheap(),251			ArrValue::Slice(r) => r.inner.is_cheap(),252			ArrValue::Reverse(i) => i.0.is_cheap(),253			ArrValue::Repeated(v) => v.is_cheap(),254			ArrValue::Expr(_) | ArrValue::Lazy(_) | ArrValue::Mapped(_) => false,255		}256	}257}258impl From<Vec<Val>> for ArrValue {259	fn from(value: Vec<Val>) -> Self {260		Self::eager(value)261	}262}263impl From<Vec<Thunk<Val>>> for ArrValue {264	fn from(value: Vec<Thunk<Val>>) -> Self {265		Self::lazy(Cc::new(value))266	}267}268impl FromIterator<Val> for ArrValue {269	fn from_iter<T: IntoIterator<Item = Val>>(iter: T) -> Self {270		Self::eager(iter.into_iter().collect())271	}272}273274#[cfg(target_pointer_width = "64")]275static_assertions::assert_eq_size!(ArrValue, [u8; 16]);