From bb84d33795c87b5c5f1d2ca4d5507347cc598185 Mon Sep 17 00:00:00 2001 From: Лач Date: Fri, 26 Jun 2020 11:21:29 +0000 Subject: [PATCH] perf(evaluator): faster json functions --- --- a/crates/jsonnet-evaluator/build.rs +++ b/crates/jsonnet-evaluator/build.rs @@ -34,7 +34,7 @@ Member::Field(FieldMember { name: FieldName::Fixed(name), .. - }) if **name == *"join" + }) if **name == *"join" || **name == *"manifestJsonEx" || **name == *"escapeStringJson" ) }) .collect(), --- a/crates/jsonnet-evaluator/src/evaluate.rs +++ b/crates/jsonnet-evaluator/src/evaluate.rs @@ -1,7 +1,7 @@ use crate::{ - context_creator, create_error, future_wrapper, lazy_val, push, with_state, Context, - ContextCreator, Error, FuncDesc, LazyBinding, LazyVal, ObjMember, ObjValue, Result, Val, - ValType, + context_creator, create_error, escape_string_json, future_wrapper, lazy_val, manifest_json_ex, + push, with_state, Context, ContextCreator, Error, FuncDesc, LazyBinding, LazyVal, ObjMember, + ObjValue, Result, Val, ValType, }; use closure::closure; use jsonnet_parser::{ @@ -730,6 +730,24 @@ (joiner, items) => panic!("bad join call: {:?} {:?}", joiner, items), } } + // Faster + ("std", "escapeStringJson") => { + assert_eq!(args.len(), 1); + match evaluate(context, &args[0].1)?.unwrap_if_lazy()? { + Val::Str(s) => Val::Str(escape_string_json(&s).into()), + _ => panic!("bad escapeStringJson call"), + } + } + // Faster + ("std", "manifestJsonEx") => { + assert_eq!(args.len(), 2); + let value = evaluate(context.clone(), &args[0].1)?.unwrap_if_lazy()?; + let ident = evaluate(context, &args[1].1)?.unwrap_if_lazy()?; + match (value, ident) { + (o, Val::Str(s)) => Val::Str(manifest_json_ex(&o, &s)?.into()), + _ => panic!("bad escapeStringJson call"), + } + } (ns, name) => panic!("Intristic not found: {}.{}", ns, name), }, Val::Func(f) => { --- a/crates/jsonnet-evaluator/src/val.rs +++ b/crates/jsonnet-evaluator/src/val.rs @@ -194,9 +194,97 @@ } } +pub fn manifest_json_ex(val: &Val, padding: &str) -> Result { + let mut out = String::new(); + manifest_json_ex_buf(val, &mut out, padding, &mut String::new())?; + Ok(out) +} +fn manifest_json_ex_buf( + val: &Val, + buf: &mut String, + padding: &str, + cur_padding: &mut String, +) -> Result<()> { + use std::fmt::Write; + match val.unwrap_if_lazy()? { + Val::Bool(v) => { + if v { + buf.push_str("true"); } else { - unreachable!() + buf.push_str("false"); + } + } + Val::Null => buf.push_str("null"), + Val::Str(s) => buf.push_str(&escape_string_json(&s)), + Val::Num(n) => write!(buf, "{}", n).unwrap(), + Val::Arr(items) => { + buf.push_str("[\n"); + if !items.is_empty() { + let old_len = cur_padding.len(); + cur_padding.push_str(padding); + for (i, item) in items.iter().enumerate() { + if i != 0 { + buf.push_str(",\n") + } + buf.push_str(cur_padding); + manifest_json_ex_buf(item, buf, padding, cur_padding)?; + } + cur_padding.truncate(old_len); + } + buf.push('\n'); + buf.push_str(cur_padding); + buf.push(']'); + } + Val::Obj(obj) => { + buf.push_str("{\n"); + let fields = obj.visible_fields(); + if !fields.is_empty() { + let old_len = cur_padding.len(); + cur_padding.push_str(padding); + for (i, field) in fields.into_iter().enumerate() { + if i != 0 { + buf.push_str(",\n") + } + buf.push_str(cur_padding); + buf.push_str(&escape_string_json(&field)); + buf.push_str(": "); + manifest_json_ex_buf(&obj.get(field)?.unwrap(), buf, padding, cur_padding)?; + } + cur_padding.truncate(old_len); + } + buf.push('\n'); + buf.push_str(cur_padding); + buf.push('}'); + } + Val::Func(_) | Val::Intristic(_, _) => panic!("tried to manifest function"), + Val::Lazy(_) => unreachable!(), + }; + Ok(()) +} +pub fn escape_string_json(s: &str) -> String { + use std::fmt::Write; + let mut out = String::new(); + out.push('"'); + for c in s.chars() { + match c { + '"' => out.push_str("\\\""), + '\\' => out.push_str("\\\\"), + '\u{0008}' => out.push_str("\\b"), + '\u{000c}' => out.push_str("\\f"), + '\n' => out.push_str("\\n"), + '\r' => out.push_str("\\r"), + '\t' => out.push_str("\\t"), + c if c < 32 as char || (c >= 127 as char && c <= 159 as char) => { + write!(out, "\\u{:04x}", c as u32).unwrap() } - }) + c => out.push(c), + } } + out.push('"'); + out +} + +#[test] +fn json_test() { + assert_eq!(escape_string_json("\u{001f}"), "\"\\u001f\"") } -- gitstuff