difftreelog
perf lazy slice
in: master
5 files changed
crates/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)
}
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.rsdiffbeforeafterboth1use std::{1use std::{2 convert::{TryFrom, TryInto},2 convert::{TryFrom, TryInto},3 ops::Deref,3 rc::Rc,4 rc::Rc,4};5};5612 error::{Error::*, LocError, Result},13 error::{Error::*, LocError, Result},13 throw,14 throw,14 typed::CheckType,15 typed::CheckType,15 ArrValue, FuncDesc, FuncVal, IndexableVal, ObjValue, ObjValueBuilder, Val,16 val::{ArrValue, FuncDesc, FuncVal, IndexableVal},17 ObjValue, ObjValueBuilder, Val,16};18};1719697170impl_int!(i8 u8 i16 u16 i32 u32);72impl_int!(i8 u8 i16 u16 i32 u32);7374macro_rules! impl_bounded_int {75 ($($name:ident = $ty:ty)*) => {$(76 #[derive(Clone, Copy)]77 pub struct $name<const MIN: $ty, const MAX: $ty>($ty);78 impl<const MIN: $ty, const MAX: $ty> $name<MIN, MAX> {79 pub const fn new(value: $ty) -> Option<$name<MIN, MAX>> {80 if value >= MIN && value <= MAX {81 Some(Self(value))82 } else {83 None84 }85 }86 pub const fn value(self) -> $ty {87 self.088 }89 }90 impl<const MIN: $ty, const MAX: $ty> Deref for $name<MIN, MAX> {91 type Target = $ty;92 fn deref(&self) -> &Self::Target {93 &self.094 }95 }9697 impl<const MIN: $ty, const MAX: $ty> Typed for $name<MIN, MAX> {98 const TYPE: &'static ComplexValType =99 &ComplexValType::BoundedNumber(100 Some(MIN as f64),101 Some(MAX as f64),102 );103 }104 impl<const MIN: $ty, const MAX: $ty> TryFrom<Val> for $name<MIN, MAX> {105 type Error = LocError;106107 fn try_from(value: Val) -> Result<Self> {108 <Self as Typed>::TYPE.check(&value)?;109 match value {110 Val::Num(n) => {111 if n.trunc() != n {112 throw!(RuntimeError(113 format!(114 "cannot convert number with fractional part to {}",115 stringify!($ty)116 )117 .into()118 ))119 }120 Ok(Self(n as $ty))121 }122 _ => unreachable!(),123 }124 }125 }126 impl<const MIN: $ty, const MAX: $ty> TryFrom<$name<MIN, MAX>> for Val {127 type Error = LocError;128129 fn try_from(value: $name<MIN, MAX>) -> Result<Self> {130 Ok(Self::Num(value.0 as f64))131 }132 }133 )*};134}135136impl_bounded_int!(137 BoundedI8 = i8138 BoundedI16 = i16139 BoundedI32 = i32140 BoundedI64 = i64141 BoundedUsize = usize142);7114372impl Typed for f64 {144impl Typed for f64 {73 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Num);145 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,