From 89a650875ae4fe4947272a6ee6b9dca451eba49d Mon Sep 17 00:00:00 2001 From: Yaroslav Bolyukin Date: Sun, 13 Aug 2023 19:34:08 +0200 Subject: [PATCH] perf: move more stdlib functions to native --- crates/jrsonnet-stdlib/src/arrays.rs | 28 +++++++++++ crates/jrsonnet-stdlib/src/lib.rs | 25 ++++++---- crates/jrsonnet-stdlib/src/manifest/mod.rs | 5 ++ crates/jrsonnet-stdlib/src/objects.rs | 43 +++++++++++++++-- crates/jrsonnet-stdlib/src/sets.rs | 55 ++++++++++++++++++++++ crates/jrsonnet-stdlib/src/std.jsonnet | 48 ------------------- crates/jrsonnet-stdlib/src/strings.rs | 5 ++ 7 files changed, 146 insertions(+), 63 deletions(-) diff --git a/crates/jrsonnet-stdlib/src/arrays.rs b/crates/jrsonnet-stdlib/src/arrays.rs index 0610cd68..11fd7c72 100644 --- a/crates/jrsonnet-stdlib/src/arrays.rs +++ b/crates/jrsonnet-stdlib/src/arrays.rs @@ -102,6 +102,15 @@ pub fn builtin_filter(func: FuncVal, arr: ArrValue) -> Result { arr.filter(|val| bool::from_untyped(func.evaluate_simple(&(val.clone(),), false)?)) } +#[builtin] +pub fn builtin_filter_map( + filter_func: FuncVal, + map_func: FuncVal, + arr: ArrValue, +) -> Result { + Ok(builtin_filter(filter_func, arr)?.map(map_func)) +} + #[builtin] pub fn builtin_foldl(func: FuncVal, arr: ArrValue, init: Val) -> Result { let mut acc = init; @@ -274,3 +283,22 @@ pub fn builtin_remove(arr: ArrValue, elem: Val) -> Result { } Ok(arr) } + +#[builtin] +pub fn builtin_flatten_arrays(arrs: Vec) -> ArrValue { + pub fn flatten_inner(values: &[ArrValue]) -> ArrValue { + if values.len() == 1 { + return values[0].clone(); + } else if values.len() == 2 { + return ArrValue::extended(values[0].clone(), values[1].clone()); + } + let (a, b) = values.split_at(values.len() / 2); + ArrValue::extended(flatten_inner(a), flatten_inner(b)) + } + if arrs.is_empty() { + return ArrValue::empty(); + } else if arrs.len() == 1 { + return arrs.into_iter().next().expect("single"); + } + flatten_inner(&arrs) +} diff --git a/crates/jrsonnet-stdlib/src/lib.rs b/crates/jrsonnet-stdlib/src/lib.rs index 037e6325..c5dcea12 100644 --- a/crates/jrsonnet-stdlib/src/lib.rs +++ b/crates/jrsonnet-stdlib/src/lib.rs @@ -84,6 +84,8 @@ pub fn stdlib_uncached(settings: Rc>) -> ObjValue { ("avg", builtin_avg::INST), ("removeAt", builtin_remove_at::INST), ("remove", builtin_remove::INST), + ("flattenArrays", builtin_flatten_arrays::INST), + ("filterMap", builtin_filter_map::INST), // Math ("abs", builtin_abs::INST), ("sign", builtin_sign::INST), @@ -137,17 +139,22 @@ pub fn stdlib_uncached(settings: Rc>) -> ObjValue { ("base64DecodeBytes", builtin_base64_decode_bytes::INST), // Objects ("objectFieldsEx", builtin_object_fields_ex::INST), + ("objectFields", builtin_object_fields::INST), + ("objectFieldsAll", builtin_object_fields_all::INST), ("objectValues", builtin_object_values::INST), ("objectValuesAll", builtin_object_values_all::INST), ("objectKeysValues", builtin_object_keys_values::INST), ("objectKeysValuesAll", builtin_object_keys_values_all::INST), ("objectHasEx", builtin_object_has_ex::INST), + ("objectHas", builtin_object_has::INST), + ("objectHasAll", builtin_object_has_all::INST), ("objectRemoveKey", builtin_object_remove_key::INST), // Manifest ("escapeStringJson", builtin_escape_string_json::INST), ("manifestJsonEx", builtin_manifest_json_ex::INST), ("manifestYamlDoc", builtin_manifest_yaml_doc::INST), ("manifestTomlEx", builtin_manifest_toml_ex::INST), + ("toString", builtin_to_string::INST), // Parsing ("parseJson", builtin_parse_json::INST), ("parseYaml", builtin_parse_yaml::INST), @@ -167,6 +174,7 @@ pub fn stdlib_uncached(settings: Rc>) -> ObjValue { ("bigint", builtin_bigint::INST), ("parseOctal", builtin_parse_octal::INST), ("parseHex", builtin_parse_hex::INST), + ("stringChars", builtin_string_chars::INST), // Misc ("length", builtin_length::INST), ("startsWith", builtin_starts_with::INST), @@ -175,6 +183,7 @@ pub fn stdlib_uncached(settings: Rc>) -> ObjValue { ("setMember", builtin_set_member::INST), ("setInter", builtin_set_inter::INST), ("setDiff", builtin_set_diff::INST), + ("setUnion", builtin_set_union::INST), // Compat ("__compare", builtin___compare::INST), ] @@ -347,19 +356,15 @@ impl jrsonnet_evaluator::ContextInitializer for ContextInitializer { let mut std = ObjValueBuilder::new(); std.with_super(self.stdlib_obj.clone()); - std.field("thisFile".into()) + std.field("thisFile") .hide() - .value(Val::string(match source.source_path().path() { - Some(p) => self.settings().path_resolver.resolve(p).into(), - None => source.source_path().to_string().into(), - })) - .expect("this object builder is empty"); + .value(match source.source_path().path() { + Some(p) => self.settings().path_resolver.resolve(p), + None => source.source_path().to_string(), + }); let stdlib_with_this_file = std.build(); - builder.bind( - "std".into(), - Thunk::evaluated(Val::Obj(stdlib_with_this_file)), - ); + builder.bind("std", Thunk::evaluated(Val::Obj(stdlib_with_this_file))); } fn as_any(&self) -> &dyn std::any::Any { self diff --git a/crates/jrsonnet-stdlib/src/manifest/mod.rs b/crates/jrsonnet-stdlib/src/manifest/mod.rs index 3d264f39..eb84ff20 100644 --- a/crates/jrsonnet-stdlib/src/manifest/mod.rs +++ b/crates/jrsonnet-stdlib/src/manifest/mod.rs @@ -60,3 +60,8 @@ pub fn builtin_manifest_toml_ex( preserve_order.unwrap_or(false), )) } + +#[builtin] +pub fn builtin_to_string(a: Val) -> Result { + a.to_string() +} diff --git a/crates/jrsonnet-stdlib/src/objects.rs b/crates/jrsonnet-stdlib/src/objects.rs index 3b20933f..64a3a5c6 100644 --- a/crates/jrsonnet-stdlib/src/objects.rs +++ b/crates/jrsonnet-stdlib/src/objects.rs @@ -1,6 +1,6 @@ use jrsonnet_evaluator::{ function::builtin, - val::{ArrValue, StrValue, Val}, + val::{ArrValue, Val}, IStr, ObjValue, ObjValueBuilder, }; @@ -17,10 +17,33 @@ pub fn builtin_object_fields_ex( #[cfg(feature = "exp-preserve-order")] preserve_order, ); - out.into_iter() - .map(StrValue::Flat) - .map(Val::Str) - .collect::>() + out.into_iter().map(Val::string).collect::>() +} + +#[builtin] +pub fn builtin_object_fields( + o: ObjValue, + #[cfg(feature = "exp-preserve-order")] preserve_order: Option, +) -> Vec { + builtin_object_fields_ex( + o, + false, + #[cfg(feature = "exp-preserve-order")] + preserve_order, + ) +} + +#[builtin] +pub fn builtin_object_fields_all( + o: ObjValue, + #[cfg(feature = "exp-preserve-order")] preserve_order: Option, +) -> Vec { + builtin_object_fields_ex( + o, + true, + #[cfg(feature = "exp-preserve-order")] + preserve_order, + ) } pub fn builtin_object_values_ex( @@ -104,6 +127,16 @@ pub fn builtin_object_has_ex(obj: ObjValue, fname: IStr, hidden: bool) -> bool { obj.has_field_ex(fname, hidden) } +#[builtin] +pub fn builtin_object_has(o: ObjValue, f: IStr) -> bool { + o.has_field(f) +} + +#[builtin] +pub fn builtin_object_has_all(o: ObjValue, f: IStr) -> bool { + o.has_field_include_hidden(f) +} + #[builtin] pub fn builtin_object_remove_key( obj: ObjValue, diff --git a/crates/jrsonnet-stdlib/src/sets.rs b/crates/jrsonnet-stdlib/src/sets.rs index 448a009e..eabc47f5 100644 --- a/crates/jrsonnet-stdlib/src/sets.rs +++ b/crates/jrsonnet-stdlib/src/sets.rs @@ -70,6 +70,7 @@ pub fn builtin_set_inter(a: ArrValue, b: ArrValue, keyF: Option) -> Res } Ok(ArrValue::lazy(out)) } + #[builtin] #[allow(non_snake_case, clippy::redundant_closure)] pub fn builtin_set_diff(a: ArrValue, b: ArrValue, keyF: Option) -> Result { @@ -115,3 +116,57 @@ pub fn builtin_set_diff(a: ArrValue, b: ArrValue, keyF: Option) -> Resu } Ok(ArrValue::lazy(out)) } + +#[builtin] +#[allow(non_snake_case, clippy::redundant_closure)] +pub fn builtin_set_union(a: ArrValue, b: ArrValue, keyF: Option) -> Result { + let mut a = a.iter_lazy(); + let mut b = b.iter_lazy(); + + let keyF = keyF + .unwrap_or(FuncVal::identity()) + .into_native::<((Thunk,), Val)>(); + let keyF = |v| keyF(v); + + let mut av = a.next(); + let mut bv = b.next(); + let mut ak = av.clone().map(keyF).transpose()?; + let mut bk = bv.clone().map(keyF).transpose()?; + + let mut out = Vec::new(); + while let (Some(ac), Some(bc)) = (&ak, &bk) { + match evaluate_compare_op(ac, bc, BinaryOpType::Lt)? { + Ordering::Less => { + out.push(av.clone().expect("ak != None")); + av = a.next(); + ak = av.clone().map(keyF).transpose()?; + } + Ordering::Greater => { + out.push(bv.clone().expect("bk != None")); + bv = b.next(); + bk = bv.clone().map(keyF).transpose()?; + } + Ordering::Equal => { + // NOTE: order matters, values in `a` win + out.push(av.clone().expect("ak != None")); + av = a.next(); + ak = av.clone().map(keyF).transpose()?; + bv = b.next(); + bk = bv.clone().map(keyF).transpose()?; + } + }; + } + // a.len() > b.len() + while let Some(_ac) = &ak { + out.push(av.clone().expect("ak != None")); + av = a.next(); + ak = av.clone().map(keyF).transpose()?; + } + // b.len() > a.len() + while let Some(_bc) = &bk { + out.push(bv.clone().expect("ak != None")); + bv = b.next(); + bk = bv.clone().map(keyF).transpose()?; + } + Ok(ArrValue::lazy(out)) +} diff --git a/crates/jrsonnet-stdlib/src/std.jsonnet b/crates/jrsonnet-stdlib/src/std.jsonnet index 3284595d..29490881 100644 --- a/crates/jrsonnet-stdlib/src/std.jsonnet +++ b/crates/jrsonnet-stdlib/src/std.jsonnet @@ -4,8 +4,6 @@ thisFile:: error 'std.thisFile is deprecated, to enable its support in jrsonnet - recompile it with "legacy-this-file" support.\nThis will slow down stdlib caching a bit, though', - toString(a):: '' + a, - lstripChars(str, chars):: if std.length(str) > 0 && std.member(chars, str[0]) then std.lstripChars(str[1:], chars) @@ -22,9 +20,6 @@ stripChars(str, chars):: std.lstripChars(std.rstripChars(str, chars), chars), - stringChars(str):: - std.makeArray(std.length(str), function(i) str[i]), - splitLimitR(str, c, maxsplits):: if maxsplits == -1 then std.splitLimit(str, c, -1) @@ -61,16 +56,6 @@ else error 'Expected string or array, got %s' % std.type(arr), - filterMap(filter_func, map_func, arr):: - if !std.isFunction(filter_func) then - error ('std.filterMap first param must be function, got ' + std.type(filter_func)) - else if !std.isFunction(map_func) then - error ('std.filterMap second param must be function, got ' + std.type(map_func)) - else if !std.isArray(arr) then - error ('std.filterMap third param must be array, got ' + std.type(arr)) - else - std.map(map_func, std.filter(filter_func, arr)), - assertEqual(a, b):: if a == b then true @@ -82,9 +67,6 @@ else if x > maxVal then maxVal else x, - flattenArrays(arrs):: - std.foldl(function(a, b) a + b, arrs, []), - manifestIni(ini):: local body_lines(body) = std.join([], [ @@ -196,24 +178,6 @@ aux(value), - setUnion(a, b, keyF=id):: - // NOTE: order matters, values in `a` win - local aux(a, b, i, j, acc) = - if i >= std.length(a) then - acc + b[j:] - else if j >= std.length(b) then - acc + a[i:] - else - local ak = keyF(a[i]); - local bk = keyF(b[j]); - if ak == bk then - aux(a, b, i + 1, j + 1, acc + [a[i]]) tailstrict - else if ak < bk then - aux(a, b, i + 1, j, acc + [a[i]]) tailstrict - else - aux(a, b, i, j + 1, acc + [b[j]]) tailstrict; - aux(a, b, 0, 0, []), - mergePatch(target, patch):: if std.isObject(patch) then local target_object = @@ -241,18 +205,6 @@ get(o, f, default=null, inc_hidden=true):: if std.objectHasEx(o, f, inc_hidden) then o[f] else default, - objectFields(o):: - std.objectFieldsEx(o, false), - - objectFieldsAll(o):: - std.objectFieldsEx(o, true), - - objectHas(o, f):: - std.objectHasEx(o, f, false), - - objectHasAll(o, f):: - std.objectHasEx(o, f, true), - resolvePath(f, r):: local arr = std.split(f, '/'); std.join('/', std.makeArray(std.length(arr) - 1, function(i) arr[i]) + [r]), diff --git a/crates/jrsonnet-stdlib/src/strings.rs b/crates/jrsonnet-stdlib/src/strings.rs index 9c8febe5..fa59e4d9 100644 --- a/crates/jrsonnet-stdlib/src/strings.rs +++ b/crates/jrsonnet-stdlib/src/strings.rs @@ -198,3 +198,8 @@ mod tests { assert_eq!(parse_nat::<16>("BbC").unwrap(), 0xBBC as f64); } } + +#[builtin] +pub fn builtin_string_chars(str: IStr) -> ArrValue { + ArrValue::chars(str.chars()) +}