git.delta.rocks / jrsonnet / refs/commits / 4c008687f967

difftreelog

perf lazy slice

Yaroslav Bolyukin2022-04-20parent: #9c0fa01.patch.diff
in: master

5 files changed

modifiedcrates/jrsonnet-evaluator/src/builtin/mod.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/builtin/mod.rs
+++ b/crates/jrsonnet-evaluator/src/builtin/mod.rs
@@ -43,33 +43,45 @@
 
 pub fn std_slice(
 	indexable: IndexableVal,
-	index: Option<usize>,
-	end: Option<usize>,
-	step: Option<usize>,
+	index: Option<BoundedUsize<0, { i32::MAX as usize }>>,
+	end: Option<BoundedUsize<0, { i32::MAX as usize }>>,
+	step: Option<BoundedUsize<1, { i32::MAX as usize }>>,
 ) -> Result<Val> {
-	let index = index.unwrap_or(0);
-	let end = end.unwrap_or_else(|| match &indexable {
-		IndexableVal::Str(_) => usize::MAX,
-		IndexableVal::Arr(v) => v.len(),
-	});
-	let step = step.unwrap_or(1);
 	match &indexable {
-		IndexableVal::Str(s) => Ok(Val::Str(
+		IndexableVal::Str(s) => {
+			let index = index.as_deref().copied().unwrap_or(0);
+			let end = end.as_deref().copied().unwrap_or(usize::MAX);
+			let step = step.as_deref().copied().unwrap_or(1);
+
+			if index >= end {
+				return Ok(Val::Str("".into()));
+			}
+
+			Ok(Val::Str(
 			(s.chars()
 				.skip(index)
 				.take(end - index)
 				.step_by(step)
 				.collect::<String>())
 			.into(),
-		)),
-		IndexableVal::Arr(arr) => Ok(Val::Arr(
-			(arr.iter()
-				.skip(index)
-				.take(end - index)
-				.step_by(step)
-				.collect::<Result<Vec<Val>>>()?)
-			.into(),
-		)),
+			))
+		}
+		IndexableVal::Arr(arr) => {
+			let index = index.as_deref().copied().unwrap_or(0);
+			let end = end.as_deref().copied().unwrap_or(usize::MAX).min(arr.len());
+			let step = step.as_deref().copied().unwrap_or(1);
+
+			if index >= end {
+				return Ok(Val::Arr(ArrValue::new_eager()));
+			}
+
+			Ok(Val::Arr(ArrValue::Slice(Box::new(Slice {
+				inner: arr.clone(),
+				from: index as u32,
+				to: end as u32,
+				step: step as u32,
+			}))))
+		}
 	}
 }
 
@@ -221,9 +233,9 @@
 #[jrsonnet_macros::builtin]
 fn builtin_slice(
 	indexable: IndexableVal,
-	index: Option<usize>,
-	end: Option<usize>,
-	step: Option<usize>,
+	index: Option<BoundedUsize<0, { i32::MAX as usize }>>,
+	end: Option<BoundedUsize<0, { i32::MAX as usize }>>,
+	step: Option<BoundedUsize<1, { i32::MAX as usize }>>,
 ) -> Result<Any> {
 	std_slice(indexable, index, end, step).map(Any)
 }
modifiedcrates/jrsonnet-evaluator/src/error.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/error.rs
+++ b/crates/jrsonnet-evaluator/src/error.rs
@@ -191,3 +191,10 @@
 		return Err($e.into())
 	};
 }
+
+#[macro_export]
+macro_rules! throw_runtime {
+	($($tt:tt)*) => {
+		return Err($crate::error::Error::RuntimeError(format!($($tt)*).into()).into())
+	};
+}
modifiedcrates/jrsonnet-evaluator/src/evaluate/mod.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/evaluate/mod.rs
+++ b/crates/jrsonnet-evaluator/src/evaluate/mod.rs
@@ -665,23 +665,28 @@
 		}
 		Slice(value, desc) => {
 			let indexable = evaluate(context.clone(), value)?;
+			let loc = CallLocation::new(loc);
 
-			fn parse_num(
+			fn parse_idx<const MIN: usize>(
+				loc: CallLocation,
 				context: &Context,
-				expr: Option<&LocExpr>,
+				expr: &Option<LocExpr>,
 				desc: &'static str,
-			) -> Result<Option<usize>> {
-				Ok(match expr {
-					Some(s) => evaluate(context.clone(), s)?
-						.try_cast_nullable_num(desc)?
-						.map(|v| v as usize),
-					None => None,
-				})
+			) -> Result<Option<BoundedUsize<MIN, { i32::MAX as usize }>>> {
+				if let Some(value) = expr {
+					Ok(Some(push_frame(
+						loc,
+						|| format!("slice {}", desc),
+						|| Ok(evaluate(context.clone(), value)?.try_into()?),
+					)?))
+				} else {
+					Ok(None)
+				}
 			}
 
-			let start = parse_num(&context, desc.start.as_ref(), "start")?;
-			let end = parse_num(&context, desc.end.as_ref(), "end")?;
-			let step = parse_num(&context, desc.step.as_ref(), "step")?;
+			let start = parse_idx(loc, &context, &desc.start, "start")?;
+			let end = parse_idx(loc, &context, &desc.end, "end")?;
+			let step = parse_idx(loc, &context, &desc.step, "step")?;
 
 			std_slice(indexable.into_indexable()?, start, end, step)?
 		}
modifiedcrates/jrsonnet-evaluator/src/typed/conversions.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/typed/conversions.rs
+++ b/crates/jrsonnet-evaluator/src/typed/conversions.rs
@@ -1,5 +1,6 @@
 use std::{
 	convert::{TryFrom, TryInto},
+	ops::Deref,
 	rc::Rc,
 };
 
@@ -12,7 +13,8 @@
 	error::{Error::*, LocError, Result},
 	throw,
 	typed::CheckType,
-	ArrValue, FuncDesc, FuncVal, IndexableVal, ObjValue, ObjValueBuilder, Val,
+	val::{ArrValue, FuncDesc, FuncVal, IndexableVal},
+	ObjValue, ObjValueBuilder, Val,
 };
 
 pub trait TypedObj: Typed {
@@ -69,6 +71,76 @@
 
 impl_int!(i8 u8 i16 u16 i32 u32);
 
+macro_rules! impl_bounded_int {
+	($($name:ident = $ty:ty)*) => {$(
+		#[derive(Clone, Copy)]
+		pub struct $name<const MIN: $ty, const MAX: $ty>($ty);
+		impl<const MIN: $ty, const MAX: $ty> $name<MIN, MAX> {
+			pub const fn new(value: $ty) -> Option<$name<MIN, MAX>> {
+				if value >= MIN && value <= MAX {
+					Some(Self(value))
+				} else {
+					None
+				}
+			}
+			pub const fn value(self) -> $ty {
+				self.0
+			}
+		}
+		impl<const MIN: $ty, const MAX: $ty> Deref for $name<MIN, MAX> {
+			type Target = $ty;
+			fn deref(&self) -> &Self::Target {
+				&self.0
+			}
+		}
+
+		impl<const MIN: $ty, const MAX: $ty> Typed for $name<MIN, MAX> {
+			const TYPE: &'static ComplexValType =
+				&ComplexValType::BoundedNumber(
+					Some(MIN as f64),
+					Some(MAX as f64),
+				);
+		}
+		impl<const MIN: $ty, const MAX: $ty> TryFrom<Val> for $name<MIN, MAX> {
+			type Error = LocError;
+
+			fn try_from(value: Val) -> Result<Self> {
+				<Self as Typed>::TYPE.check(&value)?;
+				match value {
+					Val::Num(n) => {
+						if n.trunc() != n {
+							throw!(RuntimeError(
+								format!(
+									"cannot convert number with fractional part to {}",
+									stringify!($ty)
+								)
+								.into()
+							))
+						}
+						Ok(Self(n as $ty))
+					}
+					_ => unreachable!(),
+				}
+			}
+		}
+		impl<const MIN: $ty, const MAX: $ty> TryFrom<$name<MIN, MAX>> for Val {
+			type Error = LocError;
+
+			fn try_from(value: $name<MIN, MAX>) -> Result<Self> {
+				Ok(Self::Num(value.0 as f64))
+			}
+		}
+	)*};
+}
+
+impl_bounded_int!(
+	BoundedI8 = i8
+	BoundedI16 = i16
+	BoundedI32 = i32
+	BoundedI64 = i64
+	BoundedUsize = usize
+);
+
 impl Typed for f64 {
 	const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Num);
 }
modifiedcrates/jrsonnet-evaluator/src/val.rsdiffbeforeafterboth
171 String,171 String,
172}172}
173
174#[derive(Debug, Clone, Trace)]
175pub struct Slice {
176 pub(crate) inner: ArrValue,
177 pub(crate) from: u32,
178 pub(crate) to: u32,
179 pub(crate) step: u32,
180}
181impl Slice {
182 fn from(&self) -> usize {
183 self.from as usize
184 }
185 fn to(&self) -> usize {
186 self.to as usize
187 }
188 fn step(&self) -> usize {
189 self.step as usize
190 }
191 fn len(&self) -> usize {
192 // TODO: use div_ceil
193 let diff = self.to() - self.from();
194 let rem = diff % self.step();
195 let div = diff / self.step();
196
197 if rem != 0 {
198 div + 1
199 } else {
200 div
201 }
202 }
203}
173204
174#[derive(Debug, Clone, Trace)]205#[derive(Debug, Clone, Trace)]
175#[force_tracking]206#[force_tracking]
179 Eager(Cc<Vec<Val>>),210 Eager(Cc<Vec<Val>>),
180 Extended(Box<(Self, Self)>),211 Extended(Box<(Self, Self)>),
181 Range(i32, i32),212 Range(i32, i32),
213 Slice(Box<Slice>),
182 Reversed(Box<Self>),214 Reversed(Box<Self>),
183}215}
184impl ArrValue {216impl ArrValue {
190 Self::Range(a, b)222 Self::Range(a, b)
191 }223 }
224
225 pub fn slice(self, from: Option<usize>, to: Option<usize>, step: Option<usize>) -> Self {
226 let len = self.len();
227 let from = from.unwrap_or(0);
228 let to = to.unwrap_or(len).min(len);
229 let step = step.unwrap_or(1);
230 assert!(from < to);
231 assert!(step > 0);
232
233 Self::Slice(Box::new(Slice {
234 inner: self,
235 from: from as u32,
236 to: to as u32,
237 step: step as u32,
238 }))
239 }
192240
193 pub fn len(&self) -> usize {241 pub fn len(&self) -> usize {
194 match self {242 match self {
198 Self::Extended(v) => v.0.len() + v.1.len(),246 Self::Extended(v) => v.0.len() + v.1.len(),
199 Self::Range(a, b) => a.abs_diff(*b) as usize,247 Self::Range(a, b) => a.abs_diff(*b) as usize,
200 Self::Reversed(i) => i.len(),248 Self::Reversed(i) => i.len(),
249 Self::Slice(s) => s.len(),
201 }250 }
202 }251 }
203252
239 }288 }
240 v.get(len - index - 1)289 v.get(len - index - 1)
241 }290 }
291 Self::Slice(s) => {
292 let index = s.from() + index * s.step();
293 if index >= s.to() {
294 return Ok(None);
295 }
296 s.inner.get(index as usize)
297 }
242 }298 }
243 }299 }
244300
272 }328 }
273 v.get_lazy(len - index - 1)329 v.get_lazy(len - index - 1)
274 }330 }
331 Self::Slice(s) => {
332 let index = s.from() + index * s.step();
333 if index >= s.to() {
334 return None;
335 }
336 s.inner.get_lazy(index as usize)
337 }
275 }338 }
276 }339 }
277340
311 Cc::update_with(&mut r, |v| v.reverse());374 Cc::update_with(&mut r, |v| v.reverse());
312 r375 r
313 }376 }
377 Self::Slice(v) => {
378 let mut out = Vec::with_capacity(v.inner.len());
379 for v in v
380 .inner
381 .iter_lazy()
382 .skip(v.from())
383 .take(v.to() - v.from())
384 .step_by(v.step())
385 {
386 out.push(v.evaluate()?)
387 }
388 Cc::new(out)
389 }
314 })390 })
315 }391 }
316392
317 pub fn iter(&self) -> impl DoubleEndedIterator<Item = Result<Val>> + '_ {393 pub fn iter(&self) -> impl DoubleEndedIterator<Item = Result<Val>> + '_ {
318 // if let Self::Reversed(v) = self {
319 // return v.iter().rev();
320 // }
321 let len = self.len();394 (0..self.len()).map(move |idx| match self {
322 (0..len).map(move |idx| match self {
323 Self::Bytes(b) => Ok(Val::Num(b[idx] as f64)),395 Self::Bytes(b) => Ok(Val::Num(b[idx] as f64)),
324 Self::Lazy(l) => l[idx].evaluate(),396 Self::Lazy(l) => l[idx].evaluate(),
325 Self::Eager(e) => Ok(e[idx].clone()),397 Self::Eager(e) => Ok(e[idx].clone()),
326 Self::Extended(_) => self.get(idx).map(|e| e.unwrap()),398 Self::Extended(_) => self.get(idx).map(|e| e.unwrap()),
327 Self::Range(..) => self.get(idx).map(|e| e.unwrap()),399 Self::Range(..) => self.get(idx).map(|e| e.unwrap()),
328 Self::Reversed(..) => self.get(len - idx - 1).map(|e| e.unwrap()),400 Self::Reversed(..) => self.get(idx).map(|e| e.unwrap()),
401 Self::Slice(..) => self.get(idx).map(|e| e.unwrap()),
329 })402 })
330 }403 }
331404
332 pub fn iter_lazy(&self) -> impl DoubleEndedIterator<Item = LazyVal> + '_ {405 pub fn iter_lazy(&self) -> impl DoubleEndedIterator<Item = LazyVal> + '_ {
333 let len = self.len();406 (0..self.len()).map(move |idx| match self {
334 (0..len).map(move |idx| match self {
335 Self::Bytes(b) => LazyVal::new_resolved(Val::Num(b[idx] as f64)),407 Self::Bytes(b) => LazyVal::new_resolved(Val::Num(b[idx] as f64)),
336 Self::Lazy(l) => l[idx].clone(),408 Self::Lazy(l) => l[idx].clone(),
337 Self::Eager(e) => LazyVal::new_resolved(e[idx].clone()),409 Self::Eager(e) => LazyVal::new_resolved(e[idx].clone()),
338 Self::Extended(_) => self.get_lazy(idx).unwrap(),410 Self::Extended(_) => self.get_lazy(idx).unwrap(),
339 Self::Range(..) => self.get_lazy(idx).unwrap(),411 Self::Range(..) => self.get_lazy(idx).unwrap(),
340 Self::Reversed(..) => self.get_lazy(len - idx - 1).unwrap(),412 Self::Reversed(..) => self.get_lazy(idx).unwrap(),
413 Self::Slice(..) => self.get_lazy(idx).unwrap(),
341 })414 })
342 }415 }
343416
459 }532 }
460 }533 }
461534
462 pub fn try_cast_nullable_num(self, context: &'static str) -> Result<Option<f64>> {
463 Ok(match self {
464 Val::Null => None,
465 Val::Num(num) => Some(num),
466 _ => throw!(TypeMismatch(
467 context,
468 vec![ValType::Null, ValType::Num],
469 self.value_type()
470 )),
471 })
472 }
473 pub const fn value_type(&self) -> ValType {535 pub const fn value_type(&self) -> ValType {
474 match self {536 match self {
475 Self::Str(..) => ValType::Str,537 Self::Str(..) => ValType::Str,