Skip to content

Commit c09c871

Browse files
committed
feat: Implement value size vrl function
The function returns the byte size of a given value
1 parent 4c78860 commit c09c871

File tree

2 files changed

+164
-0
lines changed

2 files changed

+164
-0
lines changed

src/stdlib/mezmo_value_size.rs

+161
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
use crate::compiler::prelude::*;
2+
3+
// Add some overhead to the array and object size
4+
// The overhead here is carried over from the implementation of usage metrics
5+
// to ensure alignment of values
6+
const BASE_ARRAY_SIZE: usize = 8;
7+
const BASE_BTREE_SIZE: usize = 8;
8+
9+
// Implementation borrows from usage_metrics computation in vector source
10+
// See: https://github.com/answerbook/vector/blob/c6c061b225008047b508f8215226b508f7686549/lib/vector-core/src/usage_metrics/mod.rs#L557
11+
fn value_size(value: &Value) -> usize {
12+
let res = match value {
13+
Value::Bytes(v) => v.len(),
14+
Value::Boolean(_) => 1,
15+
Value::Timestamp(_) | Value::Integer(_) | Value::Float(_) => 8,
16+
Value::Regex(v) => v.as_str().len(),
17+
Value::Object(v) => {
18+
BASE_BTREE_SIZE
19+
+ v.iter()
20+
.map(|(k, v)| k.len() + value_size(v))
21+
.sum::<usize>()
22+
}
23+
Value::Array(v) => BASE_ARRAY_SIZE + v.iter().map(value_size).sum::<usize>(),
24+
Value::Null => 0, // No value, just the type definition
25+
};
26+
27+
res
28+
}
29+
30+
#[derive(Clone, Copy, Debug)]
31+
pub struct MezmoValueSize;
32+
33+
impl Function for MezmoValueSize {
34+
fn identifier(&self) -> &'static str {
35+
"mezmo_value_size"
36+
}
37+
38+
fn parameters(&self) -> &'static [Parameter] {
39+
&[Parameter {
40+
keyword: "value",
41+
kind: kind::ANY,
42+
required: true,
43+
}]
44+
}
45+
46+
fn examples(&self) -> &'static [Example] {
47+
&[
48+
Example {
49+
title: "metrics for string",
50+
source: r#"mezmo_value_size("foobar")"#,
51+
result: Ok("{count: 1, bytes: 14, chars: 10}"),
52+
},
53+
Example {
54+
title: "metrics for object",
55+
source: r#"mezmo_value_size({"name": "jon doe", "department": "sales", "notes": {"entry" => "foobar works"}}, field_metrics: true)"#,
56+
result: Ok("{count: 1, bytes: 14, chars: 12}"),
57+
},
58+
]
59+
}
60+
61+
fn compile(
62+
&self,
63+
_: &TypeState,
64+
_: &mut FunctionCompileContext,
65+
arguments: ArgumentList,
66+
) -> Compiled {
67+
let value = arguments.required("value");
68+
Ok(MezmoValueSizeFn { value }.as_expr())
69+
}
70+
}
71+
72+
#[derive(Debug, Clone)]
73+
struct MezmoValueSizeFn {
74+
value: Box<dyn Expression>,
75+
}
76+
77+
impl FunctionExpression for MezmoValueSizeFn {
78+
fn resolve(&self, ctx: &mut Context) -> Resolved {
79+
let value = self.value.resolve(ctx)?;
80+
Ok(value_size(&value).into())
81+
}
82+
83+
fn type_def(&self, _state: &state::TypeState) -> TypeDef {
84+
TypeDef::integer().infallible()
85+
}
86+
}
87+
88+
#[cfg(test)]
89+
#[allow(clippy::trivial_regex)]
90+
mod tests {
91+
use super::*;
92+
use crate::value;
93+
use chrono::Utc;
94+
95+
test_function![
96+
value_size => MezmoValueSize;
97+
98+
byte_value {
99+
args: func_args![value: Bytes::from("foobar")],
100+
want: Ok(value!(6)),
101+
tdef: TypeDef::integer().infallible(),
102+
}
103+
104+
integer_value {
105+
args: func_args![value: 10],
106+
want: Ok(value!(8)),
107+
tdef: TypeDef::integer().infallible(),
108+
}
109+
110+
float_value {
111+
args: func_args![value: 15.2],
112+
want: Ok(value!(8)),
113+
tdef: TypeDef::integer().infallible(),
114+
}
115+
116+
bool_value {
117+
args: func_args![value: true],
118+
want: Ok(value!(1)),
119+
tdef: TypeDef::integer().infallible(),
120+
}
121+
122+
timestamp_value {
123+
args: func_args![value: Utc::now()],
124+
want: Ok(value!(8)),
125+
tdef: TypeDef::integer().infallible(),
126+
}
127+
128+
object_value {
129+
args: func_args![
130+
value: value!({
131+
"name": "ben johnson",
132+
"age": 20,
133+
"balance": 200.52,
134+
})],
135+
want: Ok(value!(49)),
136+
tdef: TypeDef::integer().infallible(),
137+
}
138+
139+
array_value {
140+
args: func_args![
141+
value: value!([
142+
{
143+
"name": "ben johnson",
144+
"age": 20,
145+
"balance": 200.52,
146+
},
147+
"something just happened"
148+
])],
149+
want: Ok(value!(80)),
150+
tdef: TypeDef::integer().infallible(),
151+
}
152+
153+
null_value {
154+
args: func_args![
155+
value: Value::Null
156+
],
157+
want: Ok(value!(0)),
158+
tdef: TypeDef::integer().infallible(),
159+
}
160+
];
161+
}

src/stdlib/mod.rs

+3
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ cfg_if::cfg_if! {
137137
mod mezmo_is_truthy;
138138
mod mezmo_last_index_of;
139139
mod mezmo_length;
140+
mod mezmo_value_size;
140141
mod mezmo_matching_patterns;
141142
mod mezmo_pad_end;
142143
mod mezmo_pad_start;
@@ -358,6 +359,7 @@ cfg_if::cfg_if! {
358359
pub use mezmo_to_string::MezmoToString;
359360
pub use mezmo_trim_end::MezmoTrimEnd;
360361
pub use mezmo_trim_start::MezmoTrimStart;
362+
pub use mezmo_value_size::MezmoValueSize;
361363
pub use mod_func::Mod;
362364
pub use now::Now;
363365
pub use object::Object;
@@ -549,6 +551,7 @@ pub fn all() -> Vec<Box<dyn Function>> {
549551
Box::new(MezmoIsTruthy),
550552
Box::new(MezmoLastIndexOf),
551553
Box::new(MezmoLength),
554+
Box::new(MezmoValueSize),
552555
Box::new(MezmoLt),
553556
Box::new(MezmoLte),
554557
Box::new(MezmoMatchingPatterns),

0 commit comments

Comments
 (0)