Skip to main content

dataflow_rs/base/
context.rs

1//! The context, `{data, metadata, temp_data}`, on v3's wire shape, and the
2//! reader every value of a wire shape is read back with.
3//!
4//! The context's reads and writes over a [`Path`](crate::expr::Path) live in
5//! `run`, beside the message, since `expr` sits above `base`.
6
7use datavalue::{NumberValue, OwnedDataValue};
8use serde::de::{self, MapAccess, SeqAccess, Visitor};
9use serde::{Deserialize, Deserializer, Serialize, Serializer};
10use std::collections::HashSet;
11use std::fmt;
12
13/// The three roots, in the order a context holds them.
14pub(crate) const ROOTS: [&str; 3] = ["data", "metadata", "temp_data"];
15
16/// The context: always `data`, `metadata` and `temp_data`, in that order.
17/// Each root starts as an object, and every write keeps it one: `map`,
18/// `Ctx::set` and [`Context::set`] refuse anything else at a root.
19///
20/// Read from JSON, it takes the three roots in any order and writes them in
21/// that order, reads a missing one as `{}`, takes any value at each (a
22/// trace's redacted root is `null`), and refuses any other key,
23/// since expressions evaluate against the whole object and a fourth key
24/// would be readable by `{"var": …}`. An object anywhere in it holding a key
25/// twice is refused too: the engine reads the first, where a JSON parser
26/// keeps the last, so the two would disagree on what the context says.
27#[derive(Clone, Debug, PartialEq)]
28pub struct Context {
29    pub(crate) root: OwnedDataValue,
30}
31
32/// A seed for a root: an object as it is, anything else `{}` with capacity
33/// `cap`.
34fn slot(seed: OwnedDataValue, cap: usize) -> OwnedDataValue {
35    if seed.is_object() {
36        seed
37    } else {
38        OwnedDataValue::Object(Vec::with_capacity(cap))
39    }
40}
41
42impl Context {
43    /// A context seeded with `data` and `metadata`, each ignored when it is
44    /// not an object, and an empty `temp_data`. `metadata` is sized for the
45    /// engine's run stamps and progress object so the first run does not
46    /// reallocate it.
47    pub(crate) fn new(data: OwnedDataValue, metadata: OwnedDataValue) -> Self {
48        Self::from_roots([
49            slot(data, 0),
50            slot(metadata, 4),
51            OwnedDataValue::Object(Vec::new()),
52        ])
53    }
54
55    /// A context over `root`, the object holding the three roots: a trace's
56    /// `initial` and each context its replay rebuilds.
57    pub(crate) fn from_root(root: OwnedDataValue) -> Self {
58        Self { root }
59    }
60
61    /// A context over the three roots' values, in [`ROOTS`]' order. A
62    /// literal, not an iterator over `ROOTS`: every message is built through
63    /// it, and the zipped iterator cost about 0.5% of `micro_cond_bench`'s
64    /// instructions.
65    fn from_roots([data, metadata, temp_data]: [OwnedDataValue; 3]) -> Self {
66        let [d, m, t] = ROOTS;
67        Self::from_root(OwnedDataValue::Object(vec![
68            (d.to_owned(), data),
69            (m.to_owned(), metadata),
70            (t.to_owned(), temp_data),
71        ]))
72    }
73
74    fn root(&self, key: &str) -> &OwnedDataValue {
75        &self.root[key]
76    }
77
78    /// The `data` root.
79    pub fn data(&self) -> &OwnedDataValue {
80        self.root("data")
81    }
82
83    /// The `metadata` root.
84    pub fn metadata(&self) -> &OwnedDataValue {
85        self.root("metadata")
86    }
87
88    /// The `temp_data` root.
89    pub fn temp_data(&self) -> &OwnedDataValue {
90        self.root("temp_data")
91    }
92
93    /// The object holding the three roots: what expressions evaluate against.
94    pub fn as_value(&self) -> &OwnedDataValue {
95        &self.root
96    }
97
98    /// Takes the `data` root, leaving an empty object where it was so the
99    /// context keeps its three roots and still serializes as one. `metadata`
100    /// and `temp_data` are untouched.
101    ///
102    /// The tree is moved, not cloned: its buffers, key order and numeric
103    /// representation are the ones it had, so a completed child run's result
104    /// can cross into its parent without a deep copy. A second take returns
105    /// the empty object the first left.
106    ///
107    /// A root that is not an object (a trace's redacted `null`) is returned
108    /// as it is and replaced with `{}`, as every root starts as an object.
109    #[must_use = "the data root is taken and left empty"]
110    pub fn take_data(&mut self) -> OwnedDataValue {
111        let OwnedDataValue::Object(entries) = &mut self.root else {
112            return OwnedDataValue::Object(Vec::new());
113        };
114        let Some((_, data)) = entries.iter_mut().find(|(key, _)| key.as_str() == "data") else {
115            return OwnedDataValue::Object(Vec::new());
116        };
117        std::mem::replace(data, OwnedDataValue::Object(Vec::new()))
118    }
119}
120
121impl Serialize for Context {
122    fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
123        self.root.serialize(s)
124    }
125}
126
127impl<'de> Deserialize<'de> for Context {
128    fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
129        d.deserialize_map(Roots)
130    }
131}
132
133#[cfg(feature = "ts")]
134crate::base::ts::ts_decl!(
135    Context,
136    "Context",
137    [
138        " The context: `data`, `metadata` and `temp_data`, in that order. Each",
139        " root starts as an object; a trace's redacted root is `null`.",
140    ],
141    |_cfg| {
142        use crate::base::ts::{Prop, object};
143        object(&[
144            Prop::new("data", "JsonValue", &[" The `data` root."]),
145            Prop::new("metadata", "JsonValue", &[" The `metadata` root."]),
146            Prop::new("temp_data", "JsonValue", &[" The `temp_data` root."]),
147        ])
148    }
149);
150
151/// [`Context`]'s reader: the three roots, by name, once each.
152struct Roots;
153
154impl<'de> Visitor<'de> for Roots {
155    type Value = Context;
156
157    fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
158        f.write_str("a context: an object holding `data`, `metadata` and `temp_data`")
159    }
160
161    fn visit_map<A: MapAccess<'de>>(self, mut map: A) -> Result<Context, A::Error> {
162        let mut values: [Option<OwnedDataValue>; 3] = [None, None, None];
163        while let Some(key) = map.next_key::<String>()? {
164            let Some(at) = ROOTS.iter().position(|root| *root == key) else {
165                return Err(de::Error::custom(format_args!(
166                    "a context holds only `data`, `metadata` and `temp_data`, not `{key}`"
167                )));
168            };
169            if values[at].is_some() {
170                return Err(de::Error::duplicate_field(ROOTS[at]));
171            }
172            values[at] = Some(map.next_value::<ReadValue<false>>()?.0);
173        }
174        Ok(Context::from_roots(values.map(|value| {
175            value.unwrap_or_else(|| OwnedDataValue::Object(Vec::new()))
176        })))
177    }
178}
179
180/// A value read back as v4 wrote it: as [`OwnedDataValue`]'s own reader
181/// reads it, except that a number written with a fraction or an exponent
182/// stays a float, as `OwnedDataValue::from(&serde_json::Value)` keeps one,
183/// and an object holding a key twice is refused. That reader makes `2.0` the
184/// integer `2`, which writes back as `2`, so a message holding a
185/// whole-number float would not read back byte for byte; and it keeps both
186/// pairs of a repeated key, which every path rule reads as one.
187pub(crate) fn exact<'de, D: Deserializer<'de>>(d: D) -> Result<OwnedDataValue, D::Error> {
188    d.deserialize_any(ValueVisitor::<false>)
189}
190
191/// A value read in document order, folding any repeated object key to its
192/// first position and last value (as `serde_json::Value` with `preserve_order`
193/// reads one).
194pub(crate) fn in_order<'de, D: Deserializer<'de>>(d: D) -> Result<OwnedDataValue, D::Error> {
195    d.deserialize_any(ValueVisitor::<true>)
196}
197
198/// A value read by [`ValueVisitor`].
199struct ReadValue<const DEDUP: bool>(OwnedDataValue);
200
201impl<'de, const DEDUP: bool> Deserialize<'de> for ReadValue<DEDUP> {
202    fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
203        d.deserialize_any(ValueVisitor::<DEDUP>).map(Self)
204    }
205}
206
207/// [`exact`]'s and [`in_order`]'s reader: `OwnedDataValue`'s, keeping floats
208/// as floats and either refusing (`DEDUP = false`) or folding (`DEDUP = true`)
209/// repeated object keys.
210struct ValueVisitor<const DEDUP: bool>;
211
212impl<'de, const DEDUP: bool> Visitor<'de> for ValueVisitor<DEDUP> {
213    type Value = OwnedDataValue;
214
215    fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
216        f.write_str("any valid JSON value")
217    }
218
219    fn visit_unit<E: de::Error>(self) -> Result<OwnedDataValue, E> {
220        Ok(OwnedDataValue::Null)
221    }
222
223    fn visit_none<E: de::Error>(self) -> Result<OwnedDataValue, E> {
224        Ok(OwnedDataValue::Null)
225    }
226
227    fn visit_some<D: Deserializer<'de>>(self, d: D) -> Result<OwnedDataValue, D::Error> {
228        d.deserialize_any(Self)
229    }
230
231    fn visit_bool<E: de::Error>(self, v: bool) -> Result<OwnedDataValue, E> {
232        Ok(OwnedDataValue::Bool(v))
233    }
234
235    fn visit_i64<E: de::Error>(self, v: i64) -> Result<OwnedDataValue, E> {
236        Ok(OwnedDataValue::Number(NumberValue::Integer(v)))
237    }
238
239    fn visit_u64<E: de::Error>(self, v: u64) -> Result<OwnedDataValue, E> {
240        Ok(OwnedDataValue::Number(NumberValue::from_u64(v)))
241    }
242
243    fn visit_i128<E: de::Error>(self, v: i128) -> Result<OwnedDataValue, E> {
244        Ok(OwnedDataValue::Number(match i64::try_from(v) {
245            Ok(i) => NumberValue::Integer(i),
246            Err(_) => NumberValue::Float(v as f64),
247        }))
248    }
249
250    fn visit_u128<E: de::Error>(self, v: u128) -> Result<OwnedDataValue, E> {
251        Ok(OwnedDataValue::Number(match i64::try_from(v) {
252            Ok(i) => NumberValue::Integer(i),
253            Err(_) => NumberValue::Float(v as f64),
254        }))
255    }
256
257    fn visit_f64<E: de::Error>(self, v: f64) -> Result<OwnedDataValue, E> {
258        Ok(if v.is_finite() {
259            OwnedDataValue::Number(NumberValue::Float(v))
260        } else {
261            OwnedDataValue::Null
262        })
263    }
264
265    fn visit_str<E: de::Error>(self, v: &str) -> Result<OwnedDataValue, E> {
266        Ok(OwnedDataValue::String(v.to_owned()))
267    }
268
269    fn visit_string<E: de::Error>(self, v: String) -> Result<OwnedDataValue, E> {
270        Ok(OwnedDataValue::String(v))
271    }
272
273    fn visit_seq<A: SeqAccess<'de>>(self, mut seq: A) -> Result<OwnedDataValue, A::Error> {
274        let mut items = Vec::with_capacity(seq.size_hint().unwrap_or(0));
275        while let Some(ReadValue(item)) = seq.next_element::<ReadValue<DEDUP>>()? {
276            items.push(item);
277        }
278        Ok(OwnedDataValue::Array(items))
279    }
280
281    fn visit_map<A: MapAccess<'de>>(self, mut map: A) -> Result<OwnedDataValue, A::Error> {
282        let mut pairs = Vec::with_capacity(map.size_hint().unwrap_or(0));
283        while let Some((key, ReadValue(value))) = map.next_entry::<String, ReadValue<DEDUP>>()? {
284            pairs.push((key, value));
285        }
286        if DEDUP {
287            dedup_object(&mut pairs);
288        } else if let Some(key) = repeated_key(&pairs) {
289            return Err(de::Error::custom(format_args!(
290                "an object holds the key `{key}` twice"
291            )));
292        }
293        Ok(OwnedDataValue::Object(pairs))
294    }
295}
296
297/// Above this many keys, an object finds a repeated key through a set
298/// rather than a scan.
299const SCAN: usize = 8;
300
301/// The first key `pairs` holds a second time, if any.
302fn repeated_key(pairs: &[(String, OwnedDataValue)]) -> Option<&str> {
303    if pairs.len() <= SCAN {
304        return pairs
305            .iter()
306            .enumerate()
307            .find(|(i, (key, _))| pairs[..*i].iter().any(|(k, _)| k == key))
308            .map(|(_, (key, _))| key.as_str());
309    }
310    let mut seen = HashSet::with_capacity(pairs.len());
311    pairs
312        .iter()
313        .map(|(key, _)| key.as_str())
314        .find(|key| !seen.insert(*key))
315}
316
317/// Ensures `pairs` holds each key once: a repeated key keeps its first
318/// position and its last value, as `serde_json::Value` reads one.
319fn dedup_object(pairs: &mut Vec<(String, OwnedDataValue)>) {
320    if repeated_key(pairs).is_some() {
321        fold_repeats(pairs);
322    }
323}
324
325/// `value` with every object in it holding each key once: a repeated key
326/// keeps its first position and its last value, as `serde_json::Value`
327/// reads one.
328pub(crate) fn keep_last(value: &mut OwnedDataValue) {
329    match value {
330        OwnedDataValue::Object(pairs) => {
331            dedup_object(pairs);
332            pairs.iter_mut().for_each(|(_, v)| keep_last(v));
333        }
334        OwnedDataValue::Array(items) => items.iter_mut().for_each(keep_last),
335        _ => {}
336    }
337}
338
339/// [`keep_last`] on one object known to hold a key twice.
340#[cold]
341fn fold_repeats(pairs: &mut Vec<(String, OwnedDataValue)>) {
342    let mut len = 0;
343    for i in 0..pairs.len() {
344        let (key, value) = std::mem::take(&mut pairs[i]);
345        if let Some((_, slot)) = pairs[..len].iter_mut().find(|(k, _)| *k == key) {
346            *slot = value;
347        } else {
348            pairs[len] = (key, value);
349            len += 1;
350        }
351    }
352    pairs.truncate(len);
353}
354
355#[cfg(test)]
356mod tests {
357    use super::*;
358    use serde_json::json;
359
360    fn text(c: &Context) -> String {
361        serde_json::to_string(c).unwrap()
362    }
363
364    #[test]
365    fn a_seed_that_is_not_an_object_is_ignored() {
366        let c = Context::new(
367            OwnedDataValue::from(&json!([1])),
368            OwnedDataValue::from(&json!({"m": 1})),
369        );
370        assert_eq!(text(&c), r#"{"data":{},"metadata":{"m":1},"temp_data":{}}"#);
371    }
372
373    #[test]
374    fn a_context_reads_what_it_writes() {
375        let wire = r#"{"data":{"b":1,"a":[null]},"metadata":null,"temp_data":"t"}"#;
376        let c: Context = serde_json::from_str(wire).unwrap();
377        assert_eq!(text(&c), wire);
378        assert_eq!(c.metadata(), &OwnedDataValue::Null);
379        assert_eq!(
380            c,
381            Context::from_root(OwnedDataValue::from(&json!({
382                "data": {"b": 1, "a": [null]}, "metadata": null, "temp_data": "t"
383            })))
384        );
385    }
386
387    #[test]
388    fn a_value_reads_back_as_written_and_as_the_json_bridge_builds_it() {
389        let wire =
390            r#"[2.0,-3.0,2.5,1e+300,7,-7,9223372036854775807,"s",true,null,{"b":[1.0],"a":{}}]"#;
391        let mut de = serde_json::Deserializer::from_str(wire);
392        let read = exact(&mut de).unwrap();
393        assert_eq!(serde_json::to_string(&read).unwrap(), wire);
394        let bridged: serde_json::Value = serde_json::from_str(wire).unwrap();
395        assert_eq!(read, OwnedDataValue::from(&bridged));
396        // `OwnedDataValue`'s own reader makes the whole floats integers.
397        let collapsed: OwnedDataValue = serde_json::from_str(wire).unwrap();
398        assert!(
399            serde_json::to_string(&collapsed)
400                .unwrap()
401                .starts_with("[2,-3,2.5,")
402        );
403        // Above `i64::MAX`, an integer is a float, as the bridge makes it.
404        let big = exact(&mut serde_json::Deserializer::from_str(
405            "18446744073709551615",
406        ))
407        .unwrap();
408        assert_eq!(big, OwnedDataValue::from(&json!(u64::MAX)));
409    }
410    #[test]
411    fn a_repeated_key_keeps_its_first_position_and_its_last_value() {
412        let fold = |text: &str| {
413            let mut value = OwnedDataValue::from_json(text).unwrap();
414            keep_last(&mut value);
415            serde_json::to_string(&value).unwrap()
416        };
417        assert_eq!(
418            fold(r#"{"a":1,"b":2,"a":3,"c":4,"b":5}"#),
419            r#"{"a":3,"b":5,"c":4}"#
420        );
421        // Inside arrays and objects, and inside a repeated key's last value.
422        assert_eq!(
423            fold(r#"[{"x":{"y":1,"y":2}},{"a":{"q":1},"a":{"q":2,"q":3}}]"#),
424            r#"[{"x":{"y":2}},{"a":{"q":3}}]"#
425        );
426        // Past the scan, through the set.
427        let wide: String = (0..12).map(|i| format!(r#""k{i}":{i},"#)).collect();
428        let wanted: String = (0..12)
429            .map(|i| format!(r#""k{i}":{},"#, if i == 3 { 99 } else { i }))
430            .collect();
431        assert_eq!(
432            fold(&format!(r#"{{{wide}"k3":99}}"#)),
433            format!("{{{}}}", wanted.trim_end_matches(','))
434        );
435        // Nothing repeated, nothing moves.
436        let plain = r#"{"b":1,"a":{"b":2},"c":[{"b":3}]}"#;
437        assert_eq!(fold(plain), plain);
438    }
439
440    /// The buffer of `value`'s `a.b` array, when it has one.
441    fn array_buffer(value: &OwnedDataValue) -> *const OwnedDataValue {
442        let OwnedDataValue::Object(entries) = value else {
443            return std::ptr::null();
444        };
445        let Some((_, OwnedDataValue::Object(inner))) = entries.iter().find(|(key, _)| key == "a")
446        else {
447            return std::ptr::null();
448        };
449        let Some((_, OwnedDataValue::Array(items))) = inner.iter().find(|(key, _)| key == "b")
450        else {
451            return std::ptr::null();
452        };
453        items.as_ptr()
454    }
455
456    #[test]
457    fn taking_the_data_root_moves_the_tree_and_leaves_an_object() {
458        let data = json!({"a": {"b": [1, {"c": 2.0}]}, "big": 12345678901234i64});
459        let mut c = Context::from_root(OwnedDataValue::from(&json!({
460            "data": data, "metadata": {"m": 1}, "temp_data": {"t": true}
461        })));
462        let buffer = array_buffer(c.data());
463        assert!(!buffer.is_null());
464        let taken = c.take_data();
465        // The value is the one that was there, its buffer included: the tree
466        // moved, and nothing was copied.
467        assert_eq!(taken, OwnedDataValue::from(&data));
468        assert_eq!(array_buffer(&taken), buffer);
469        assert_eq!(
470            serde_json::to_string(&taken).unwrap(),
471            r#"{"a":{"b":[1,{"c":2.0}]},"big":12345678901234}"#
472        );
473        // The context keeps its three roots, `data` as an object, and the
474        // other two as they were.
475        assert_eq!(
476            text(&c),
477            r#"{"data":{},"metadata":{"m":1},"temp_data":{"t":true}}"#
478        );
479    }
480
481    #[test]
482    fn a_second_take_returns_the_empty_object_the_first_left() {
483        let mut c = Context::new(
484            OwnedDataValue::from(&json!({"a": 1})),
485            OwnedDataValue::from(&json!({"m": 1})),
486        );
487        assert_eq!(c.take_data(), OwnedDataValue::from(&json!({"a": 1})));
488        assert_eq!(c.take_data(), OwnedDataValue::Object(Vec::new()));
489        assert_eq!(text(&c), r#"{"data":{},"metadata":{"m":1},"temp_data":{}}"#);
490    }
491
492    #[test]
493    fn an_empty_data_root_is_taken_as_an_empty_object() {
494        let mut c = Context::new(OwnedDataValue::Object(Vec::new()), OwnedDataValue::Null);
495        assert_eq!(c.take_data(), OwnedDataValue::Object(Vec::new()));
496        assert_eq!(text(&c), r#"{"data":{},"metadata":{},"temp_data":{}}"#);
497    }
498
499    #[test]
500    fn a_redacted_root_is_taken_as_it_is_and_replaced_with_an_object() {
501        let mut c = Context::from_root(OwnedDataValue::from(&json!({
502            "data": null, "metadata": null, "temp_data": null
503        })));
504        assert_eq!(c.take_data(), OwnedDataValue::Null);
505        assert_eq!(text(&c), r#"{"data":{},"metadata":null,"temp_data":null}"#);
506    }
507}