json_size/
lib.rs

1/// Calculates the approximate size of a `serde_json::Value` in bytes.
2///
3/// This function estimates the memory consumption of the given `serde_json::Value` object, including its nested structures. The estimation is based on the following assumptions:
4///
5/// - Null, Boolean, and Number types have no additional size overhead.
6/// - String sizes are based on the capacity of the internal buffer.
7/// - Array sizes are calculated recursively based on the sum of each element's size.
8/// - Object sizes are calculated recursively, summing the size of each key-value pair. An additional crude approximation of map entry overhead is included.
9///
10/// ## Parameters
11/// - `v`: A reference to a `serde_json::Value` whose size will be estimated.
12///
13/// ## Returns
14/// An estimated size of the provided JSON value in bytes.
15///
16/// ## Example
17/// ```
18/// use serde_json::json;
19/// use json_size::sizeof_val;
20///
21/// let val = json!({
22///     "name": "OpenAI",
23///     "founded": 2015,
24///     "services": ["chatbot", "API"]
25/// });
26///
27/// let size = sizeof_val(&val);
28/// println!("Estimated size: {} bytes", size);
29/// ```
30///
31/// ## Caveats
32/// - This estimation might not be precise for objects using arbitrary precision numbers.
33/// - The estimation might vary depending on the specific architecture and implementation of the `serde_json` crate.
34///
35/// ## Implementation
36use serde_json::Value;
37use std::mem::size_of;
38
39const STRING_OVERHEAD: usize = size_of::<String>();
40const MAP_ENTRY_OVERHEAD: usize = size_of::<usize>() * 3;
41
42pub fn sizeof_val(v: &Value) -> usize {
43    size_of::<Value>()
44        + match v {
45            Value::Null => 0,
46            Value::Bool(_) => 0,
47            Value::Number(_) => 0, // incorrect if arbitrary_precision is enabled
48            Value::String(s) => STRING_OVERHEAD + s.capacity(),
49            Value::Array(a) => a.iter().map(sizeof_val).sum(),
50            Value::Object(o) => o
51                .iter()
52                .map(|(k, v)| STRING_OVERHEAD + k.capacity() + sizeof_val(v) + MAP_ENTRY_OVERHEAD)
53                .sum(),
54        }
55}
56
57#[cfg(test)]
58
59mod tests {
60    use super::*;
61    use serde_json::json;
62
63    #[test]
64    fn test_sizeof_val_null() {
65        let val = json!(null);
66        assert_eq!(sizeof_val(&val), std::mem::size_of::<serde_json::Value>());
67    }
68
69    #[test]
70    fn test_sizeof_val_bool() {
71        let val = json!(true);
72        assert_eq!(sizeof_val(&val), std::mem::size_of::<serde_json::Value>());
73    }
74
75    #[test]
76    fn test_sizeof_val_number() {
77        let val = json!(42);
78        assert_eq!(sizeof_val(&val), std::mem::size_of::<serde_json::Value>());
79    }
80
81    #[test]
82    fn test_sizeof_val_string() {
83        let val = json!("Hello, world!");
84        let expected_size = std::mem::size_of::<serde_json::Value>()
85            + String::from("Hello, world!").capacity()
86            + STRING_OVERHEAD;
87        assert_eq!(sizeof_val(&val), expected_size);
88    }
89
90    #[test]
91    fn test_sizeof_val_array() {
92        let val = json!([1, 2, 3]);
93        let expected_size = std::mem::size_of::<serde_json::Value>()
94            + sizeof_val(&json!(1))
95            + sizeof_val(&json!(2))
96            + sizeof_val(&json!(3));
97        assert_eq!(sizeof_val(&val), expected_size);
98    }
99
100    #[test]
101    fn test_sizeof_val_object() {
102        let val = json!({"key": "value"});
103        let expected_size = std::mem::size_of::<serde_json::Value>()
104            + String::from("key").capacity()
105            + sizeof_val(&json!("value"))
106            + std::mem::size_of::<String>()
107            + std::mem::size_of::<usize>() * 3;
108        assert_eq!(sizeof_val(&val), expected_size);
109    }
110
111    #[test]
112    fn test_sizeof_val_complex_object() {
113        let val = json!({
114            "name": "json_size",
115            "details": {"year": 2022, "version": "v4"}
116        });
117        let expected_size = std::mem::size_of::<serde_json::Value>()
118            + String::from("name").capacity()
119            + sizeof_val(&json!("json_size"))
120            + String::from("details").capacity()
121            + sizeof_val(&json!({"year": 2022, "version": "v4"}))
122            + std::mem::size_of::<String>() * 2
123            + std::mem::size_of::<usize>() * 6; // Assuming each object entry overhead is 3 usize
124        assert_eq!(sizeof_val(&val), expected_size);
125    }
126}