difftreelog
perf lazy slice
in: master
5 files changed
crates/jrsonnet-evaluator/src/builtin/mod.rsdiffbeforeafterboth434344pub fn std_slice(44pub fn std_slice(45 indexable: IndexableVal,45 indexable: IndexableVal,46 index: Option<usize>,46 index: Option<BoundedUsize<0, { i32::MAX as usize }>>,47 end: Option<usize>,47 end: Option<BoundedUsize<0, { i32::MAX as usize }>>,48 step: Option<usize>,48 step: Option<BoundedUsize<1, { i32::MAX as usize }>>,49) -> Result<Val> {49) -> Result<Val> {50 let index = index.unwrap_or(0);50 match &indexable {51 let end = end.unwrap_or_else(|| match &indexable {51 IndexableVal::Str(s) => {52 IndexableVal::Str(_) => usize::MAX,53 IndexableVal::Arr(v) => v.len(),54 });52 let index = index.as_deref().copied().unwrap_or(0);53 let end = end.as_deref().copied().unwrap_or(usize::MAX);55 let step = step.unwrap_or(1);54 let step = step.as_deref().copied().unwrap_or(1);56 match &indexable {5556 if index >= end {57 return Ok(Val::Str("".into()));58 }5957 IndexableVal::Str(s) => Ok(Val::Str(60 Ok(Val::Str(58 (s.chars()61 (s.chars()59 .skip(index)62 .skip(index)60 .take(end - index)63 .take(end - index)61 .step_by(step)64 .step_by(step)62 .collect::<String>())65 .collect::<String>())63 .into(),66 .into(),64 )),67 ))68 }65 IndexableVal::Arr(arr) => Ok(Val::Arr(69 IndexableVal::Arr(arr) => {70 let index = index.as_deref().copied().unwrap_or(0);71 let end = end.as_deref().copied().unwrap_or(usize::MAX).min(arr.len());72 let step = step.as_deref().copied().unwrap_or(1);7374 if index >= end {75 return Ok(Val::Arr(ArrValue::new_eager()));76 }7766 (arr.iter()78 Ok(Val::Arr(ArrValue::Slice(Box::new(Slice {67 .skip(index)79 inner: arr.clone(),68 .take(end - index)80 from: index as u32,69 .step_by(step)81 to: end as u32,82 step: step as u32,70 .collect::<Result<Vec<Val>>>()?)83 }))))71 .into(),84 }72 )),85 }73 }74}86}758776type BuiltinsType = HashMap<IStr, &'static dyn StaticBuiltin>;88type BuiltinsType = HashMap<IStr, &'static dyn StaticBuiltin>;221#[jrsonnet_macros::builtin]233#[jrsonnet_macros::builtin]222fn builtin_slice(234fn builtin_slice(223 indexable: IndexableVal,235 indexable: IndexableVal,224 index: Option<usize>,236 index: Option<BoundedUsize<0, { i32::MAX as usize }>>,225 end: Option<usize>,237 end: Option<BoundedUsize<0, { i32::MAX as usize }>>,226 step: Option<usize>,238 step: Option<BoundedUsize<1, { i32::MAX as usize }>>,227) -> Result<Any> {239) -> Result<Any> {228 std_slice(indexable, index, end, step).map(Any)240 std_slice(indexable, index, end, step).map(Any)229}241}crates/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())
+ };
+}
crates/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)?
}
crates/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);
}
crates/jrsonnet-evaluator/src/val.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/val.rs
+++ b/crates/jrsonnet-evaluator/src/val.rs
@@ -172,6 +172,37 @@
}
#[derive(Debug, Clone, Trace)]
+pub struct Slice {
+ pub(crate) inner: ArrValue,
+ pub(crate) from: u32,
+ pub(crate) to: u32,
+ pub(crate) step: u32,
+}
+impl Slice {
+ fn from(&self) -> usize {
+ self.from as usize
+ }
+ fn to(&self) -> usize {
+ self.to as usize
+ }
+ fn step(&self) -> usize {
+ self.step as usize
+ }
+ fn len(&self) -> usize {
+ // TODO: use div_ceil
+ let diff = self.to() - self.from();
+ let rem = diff % self.step();
+ let div = diff / self.step();
+
+ if rem != 0 {
+ div + 1
+ } else {
+ div
+ }
+ }
+}
+
+#[derive(Debug, Clone, Trace)]
#[force_tracking]
pub enum ArrValue {
Bytes(#[skip_trace] Rc<[u8]>),
@@ -179,6 +210,7 @@
Eager(Cc<Vec<Val>>),
Extended(Box<(Self, Self)>),
Range(i32, i32),
+ Slice(Box<Slice>),
Reversed(Box<Self>),
}
impl ArrValue {
@@ -190,6 +222,22 @@
Self::Range(a, b)
}
+ pub fn slice(self, from: Option<usize>, to: Option<usize>, step: Option<usize>) -> Self {
+ let len = self.len();
+ let from = from.unwrap_or(0);
+ let to = to.unwrap_or(len).min(len);
+ let step = step.unwrap_or(1);
+ assert!(from < to);
+ assert!(step > 0);
+
+ Self::Slice(Box::new(Slice {
+ inner: self,
+ from: from as u32,
+ to: to as u32,
+ step: step as u32,
+ }))
+ }
+
pub fn len(&self) -> usize {
match self {
Self::Bytes(i) => i.len(),
@@ -198,6 +246,7 @@
Self::Extended(v) => v.0.len() + v.1.len(),
Self::Range(a, b) => a.abs_diff(*b) as usize,
Self::Reversed(i) => i.len(),
+ Self::Slice(s) => s.len(),
}
}
@@ -239,6 +288,13 @@
}
v.get(len - index - 1)
}
+ Self::Slice(s) => {
+ let index = s.from() + index * s.step();
+ if index >= s.to() {
+ return Ok(None);
+ }
+ s.inner.get(index as usize)
+ }
}
}
@@ -272,6 +328,13 @@
}
v.get_lazy(len - index - 1)
}
+ Self::Slice(s) => {
+ let index = s.from() + index * s.step();
+ if index >= s.to() {
+ return None;
+ }
+ s.inner.get_lazy(index as usize)
+ }
}
}
@@ -311,33 +374,43 @@
Cc::update_with(&mut r, |v| v.reverse());
r
}
+ Self::Slice(v) => {
+ let mut out = Vec::with_capacity(v.inner.len());
+ for v in v
+ .inner
+ .iter_lazy()
+ .skip(v.from())
+ .take(v.to() - v.from())
+ .step_by(v.step())
+ {
+ out.push(v.evaluate()?)
+ }
+ Cc::new(out)
+ }
})
}
pub fn iter(&self) -> impl DoubleEndedIterator<Item = Result<Val>> + '_ {
- // if let Self::Reversed(v) = self {
- // return v.iter().rev();
- // }
- let len = self.len();
- (0..len).map(move |idx| match self {
+ (0..self.len()).map(move |idx| match self {
Self::Bytes(b) => Ok(Val::Num(b[idx] as f64)),
Self::Lazy(l) => l[idx].evaluate(),
Self::Eager(e) => Ok(e[idx].clone()),
Self::Extended(_) => self.get(idx).map(|e| e.unwrap()),
Self::Range(..) => self.get(idx).map(|e| e.unwrap()),
- Self::Reversed(..) => self.get(len - idx - 1).map(|e| e.unwrap()),
+ Self::Reversed(..) => self.get(idx).map(|e| e.unwrap()),
+ Self::Slice(..) => self.get(idx).map(|e| e.unwrap()),
})
}
pub fn iter_lazy(&self) -> impl DoubleEndedIterator<Item = LazyVal> + '_ {
- let len = self.len();
- (0..len).map(move |idx| match self {
+ (0..self.len()).map(move |idx| match self {
Self::Bytes(b) => LazyVal::new_resolved(Val::Num(b[idx] as f64)),
Self::Lazy(l) => l[idx].clone(),
Self::Eager(e) => LazyVal::new_resolved(e[idx].clone()),
Self::Extended(_) => self.get_lazy(idx).unwrap(),
Self::Range(..) => self.get_lazy(idx).unwrap(),
- Self::Reversed(..) => self.get_lazy(len - idx - 1).unwrap(),
+ Self::Reversed(..) => self.get_lazy(idx).unwrap(),
+ Self::Slice(..) => self.get_lazy(idx).unwrap(),
})
}
@@ -459,17 +532,6 @@
}
}
- pub fn try_cast_nullable_num(self, context: &'static str) -> Result<Option<f64>> {
- Ok(match self {
- Val::Null => None,
- Val::Num(num) => Some(num),
- _ => throw!(TypeMismatch(
- context,
- vec![ValType::Null, ValType::Num],
- self.value_type()
- )),
- })
- }
pub const fn value_type(&self) -> ValType {
match self {
Self::Str(..) => ValType::Str,