use std::io;
use crate::{shallow_value::ShallowValue, Encoder};
use super::{
types::{Offset, Tag, VariantIdx},
Decoder, Immediate, Result,
};
#[derive(Debug, Clone, PartialEq, PartialOrd)]
pub enum Value {
Null,
Bool(bool),
Int64(i64),
Float(f64),
String(String),
Bytes(Vec<u8>),
Variant0(VariantIdx),
Ref(Offset),
Pointer(Offset),
Tag(Tag, Box<Value>),
Array(Vec<Value>),
Map(Vec<(Value, Value)>),
Variant(VariantIdx, Vec<Value>),
}
impl Default for Value {
fn default() -> Self {
Value::Null
}
}
impl<'a> From<Immediate<'a>> for Value {
fn from(v: Immediate<'a>) -> Self {
match v {
Immediate::Null => Value::Null,
Immediate::Bool(b) => Value::Bool(b),
Immediate::Int64(i) => Value::Int64(i),
Immediate::Float(f) => Value::Float(f),
Immediate::String(s) => Value::String(s.to_string()),
Immediate::Bytes(bs) => Value::Bytes(bs.to_vec()),
Immediate::Variant0(variant_idx) => Value::Variant0(variant_idx),
Immediate::Ref(p) => Value::Ref(p),
Immediate::Pointer(p) => Value::Pointer(p),
}
}
}
pub fn read_value(d: &Decoder, off: Offset) -> Result<Value> {
let v: Value = match d.get_shallow_value(off)? {
ShallowValue::Imm(v) => Value::from(v),
ShallowValue::Tag(tag, off) => Value::Tag(tag, Box::new(read_value(d, off)?)),
ShallowValue::Array(arr) => {
let mut arr_v = Vec::with_capacity(arr.len());
for off in arr {
let off = off?;
arr_v.push(read_value(d, off)?)
}
Value::Array(arr_v)
}
ShallowValue::Map(map) => {
let mut map_v = Vec::with_capacity(map.len());
for kv in map {
let (k, v) = kv?;
map_v.push((read_value(d, k)?, read_value(d, v)?))
}
Value::Map(map_v)
}
ShallowValue::Variant(variant_idx, args) => {
let mut args_v = Vec::with_capacity(args.len());
for a in args {
let a = a?;
args_v.push(read_value(d, a)?)
}
Value::Variant(variant_idx, args_v)
}
};
Ok(v)
}
pub fn read_value_from_entrypoint(d: &Decoder) -> Result<Value> {
let off = d.entrypoint()?;
read_value(d, off)
}
fn write_value_or_imm<'a, W: io::Write>(
enc: &'_ mut Encoder<W>,
v: &'a Value,
) -> io::Result<Immediate<'a>> {
let imm = match v {
Value::Null => Immediate::Null,
Value::Bool(b) => Immediate::Bool(*b),
Value::Int64(i) => Immediate::Int64(*i),
Value::Float(f) => Immediate::Float(*f),
Value::String(s) => Immediate::String(&s),
Value::Bytes(vec) => Immediate::Bytes(&vec),
Value::Variant0(variant_idx) => Immediate::Variant0(*variant_idx),
Value::Ref(p) => Immediate::Ref(*p),
Value::Pointer(p) => Immediate::Pointer(*p),
Value::Tag(tag, v) => {
let v = write_value_or_imm(enc, v)?;
enc.write_tag(*tag, v)?.into()
}
Value::Array(arr) => {
let mut res = Vec::with_capacity(arr.len());
for x in arr {
res.push(write_value_or_imm(enc, x)?);
}
enc.write_array(&res)?.into()
}
Value::Map(map) => {
let mut res = Vec::with_capacity(map.len());
for (k, v) in map {
let k = write_value_or_imm(enc, k)?;
let v = write_value_or_imm(enc, v)?;
res.push((k, v));
}
enc.write_map(&res)?.into()
}
Value::Variant(variant_idx, args) => {
let mut args_res = Vec::with_capacity(args.len());
for x in args {
args_res.push(write_value_or_imm(enc, x)?);
}
enc.write_variant(*variant_idx, &args_res)?.into()
}
};
Ok(imm)
}
pub fn write_value<'a, W: io::Write>(enc: &mut Encoder<W>, v: &Value) -> io::Result<Offset> {
let imm = write_value_or_imm(enc, v)?;
enc.write_immediate_or_return_pointer(imm)
}
#[cfg(test)]
mod tests {
use super::*;
use proptest::prelude::*;
fn arb_values() -> impl Strategy<Value = Value> {
let leaf = prop_oneof![
Just(Value::Null),
any::<bool>().prop_map(|b| Value::Bool(b)),
any::<i64>().prop_map(|b| Value::Int64(b)),
any::<f64>().prop_map(|b| Value::Float(b)),
".*".prop_map(|s| Value::String(s)),
prop::collection::vec(any::<u8>(), 0..100).prop_map(|v| Value::Bytes(v)),
];
leaf.prop_recursive(8, 384, 100, |inner| {
prop_oneof![
prop::collection::vec(inner.clone(), 0..129).prop_map(|v| Value::Array(v)),
prop::collection::vec((inner.clone(), inner.clone()), 0..129)
.prop_map(|map| Value::Map(map)),
(any::<u64>(), inner.clone()).prop_map(|(tag, sub)| Value::Tag(tag, Box::new(sub))),
(any::<u32>(), prop::collection::vec(inner.clone(), 0..6))
.prop_map(|(c, args)| Value::Variant(VariantIdx(c), args)),
]
})
.boxed()
}
proptest! {
#[test]
fn encode_then_decode(v in arb_values()) {
let mut res = vec![];
let mut enc= crate::Encoder::new(&mut res);
let offset = write_value(&mut enc, &v).unwrap();
let v2 = read_value(&Decoder::new(&res[..]).unwrap(), offset).unwrap();
assert_eq!(v, v2);
}
}
}