Skip to content

Commit

Permalink
Use Rc<str> instead of string. (#52)
Browse files Browse the repository at this point in the history
This reduces std::mem::size_of::<Value>() to 16 bytes.
String values are also efficiently copied like Objects, arrays, sets etc.

When multiple function rules are evaluated, ensure that constant argument values
match before running the rule. If actual parameter does not match the constant parameter,
then the rule is skipped.

Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
  • Loading branch information
anakrish authored Nov 25, 2023
1 parent 4db2270 commit 3514594
Show file tree
Hide file tree
Showing 10 changed files with 117 additions and 89 deletions.
8 changes: 5 additions & 3 deletions src/builtins/encoding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ fn base64_decode(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Va
let encoded_str = ensure_string(name, &params[0], &args[0])?;
let decoded_bytes = BASE64.decode(encoded_str.as_bytes())?;
Ok(Value::String(
String::from_utf8_lossy(&decoded_bytes).to_string(),
String::from_utf8_lossy(&decoded_bytes).into(),
))
}

Expand All @@ -46,7 +46,8 @@ fn yaml_marshal(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Val
ensure_args_count(span, name, params, args, 1)?;
Ok(Value::String(
serde_yaml::to_string(&args[0])
.with_context(|| span.error("could not serialize to yaml"))?,
.with_context(|| span.error("could not serialize to yaml"))?
.into(),
))
}

Expand All @@ -70,7 +71,8 @@ fn json_marshal(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Val
ensure_args_count(span, name, params, args, 1)?;
Ok(Value::String(
serde_json::to_string(&args[0])
.with_context(|| span.error("could not serialize to json"))?,
.with_context(|| span.error("could not serialize to json"))?
.into(),
))
}

Expand Down
67 changes: 33 additions & 34 deletions src/builtins/strings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,23 +46,23 @@ fn concat(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
ensure_args_count(span, name, params, args, 2)?;
let delimiter = ensure_string(name, &params[0], &args[0])?;
let collection = ensure_string_collection(name, &params[1], &args[1])?;
Ok(Value::String(collection.join(&delimiter)))
Ok(Value::String(collection.join(&delimiter).into()))
}

fn contains(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
let name = "contains";
ensure_args_count(span, name, params, args, 2)?;
let s1 = ensure_string(name, &params[0], &args[0])?;
let s2 = ensure_string(name, &params[1], &args[1])?;
Ok(Value::Bool(s1.contains(&s2)))
Ok(Value::Bool(s1.contains(s2.as_ref())))
}

fn endswith(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
let name = "endswith";
ensure_args_count(span, name, params, args, 2)?;
let s1 = ensure_string(name, &params[0], &args[0])?;
let s2 = ensure_string(name, &params[1], &args[1])?;
Ok(Value::Bool(s1.ends_with(&s2)))
Ok(Value::Bool(s1.ends_with(s2.as_ref())))
}

fn format_int(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
Expand All @@ -77,14 +77,15 @@ fn format_int(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value
let n = n.floor() as u64;

Ok(Value::String(
sign.to_owned()
(sign.to_owned()
+ &match ensure_numeric(name, &params[1], &args[1])? as u64 {
2 => format!("{:b}", n),
8 => format!("{:o}", n),
10 => format!("{}", n),
16 => format!("{:x}", n),
_ => return Ok(Value::Undefined),
},
})
.into(),
))
}

Expand All @@ -93,7 +94,7 @@ fn indexof(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
ensure_args_count(span, name, params, args, 2)?;
let s1 = ensure_string(name, &params[0], &args[0])?;
let s2 = ensure_string(name, &params[1], &args[1])?;
Ok(Value::from_float(match s1.find(&s2) {
Ok(Value::from_float(match s1.find(s2.as_ref()) {
Some(pos) => pos as i64,
_ => -1,
} as Float))
Expand All @@ -109,7 +110,7 @@ fn indexof_n(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value>
let mut positions = vec![];
let mut idx = 0;
while idx < s1.len() {
if let Some(pos) = s1.find(&s2) {
if let Some(pos) = s1.find(s2.as_ref()) {
positions.push(Value::from_float(pos as Float));
idx = pos + 1;
} else {
Expand All @@ -123,7 +124,7 @@ fn lower(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
let name = "lower";
ensure_args_count(span, name, params, args, 1)?;
let s = ensure_string(name, &params[0], &args[0])?;
Ok(Value::String(s.to_lowercase()))
Ok(Value::String(s.to_lowercase().into()))
}

fn replace(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
Expand All @@ -132,7 +133,7 @@ fn replace(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
let s = ensure_string(name, &params[0], &args[0])?;
let old = ensure_string(name, &params[1], &args[1])?;
let new = ensure_string(name, &params[2], &args[2])?;
Ok(Value::String(s.replace(&old, &new)))
Ok(Value::String(s.replace(old.as_ref(), new.as_ref()).into()))
}

fn split(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
Expand All @@ -142,8 +143,8 @@ fn split(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
let delimiter = ensure_string(name, &params[1], &args[1])?;

Ok(Value::from_array(
s.split(&delimiter)
.map(|s| Value::String(s.to_string()))
s.split(delimiter.as_ref())
.map(|s| Value::String(s.into()))
.collect(),
))
}
Expand Down Expand Up @@ -285,7 +286,7 @@ fn sprintf(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
bail!(args_span.error("floating-point number specified for format verb {verb}."));
}

('s', Value::String(sv)) => s += &sv.to_string(),
('s', Value::String(sv)) => s += sv.as_ref(),
('s', _) => {
bail!(args_span.error("invalid non string argument specified for format verb %s"));
}
Expand All @@ -312,15 +313,15 @@ fn sprintf(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
));
}

Ok(Value::String(s.to_string()))
Ok(Value::String(s.into()))
}

fn any_prefix_match(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
let name = "strings.any_prefix_match";
ensure_args_count(span, name, params, args, 2)?;

let search = match &args[0] {
Value::String(s) => vec![s.as_str()],
Value::String(s) => vec![s.as_ref()],
Value::Array(_) | Value::Set(_) => ensure_string_collection(name, &params[0], &args[0])?,
_ => {
let span = params[0].span();
Expand All @@ -331,7 +332,7 @@ fn any_prefix_match(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result
};

let base = match &args[1] {
Value::String(s) => vec![s.as_str()],
Value::String(s) => vec![s.as_ref()],
Value::Array(_) | Value::Set(_) => ensure_string_collection(name, &params[1], &args[1])?,
_ => {
let span = params[0].span();
Expand All @@ -351,7 +352,7 @@ fn any_suffix_match(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result
ensure_args_count(span, name, params, args, 2)?;

let search = match &args[0] {
Value::String(s) => vec![s.as_str()],
Value::String(s) => vec![s.as_ref()],
Value::Array(_) | Value::Set(_) => ensure_string_collection(name, &params[0], &args[0])?,
_ => {
let span = params[0].span();
Expand All @@ -362,7 +363,7 @@ fn any_suffix_match(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result
};

let base = match &args[1] {
Value::String(s) => vec![s.as_str()],
Value::String(s) => vec![s.as_ref()],
Value::Array(_) | Value::Set(_) => ensure_string_collection(name, &params[1], &args[1])?,
_ => {
let span = params[0].span();
Expand All @@ -382,7 +383,7 @@ fn startswith(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value
ensure_args_count(span, name, params, args, 2)?;
let s1 = ensure_string(name, &params[0], &args[0])?;
let s2 = ensure_string(name, &params[1], &args[1])?;
Ok(Value::Bool(s1.starts_with(&s2)))
Ok(Value::Bool(s1.starts_with(s2.as_ref())))
}

fn replace_n(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
Expand All @@ -395,7 +396,7 @@ fn replace_n(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value>
for item in obj.as_ref().iter() {
match item {
(Value::String(k), Value::String(v)) => {
s = s.replace(k, v);
s = s.replace(k.as_ref(), v.as_ref()).into();
}
_ => {
bail!(span.error(
Expand All @@ -405,14 +406,14 @@ fn replace_n(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value>
}
}

Ok(Value::String(s))
Ok(Value::String(s.clone()))
}

fn reverse(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
let name = "reverse";
ensure_args_count(span, name, params, args, 1)?;
let s = ensure_string(name, &params[0], &args[0])?;
Ok(Value::String(s.chars().rev().collect()))
Ok(Value::String(s.chars().rev().collect::<String>().into()))
}

fn substring(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
Expand All @@ -437,20 +438,18 @@ fn substring(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value>
// Also: behavior of
// x = substring("hello", 20 + 0.0, 25)
if offset > s.len() || length <= offset {
return Ok(Value::String("".to_string()));
return Ok(Value::String("".into()));
}

Ok(Value::String(s[offset..offset + length].to_string()))
Ok(Value::String(s[offset..offset + length].into()))
}

fn trim(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
let name = "trim";
ensure_args_count(span, name, params, args, 2)?;
let s1 = ensure_string(name, &params[0], &args[0])?;
let s2 = ensure_string(name, &params[1], &args[1])?;
Ok(Value::String(
s1.trim_matches(|c| s2.contains(c)).to_string(),
))
Ok(Value::String(s1.trim_matches(|c| s2.contains(c)).into()))
}

fn trim_left(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
Expand All @@ -459,7 +458,7 @@ fn trim_left(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value>
let s1 = ensure_string(name, &params[0], &args[0])?;
let s2 = ensure_string(name, &params[1], &args[1])?;
Ok(Value::String(
s1.trim_start_matches(|c| s2.contains(c)).to_string(),
s1.trim_start_matches(|c| s2.contains(c)).into(),
))
}

Expand All @@ -468,8 +467,8 @@ fn trim_prefix(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Valu
ensure_args_count(span, name, params, args, 2)?;
let s1 = ensure_string(name, &params[0], &args[0])?;
let s2 = ensure_string(name, &params[1], &args[1])?;
Ok(Value::String(match s1.strip_prefix(&s2) {
Some(s) => s.to_string(),
Ok(Value::String(match s1.strip_prefix(s2.as_ref()) {
Some(s) => s.into(),
_ => s1,
}))
}
Expand All @@ -480,24 +479,24 @@ fn trim_right(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value
let s1 = ensure_string(name, &params[0], &args[0])?;
let s2 = ensure_string(name, &params[1], &args[1])?;
Ok(Value::String(
s1.trim_end_matches(|c| s2.contains(c)).to_string(),
s1.trim_end_matches(|c| s2.contains(c)).into(),
))
}

fn trim_space(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
let name = "trim_space";
ensure_args_count(span, name, params, args, 1)?;
let s = ensure_string(name, &params[0], &args[0])?;
Ok(Value::String(s.trim().to_string()))
Ok(Value::String(s.trim().into()))
}

fn trim_suffix(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
let name = "trim_suffix";
ensure_args_count(span, name, params, args, 2)?;
let s1 = ensure_string(name, &params[0], &args[0])?;
let s2 = ensure_string(name, &params[1], &args[1])?;
Ok(Value::String(match s1.strip_suffix(&s2) {
Some(s) => s.to_string(),
Ok(Value::String(match s1.strip_suffix(s2.as_ref()) {
Some(s) => s.into(),
_ => s1,
}))
}
Expand All @@ -506,5 +505,5 @@ fn upper(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
let name = "upper";
ensure_args_count(span, name, params, args, 1)?;
let s = ensure_string(name, &params[0], &args[0])?;
Ok(Value::String(s.to_uppercase()))
Ok(Value::String(s.to_uppercase().into()))
}
2 changes: 1 addition & 1 deletion src/builtins/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,5 +72,5 @@ pub fn get_type(value: &Value) -> &str {

pub fn type_name(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
ensure_args_count(span, "type_name", params, args, 1)?;
Ok(Value::String(get_type(&args[0]).to_string()))
Ok(Value::String(get_type(&args[0]).into()))
}
4 changes: 2 additions & 2 deletions src/builtins/units.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ fn parse(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
let name = "units.parse";
ensure_args_count(span, name, params, args, 1)?;
let string = ensure_string(name, &params[0], &args[0])?;
let string = string.as_str();
let string = string.as_ref();

// Remove quotes.
let string = if string.starts_with('"') && string.ends_with('"') && string.len() >= 2 {
Expand Down Expand Up @@ -101,7 +101,7 @@ fn parse_bytes(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Valu
let name = "units.parse_bytes";
ensure_args_count(span, name, params, args, 1)?;
let string = ensure_string(name, &params[0], &args[0])?;
let string = string.as_str();
let string = string.as_ref();

// Remove quotes.
let string = if string.starts_with('"') && string.ends_with('"') && string.len() >= 2 {
Expand Down
4 changes: 2 additions & 2 deletions src/builtins/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ pub fn ensure_numeric(fcn: &str, arg: &Expr, v: &Value) -> Result<Float> {
})
}

pub fn ensure_string(fcn: &str, arg: &Expr, v: &Value) -> Result<String> {
pub fn ensure_string(fcn: &str, arg: &Expr, v: &Value) -> Result<Rc<str>> {
Ok(match &v {
Value::String(s) => s.clone(),
_ => {
Expand All @@ -60,7 +60,7 @@ pub fn ensure_string_element<'a>(
idx: usize,
) -> Result<&'a str> {
Ok(match &v {
Value::String(s) => s.as_str(),
Value::String(s) => s.as_ref(),
_ => {
let span = arg.span();
bail!(span.error(
Expand Down
Loading

0 comments on commit 3514594

Please sign in to comment.