twine_data/
value.rs

1//! Full, nested values, with individual allocations.
2//!
3//! Such values are useful for various tools that need a global view of the
4//! data, or for manipulating twine data as if it were JSON.
5
6use std::io;
7
8use crate::{shallow_value::ShallowValue, Encoder};
9
10use super::{
11    types::{Offset, Tag, VariantIdx},
12    Decoder, Immediate, Result,
13};
14
15/// A value, potentially containing other values. All the sub-values live in the same allocator.
16#[derive(Debug, Clone, PartialEq, PartialOrd)]
17pub enum Value {
18    Null,
19    Bool(bool),
20    Int64(i64),
21    Float(f64),
22    /// Text, in UTF8
23    String(String),
24    /// Binary blob.
25    Bytes(Vec<u8>),
26    /// A variant with 0 arguments.
27    Variant0(VariantIdx),
28    /// A reference to a full value (which comes at an earlier offset).
29    Ref(Offset),
30    /// An implicitly followed reference to a full value (which comes at an earlier offset).
31    Pointer(Offset),
32    Tag(Tag, Box<Value>),
33    Array(Vec<Value>),
34    Map(Vec<(Value, Value)>),
35    Variant(VariantIdx, Vec<Value>),
36}
37
38impl Default for Value {
39    fn default() -> Self {
40        Value::Null
41    }
42}
43
44impl<'a> From<Immediate<'a>> for Value {
45    fn from(v: Immediate<'a>) -> Self {
46        match v {
47            Immediate::Null => Value::Null,
48            Immediate::Bool(b) => Value::Bool(b),
49            Immediate::Int64(i) => Value::Int64(i),
50            Immediate::Float(f) => Value::Float(f),
51            Immediate::String(s) => Value::String(s.to_string()),
52            Immediate::Bytes(bs) => Value::Bytes(bs.to_vec()),
53            Immediate::Variant0(variant_idx) => Value::Variant0(variant_idx),
54            Immediate::Ref(p) => Value::Ref(p),
55            Immediate::Pointer(p) => Value::Pointer(p),
56        }
57    }
58}
59
60/// Read a value from a decoder, starting at given offset.
61pub fn read_value(d: &Decoder, off: Offset) -> Result<Value> {
62    let v: Value = match d.get_shallow_value(off)? {
63        ShallowValue::Imm(v) => Value::from(v),
64        ShallowValue::Tag(tag, off) => Value::Tag(tag, Box::new(read_value(d, off)?)),
65        ShallowValue::Array(arr) => {
66            let mut arr_v = Vec::with_capacity(arr.len());
67            for off in arr {
68                let off = off?;
69                arr_v.push(read_value(d, off)?)
70            }
71            Value::Array(arr_v)
72        }
73        ShallowValue::Map(map) => {
74            let mut map_v = Vec::with_capacity(map.len());
75            for kv in map {
76                let (k, v) = kv?;
77                map_v.push((read_value(d, k)?, read_value(d, v)?))
78            }
79            Value::Map(map_v)
80        }
81        ShallowValue::Variant(variant_idx, args) => {
82            let mut args_v = Vec::with_capacity(args.len());
83            for a in args {
84                let a = a?;
85                args_v.push(read_value(d, a)?)
86            }
87            Value::Variant(variant_idx, args_v)
88        }
89    };
90    Ok(v)
91}
92
93/// Find the entrypoint and read a value from it.
94pub fn read_value_from_entrypoint(d: &Decoder) -> Result<Value> {
95    let off = d.entrypoint()?;
96    read_value(d, off)
97}
98
99fn write_value_or_imm<'a, W: io::Write>(
100    enc: &'_ mut Encoder<W>,
101    v: &'a Value,
102) -> io::Result<Immediate<'a>> {
103    let imm = match v {
104        Value::Null => Immediate::Null,
105        Value::Bool(b) => Immediate::Bool(*b),
106        Value::Int64(i) => Immediate::Int64(*i),
107        Value::Float(f) => Immediate::Float(*f),
108        Value::String(s) => Immediate::String(&s),
109        Value::Bytes(vec) => Immediate::Bytes(&vec),
110        Value::Variant0(variant_idx) => Immediate::Variant0(*variant_idx),
111        Value::Ref(p) => Immediate::Ref(*p),
112        Value::Pointer(p) => Immediate::Pointer(*p),
113        Value::Tag(tag, v) => {
114            let v = write_value_or_imm(enc, v)?;
115            enc.write_tag(*tag, v)?.into()
116        }
117        Value::Array(arr) => {
118            // locally gather immediates
119            let mut res = Vec::with_capacity(arr.len());
120            for x in arr {
121                res.push(write_value_or_imm(enc, x)?);
122            }
123            enc.write_array(&res)?.into()
124        }
125        Value::Map(map) => {
126            // locally gather immediates
127            let mut res = Vec::with_capacity(map.len());
128            for (k, v) in map {
129                let k = write_value_or_imm(enc, k)?;
130                let v = write_value_or_imm(enc, v)?;
131                res.push((k, v));
132            }
133            enc.write_map(&res)?.into()
134        }
135        Value::Variant(variant_idx, args) => {
136            let mut args_res = Vec::with_capacity(args.len());
137            for x in args {
138                args_res.push(write_value_or_imm(enc, x)?);
139            }
140            enc.write_variant(*variant_idx, &args_res)?.into()
141        }
142    };
143    Ok(imm)
144}
145
146/// Write a value, return an offset to it.
147pub fn write_value<'a, W: io::Write>(enc: &mut Encoder<W>, v: &Value) -> io::Result<Offset> {
148    let imm = write_value_or_imm(enc, v)?;
149    enc.write_immediate_or_return_pointer(imm)
150}
151
152#[cfg(test)]
153mod tests {
154    use super::*;
155    use proptest::prelude::*;
156
157    fn arb_values() -> impl Strategy<Value = Value> {
158        // https://proptest-rs.github.io/proptest/proptest/tutorial/recursive.html heck yeah
159        let leaf = prop_oneof![
160            Just(Value::Null),
161            any::<bool>().prop_map(|b| Value::Bool(b)),
162            any::<i64>().prop_map(|b| Value::Int64(b)),
163            any::<f64>().prop_map(|b| Value::Float(b)),
164            ".*".prop_map(|s| Value::String(s)),
165            prop::collection::vec(any::<u8>(), 0..100).prop_map(|v| Value::Bytes(v)),
166        ];
167        leaf.prop_recursive(8, 384, 100, |inner| {
168            prop_oneof![
169                prop::collection::vec(inner.clone(), 0..129).prop_map(|v| Value::Array(v)),
170                prop::collection::vec((inner.clone(), inner.clone()), 0..129)
171                    .prop_map(|map| Value::Map(map)),
172                (any::<u64>(), inner.clone()).prop_map(|(tag, sub)| Value::Tag(tag, Box::new(sub))),
173                (any::<u32>(), prop::collection::vec(inner.clone(), 0..6))
174                    .prop_map(|(c, args)| Value::Variant(VariantIdx(c), args)),
175            ]
176        })
177        .boxed()
178    }
179
180    proptest! {
181        #[test]
182        fn encode_then_decode(v in arb_values()) {
183            let mut res = vec![];
184            let mut enc= crate::Encoder::new(&mut res);
185            let offset = write_value(&mut enc, &v).unwrap();
186
187            let v2 = read_value(&Decoder::new(&res[..]).unwrap(), offset).unwrap();
188            assert_eq!(v, v2);
189        }
190    }
191}