From 9c0cd68fcf1632cd2087eea132555b8ceff5ce1b Mon Sep 17 00:00:00 2001 From: Ning Sun Date: Tue, 14 Mar 2023 20:56:45 +0800 Subject: [PATCH 1/2] feat: use smartstring for internal path string --- Cargo.toml | 1 + src/block.rs | 13 +++++++------ src/context.rs | 15 ++++++++------- src/helpers/helper_each.rs | 23 ++++++++++++++++++----- src/helpers/helper_log.rs | 2 +- src/json/path.rs | 25 +++++++++++++------------ src/json/value.rs | 7 ++++--- src/util.rs | 6 ++++-- 8 files changed, 56 insertions(+), 36 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 60a47903e..95ec0668c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,6 +28,7 @@ pest = "2.1.0" pest_derive = "2.1.0" serde = "1.0.0" serde_json = "1.0.39" +smartstring = "1" walkdir = { version = "2.2.3", optional = true } rhai = { version = "1.6", optional = true, features = ["sync", "serde"] } rust-embed = { version = "6.3.0", optional = true, features = ["include-exclude"] } diff --git a/src/block.rs b/src/block.rs index 10e429136..decbe230f 100644 --- a/src/block.rs +++ b/src/block.rs @@ -1,6 +1,7 @@ use std::collections::BTreeMap; use serde_json::value::Value as Json; +use smartstring::alias::CompactString; use crate::error::RenderError; use crate::local_vars::LocalVars; @@ -8,7 +9,7 @@ use crate::local_vars::LocalVars; #[derive(Clone, Debug)] pub enum BlockParamHolder { // a reference to certain context value - Path(Vec), + Path(Vec), // an actual value holder Value(Json), } @@ -18,7 +19,7 @@ impl BlockParamHolder { BlockParamHolder::Value(v) } - pub fn path(r: Vec) -> BlockParamHolder { + pub fn path(r: Vec) -> BlockParamHolder { BlockParamHolder::Path(r) } } @@ -37,7 +38,7 @@ impl<'reg> BlockParams<'reg> { /// Add a path reference as the parameter. The `path` is a vector of path /// segments the relative to current block's base path. - pub fn add_path(&mut self, k: &'reg str, path: Vec) -> Result<(), RenderError> { + pub fn add_path(&mut self, k: &'reg str, path: Vec) -> Result<(), RenderError> { self.data.insert(k, BlockParamHolder::path(path)); Ok(()) } @@ -58,7 +59,7 @@ impl<'reg> BlockParams<'reg> { #[derive(Debug, Clone, Default)] pub struct BlockContext<'rc> { /// the base_path of current block scope - base_path: Vec, + base_path: Vec, /// the base_value of current block scope, when the block is using a /// constant or derived value as block base base_value: Option, @@ -91,12 +92,12 @@ impl<'rc> BlockContext<'rc> { /// borrow a reference to current scope's base path /// all paths inside this block will be relative to this path - pub fn base_path(&self) -> &Vec { + pub fn base_path(&self) -> &Vec { &self.base_path } /// borrow a mutable reference to the base path - pub fn base_path_mut(&mut self) -> &mut Vec { + pub fn base_path_mut(&mut self) -> &mut Vec { &mut self.base_path } diff --git a/src/context.rs b/src/context.rs index 6b5cfffba..2d9148a3b 100644 --- a/src/context.rs +++ b/src/context.rs @@ -2,6 +2,7 @@ use std::collections::{HashMap, VecDeque}; use serde::Serialize; use serde_json::value::{to_value, Map, Value as Json}; +use smartstring::alias::CompactString; use crate::block::{BlockContext, BlockParamHolder}; use crate::error::RenderError; @@ -23,13 +24,13 @@ pub struct Context { enum ResolvedPath<'a> { // FIXME: change to borrowed when possible // full path - AbsolutePath(Vec), + AbsolutePath(Vec), // relative path and path root - RelativePath(Vec), + RelativePath(Vec), // relative path against block param value - BlockParamValue(Vec, &'a Json), + BlockParamValue(Vec, &'a Json), // relative path against derived value, - LocalValue(Vec, &'a Json), + LocalValue(Vec, &'a Json), } fn parse_json_visitor<'a>( @@ -127,7 +128,7 @@ fn get_data<'a>(d: Option<&'a Json>, p: &str) -> Result, Render fn get_in_block_params<'a>( block_contexts: &'a VecDeque>, p: &str, -) -> Option<(&'a BlockParamHolder, &'a Vec)> { +) -> Option<(&'a BlockParamHolder, &'a Vec)> { for bc in block_contexts { let v = bc.get_block_param(p); if v.is_some() { @@ -406,7 +407,7 @@ mod test { }); let ctx = Context::wraps(&m).unwrap(); let mut block = BlockContext::new(); - *block.base_path_mut() = ["a".to_owned(), "b".to_owned()].to_vec(); + *block.base_path_mut() = ["a".into(), "b".into()].to_vec(); let mut blocks = VecDeque::new(); blocks.push_front(block); @@ -430,7 +431,7 @@ mod test { let ctx = Context::wraps(&m).unwrap(); let mut block_params = BlockParams::new(); block_params - .add_path("z", ["0".to_owned(), "a".to_owned()].to_vec()) + .add_path("z", ["0".into(), "a".into()].to_vec()) .unwrap(); block_params.add_value("t", json!("good")).unwrap(); diff --git a/src/helpers/helper_each.rs b/src/helpers/helper_each.rs index 2fec8f010..cf7ab815d 100644 --- a/src/helpers/helper_each.rs +++ b/src/helpers/helper_each.rs @@ -1,4 +1,5 @@ use serde_json::value::Value as Json; +use smartstring::alias::CompactString; use super::block_util::create_block; use crate::block::{BlockContext, BlockParams}; @@ -13,8 +14,8 @@ use crate::util::copy_on_push_vec; fn update_block_context( block: &mut BlockContext<'_>, - base_path: Option<&Vec>, - relative_path: String, + base_path: Option<&Vec>, + relative_path: CompactString, is_first: bool, value: &Json, ) { @@ -32,7 +33,7 @@ fn update_block_context( fn set_block_param<'rc>( block: &mut BlockContext<'rc>, h: &Helper<'rc>, - base_path: Option<&Vec>, + base_path: Option<&Vec>, k: &Json, v: &Json, ) -> Result<(), RenderError> { @@ -100,7 +101,13 @@ impl HelperDef for EachHelper { block.set_local_var("last", to_json(is_last)); block.set_local_var("index", index.clone()); - update_block_context(block, array_path, i.to_string(), is_first, v); + update_block_context( + block, + array_path, + i.to_string().into(), + is_first, + v, + ); set_block_param(block, h, array_path, &index, v)?; } @@ -130,7 +137,13 @@ impl HelperDef for EachHelper { block.set_local_var("last", to_json(is_last)); block.set_local_var("key", key.clone()); - update_block_context(block, obj_path, k.to_string(), is_first, v); + update_block_context( + block, + obj_path, + k.to_string().into(), + is_first, + v, + ); set_block_param(block, h, obj_path, &key, v)?; } diff --git a/src/helpers/helper_log.rs b/src/helpers/helper_log.rs index 36394be32..bb97af8f6 100644 --- a/src/helpers/helper_log.rs +++ b/src/helpers/helper_log.rs @@ -46,7 +46,7 @@ impl HelperDef for LogHelper { if let Ok(log_level) = Level::from_str(level) { log!(log_level, "{}", param_to_log) } else { - return Err(RenderError::new(&format!( + return Err(RenderError::new(format!( "Unsupported logging level {}", level ))); diff --git a/src/json/path.rs b/src/json/path.rs index 17a7a91ee..7c989efb2 100644 --- a/src/json/path.rs +++ b/src/json/path.rs @@ -2,13 +2,14 @@ use std::iter::Peekable; use pest::iterators::Pair; use pest::Parser; +use smartstring::alias::CompactString; use crate::error::RenderError; use crate::grammar::{HandlebarsParser, Rule}; #[derive(PartialEq, Eq, Clone, Debug)] pub enum PathSeg { - Named(String), + Named(CompactString), Ruled(Rule), } @@ -18,16 +19,16 @@ pub enum PathSeg { /// or a normal relative path like `a/b/c`. #[derive(PartialEq, Eq, Clone, Debug)] pub enum Path { - Relative((Vec, String)), - Local((usize, String, String)), + Relative((Vec, CompactString)), + Local((usize, CompactString, CompactString)), } impl Path { pub(crate) fn new(raw: &str, segs: Vec) -> Path { if let Some((level, name)) = get_local_path_and_level(&segs) { - Path::Local((level, name, raw.to_owned())) + Path::Local((level, name, raw.into())) } else { - Path::Relative((segs, raw.to_owned())) + Path::Relative((segs, raw.into())) } } @@ -49,16 +50,16 @@ impl Path { } pub(crate) fn current() -> Path { - Path::Relative((Vec::with_capacity(0), "".to_owned())) + Path::Relative((Vec::with_capacity(0), "".into())) } // for test only pub(crate) fn with_named_paths(name_segs: &[&str]) -> Path { let segs = name_segs .iter() - .map(|n| PathSeg::Named((*n).to_string())) + .map(|n| PathSeg::Named((*n).into())) .collect(); - Path::Relative((segs, name_segs.join("/"))) + Path::Relative((segs, name_segs.join("/").into())) } // for test only @@ -70,7 +71,7 @@ impl Path { } } -fn get_local_path_and_level(paths: &[PathSeg]) -> Option<(usize, String)> { +fn get_local_path_and_level(paths: &[PathSeg]) -> Option<(usize, CompactString)> { paths.get(0).and_then(|seg| { if seg == &PathSeg::Ruled(Rule::path_local) { let mut level = 0; @@ -112,7 +113,7 @@ where Rule::path_id | Rule::path_raw_id => { let name = n.as_str(); if name != "this" { - path_stack.push(PathSeg::Named(name.to_string())); + path_stack.push(PathSeg::Named(name.into())); } } _ => {} @@ -124,11 +125,11 @@ where path_stack } -pub(crate) fn merge_json_path(path_stack: &mut Vec, relative_path: &[PathSeg]) { +pub(crate) fn merge_json_path(path_stack: &mut Vec, relative_path: &[PathSeg]) { for seg in relative_path { match seg { PathSeg::Named(ref s) => { - path_stack.push(s.to_owned()); + path_stack.push(s.clone()); } PathSeg::Ruled(Rule::path_root) => {} PathSeg::Ruled(Rule::path_up) => {} diff --git a/src/json/value.rs b/src/json/value.rs index d8e851a66..7f8488144 100644 --- a/src/json/value.rs +++ b/src/json/value.rs @@ -1,5 +1,6 @@ use serde::Serialize; use serde_json::value::{to_value, Value as Json}; +use smartstring::alias::CompactString; pub(crate) static DEFAULT_VALUE: Json = Json::Null; @@ -14,7 +15,7 @@ pub enum ScopedJson<'rc> { Constant(&'rc Json), Derived(Json), // represents a json reference to context value, its full path - Context(&'rc Json, Vec), + Context(&'rc Json, Vec), Missing, } @@ -42,7 +43,7 @@ impl<'rc> ScopedJson<'rc> { ScopedJson::Derived(v.clone()) } - pub fn context_path(&self) -> Option<&Vec> { + pub fn context_path(&self) -> Option<&Vec> { match self { ScopedJson::Context(_, ref p) => Some(p), _ => None, @@ -79,7 +80,7 @@ impl<'rc> PathAndJson<'rc> { } /// Returns full path to this value if any - pub fn context_path(&self) -> Option<&Vec> { + pub fn context_path(&self) -> Option<&Vec> { self.value.context_path() } diff --git a/src/util.rs b/src/util.rs index c237f97a2..c8c804f15 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,3 +1,5 @@ +use smartstring::alias::CompactString; + #[inline] pub(crate) fn copy_on_push_vec(input: &[T], el: T) -> Vec where @@ -10,8 +12,8 @@ where } #[inline] -pub(crate) fn extend(base: &mut Vec, slice: &[String]) { +pub(crate) fn extend(base: &mut Vec, slice: &[CompactString]) { for i in slice { - base.push(i.to_owned()); + base.push(i.clone()); } } From d74f9669a98b463799dadc9482b4b8641e6aef21 Mon Sep 17 00:00:00 2001 From: Ning Sun Date: Tue, 14 Mar 2023 21:07:42 +0800 Subject: [PATCH 2/2] feat: switch to lazy-compact string for better performance --- src/block.rs | 18 +++++++++++------- src/context.rs | 12 ++++++------ src/helpers/helper_each.rs | 8 ++++---- src/json/path.rs | 12 ++++++------ src/json/value.rs | 8 ++++---- src/util.rs | 4 ++-- 6 files changed, 33 insertions(+), 29 deletions(-) diff --git a/src/block.rs b/src/block.rs index decbe230f..91f2252b7 100644 --- a/src/block.rs +++ b/src/block.rs @@ -1,7 +1,7 @@ use std::collections::BTreeMap; use serde_json::value::Value as Json; -use smartstring::alias::CompactString; +use smartstring::alias::String as LazyCompactString; use crate::error::RenderError; use crate::local_vars::LocalVars; @@ -9,7 +9,7 @@ use crate::local_vars::LocalVars; #[derive(Clone, Debug)] pub enum BlockParamHolder { // a reference to certain context value - Path(Vec), + Path(Vec), // an actual value holder Value(Json), } @@ -19,7 +19,7 @@ impl BlockParamHolder { BlockParamHolder::Value(v) } - pub fn path(r: Vec) -> BlockParamHolder { + pub fn path(r: Vec) -> BlockParamHolder { BlockParamHolder::Path(r) } } @@ -38,7 +38,11 @@ impl<'reg> BlockParams<'reg> { /// Add a path reference as the parameter. The `path` is a vector of path /// segments the relative to current block's base path. - pub fn add_path(&mut self, k: &'reg str, path: Vec) -> Result<(), RenderError> { + pub fn add_path( + &mut self, + k: &'reg str, + path: Vec, + ) -> Result<(), RenderError> { self.data.insert(k, BlockParamHolder::path(path)); Ok(()) } @@ -59,7 +63,7 @@ impl<'reg> BlockParams<'reg> { #[derive(Debug, Clone, Default)] pub struct BlockContext<'rc> { /// the base_path of current block scope - base_path: Vec, + base_path: Vec, /// the base_value of current block scope, when the block is using a /// constant or derived value as block base base_value: Option, @@ -92,12 +96,12 @@ impl<'rc> BlockContext<'rc> { /// borrow a reference to current scope's base path /// all paths inside this block will be relative to this path - pub fn base_path(&self) -> &Vec { + pub fn base_path(&self) -> &Vec { &self.base_path } /// borrow a mutable reference to the base path - pub fn base_path_mut(&mut self) -> &mut Vec { + pub fn base_path_mut(&mut self) -> &mut Vec { &mut self.base_path } diff --git a/src/context.rs b/src/context.rs index 2d9148a3b..c0877b0f1 100644 --- a/src/context.rs +++ b/src/context.rs @@ -2,7 +2,7 @@ use std::collections::{HashMap, VecDeque}; use serde::Serialize; use serde_json::value::{to_value, Map, Value as Json}; -use smartstring::alias::CompactString; +use smartstring::alias::String as LazyCompactString; use crate::block::{BlockContext, BlockParamHolder}; use crate::error::RenderError; @@ -24,13 +24,13 @@ pub struct Context { enum ResolvedPath<'a> { // FIXME: change to borrowed when possible // full path - AbsolutePath(Vec), + AbsolutePath(Vec), // relative path and path root - RelativePath(Vec), + RelativePath(Vec), // relative path against block param value - BlockParamValue(Vec, &'a Json), + BlockParamValue(Vec, &'a Json), // relative path against derived value, - LocalValue(Vec, &'a Json), + LocalValue(Vec, &'a Json), } fn parse_json_visitor<'a>( @@ -128,7 +128,7 @@ fn get_data<'a>(d: Option<&'a Json>, p: &str) -> Result, Render fn get_in_block_params<'a>( block_contexts: &'a VecDeque>, p: &str, -) -> Option<(&'a BlockParamHolder, &'a Vec)> { +) -> Option<(&'a BlockParamHolder, &'a Vec)> { for bc in block_contexts { let v = bc.get_block_param(p); if v.is_some() { diff --git a/src/helpers/helper_each.rs b/src/helpers/helper_each.rs index cf7ab815d..76ef3993f 100644 --- a/src/helpers/helper_each.rs +++ b/src/helpers/helper_each.rs @@ -1,5 +1,5 @@ use serde_json::value::Value as Json; -use smartstring::alias::CompactString; +use smartstring::alias::String as LazyCompactString; use super::block_util::create_block; use crate::block::{BlockContext, BlockParams}; @@ -14,8 +14,8 @@ use crate::util::copy_on_push_vec; fn update_block_context( block: &mut BlockContext<'_>, - base_path: Option<&Vec>, - relative_path: CompactString, + base_path: Option<&Vec>, + relative_path: LazyCompactString, is_first: bool, value: &Json, ) { @@ -33,7 +33,7 @@ fn update_block_context( fn set_block_param<'rc>( block: &mut BlockContext<'rc>, h: &Helper<'rc>, - base_path: Option<&Vec>, + base_path: Option<&Vec>, k: &Json, v: &Json, ) -> Result<(), RenderError> { diff --git a/src/json/path.rs b/src/json/path.rs index 7c989efb2..2ae203843 100644 --- a/src/json/path.rs +++ b/src/json/path.rs @@ -2,14 +2,14 @@ use std::iter::Peekable; use pest::iterators::Pair; use pest::Parser; -use smartstring::alias::CompactString; +use smartstring::alias::String as LazyCompactString; use crate::error::RenderError; use crate::grammar::{HandlebarsParser, Rule}; #[derive(PartialEq, Eq, Clone, Debug)] pub enum PathSeg { - Named(CompactString), + Named(LazyCompactString), Ruled(Rule), } @@ -19,8 +19,8 @@ pub enum PathSeg { /// or a normal relative path like `a/b/c`. #[derive(PartialEq, Eq, Clone, Debug)] pub enum Path { - Relative((Vec, CompactString)), - Local((usize, CompactString, CompactString)), + Relative((Vec, LazyCompactString)), + Local((usize, LazyCompactString, LazyCompactString)), } impl Path { @@ -71,7 +71,7 @@ impl Path { } } -fn get_local_path_and_level(paths: &[PathSeg]) -> Option<(usize, CompactString)> { +fn get_local_path_and_level(paths: &[PathSeg]) -> Option<(usize, LazyCompactString)> { paths.get(0).and_then(|seg| { if seg == &PathSeg::Ruled(Rule::path_local) { let mut level = 0; @@ -125,7 +125,7 @@ where path_stack } -pub(crate) fn merge_json_path(path_stack: &mut Vec, relative_path: &[PathSeg]) { +pub(crate) fn merge_json_path(path_stack: &mut Vec, relative_path: &[PathSeg]) { for seg in relative_path { match seg { PathSeg::Named(ref s) => { diff --git a/src/json/value.rs b/src/json/value.rs index 7f8488144..a469a78e8 100644 --- a/src/json/value.rs +++ b/src/json/value.rs @@ -1,6 +1,6 @@ use serde::Serialize; use serde_json::value::{to_value, Value as Json}; -use smartstring::alias::CompactString; +use smartstring::alias::String as LazyCompactString; pub(crate) static DEFAULT_VALUE: Json = Json::Null; @@ -15,7 +15,7 @@ pub enum ScopedJson<'rc> { Constant(&'rc Json), Derived(Json), // represents a json reference to context value, its full path - Context(&'rc Json, Vec), + Context(&'rc Json, Vec), Missing, } @@ -43,7 +43,7 @@ impl<'rc> ScopedJson<'rc> { ScopedJson::Derived(v.clone()) } - pub fn context_path(&self) -> Option<&Vec> { + pub fn context_path(&self) -> Option<&Vec> { match self { ScopedJson::Context(_, ref p) => Some(p), _ => None, @@ -80,7 +80,7 @@ impl<'rc> PathAndJson<'rc> { } /// Returns full path to this value if any - pub fn context_path(&self) -> Option<&Vec> { + pub fn context_path(&self) -> Option<&Vec> { self.value.context_path() } diff --git a/src/util.rs b/src/util.rs index c8c804f15..d121bf891 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,4 +1,4 @@ -use smartstring::alias::CompactString; +use smartstring::alias::String as LazyCompactString; #[inline] pub(crate) fn copy_on_push_vec(input: &[T], el: T) -> Vec @@ -12,7 +12,7 @@ where } #[inline] -pub(crate) fn extend(base: &mut Vec, slice: &[CompactString]) { +pub(crate) fn extend(base: &mut Vec, slice: &[LazyCompactString]) { for i in slice { base.push(i.clone()); }