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.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.rsdiffbeforeafterboth171 String,171 String,172}172}173174#[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 usize184 }185 fn to(&self) -> usize {186 self.to as usize187 }188 fn step(&self) -> usize {189 self.step as usize190 }191 fn len(&self) -> usize {192 // TODO: use div_ceil193 let diff = self.to() - self.from();194 let rem = diff % self.step();195 let div = diff / self.step();196197 if rem != 0 {198 div + 1199 } else {200 div201 }202 }203}173204174#[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 }224225 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);232233 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 }192240193 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 }203252239 }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 }244300272 }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 }277340311 Cc::update_with(&mut r, |v| v.reverse());374 Cc::update_with(&mut r, |v| v.reverse());312 r375 r313 }376 }377 Self::Slice(v) => {378 let mut out = Vec::with_capacity(v.inner.len());379 for v in v380 .inner381 .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 }316392317 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 }331404332 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 }343416459 }532 }460 }533 }461534462 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,