use std::mem::discriminant;
use crate::{
Key, Value,
encoding::{
decode_arr, decode_bint, decode_bool, decode_dur, decode_f32, decode_f64, decode_i8,
decode_i16, decode_i32, decode_i64, decode_inst, decode_instN, decode_map, decode_str,
decode_u8, decode_u16, decode_u32, decode_u64, decode_uuid, decode_vint, decode_vuint,
encode_arr, encode_bint, encode_bool, encode_dur, encode_f64, encode_instN, encode_map,
encode_str, encode_uuid, encode_vint, encode_vuint,
},
};
macro_rules! cast_typeid {
[$($name:ident) +] => {
$( pub const $name: u8 = crate::builtins::$name as u8; )*
};
}
cast_typeid![
ANY_TYPEID BOOL_TYPEID
U8_TYPEID U16_TYPEID U32_TYPEID U64_TYPEID
I8_TYPEID I16_TYPEID I32_TYPEID I64_TYPEID
F32_TYPEID F64_TYPEID
VUINT_TYPEID VINT_TYPEID BINT_TYPEID
STR_TYPEID ARR_TYPEID MAP_TYPEID
UUID_TYPEID INST_TYPEID INSTN_TYPEID DUR_TYPEID
];
macro_rules! encode_typeid_commons {
($enum:ident, $value:ident, $data:ident) => {
match $value {
$enum::Bool(_) => $data.push(BOOL_TYPEID),
$enum::Uint(_) => $data.push(VUINT_TYPEID),
$enum::Int(_) => $data.push(VINT_TYPEID),
$enum::BigInt(_) => $data.push(BINT_TYPEID),
$enum::Str(_) => $data.push(STR_TYPEID),
$enum::Inst(_) => $data.push(INSTN_TYPEID),
$enum::Dur(_) => $data.push(DUR_TYPEID),
$enum::UUID(_) => $data.push(UUID_TYPEID),
#[allow(unused)]
_ => (),
}
};
}
macro_rules! encode_value_commons {
($enum:ident, $value:ident, $data:ident) => {
match $value {
$enum::Bool(b) => encode_bool($data, *b),
$enum::Uint(nb) => encode_vuint($data, *nb),
$enum::Int(nb) => encode_vint($data, *nb),
$enum::BigInt(nb) => encode_bint($data, nb),
$enum::Str(str) => encode_str($data, str),
$enum::Inst(inst) => encode_instN($data, inst),
$enum::Dur(dur) => encode_dur($data, dur),
$enum::UUID(uuid) => encode_uuid($data, uuid),
#[allow(unused)]
_ => (),
}
};
}
fn enocde_value_typeid(data: &mut Vec<u8>, value: &Value) {
encode_typeid_commons!(Value, value, data);
if value.is_float() {
data.push(F64_TYPEID);
}
}
fn encode_value(data: &mut Vec<u8>, value: &Value) {
encode_value_commons!(Value, value, data);
if let Value::Float(nb) = value {
encode_f64(data, *nb);
}
}
pub fn encode_any_key(data: &mut Vec<u8>, value: &Key) {
encode_typeid_commons!(Key, value, data);
encode_value_commons!(Key, value, data);
}
pub fn encode_any(data: &mut Vec<u8>, value: &Value) {
encode_typeid_commons!(Value, value, data);
encode_value_commons!(Value, value, data);
match value {
Value::Float(nb) => {
data.push(F64_TYPEID);
encode_f64(data, *nb);
}
Value::Arr(arr) => {
data.push(ARR_TYPEID);
if let Some(first) = arr.first()
&& arr.iter().all(|v| discriminant(v) == discriminant(first))
&& !(first.is_array() || first.is_map())
{
enocde_value_typeid(data, first);
encode_arr(data, arr, false, encode_value);
} else {
data.push(ANY_TYPEID);
encode_arr(data, arr, false, encode_any);
}
}
Value::Map(map) => {
data.push(MAP_TYPEID);
let key_encoder: fn(&mut Vec<u8>, &Key) = if let Some(first) = map.keys().next()
&& map.keys().all(|key| discriminant(key) == discriminant(first))
{
encode_typeid_commons!(Key, first, data);
|data, key| encode_value_commons!(Key, key, data)
} else {
data.push(ANY_TYPEID);
encode_any_key
};
let value_encoder = if let Some(first) = map.values().next()
&& map.values().all(|value| discriminant(value) == discriminant(first))
&& !(first.is_array() || first.is_map())
{
enocde_value_typeid(data, first);
encode_value
} else {
data.push(ANY_TYPEID);
encode_any
};
encode_map(data, map, false, key_encoder, value_encoder);
}
_ => (),
}
}
macro_rules! decode_value_commons {
($enum:ident, $id:ident, $data:ident, $ind:ident, $any_decoder:ident) => {
match $id {
ANY_TYPEID => $any_decoder($data, $ind),
BOOL_TYPEID => Some($enum::Bool(decode_bool($data, $ind)?)),
U8_TYPEID => Some($enum::Uint(decode_u8($data, $ind)? as u64)),
U16_TYPEID => Some($enum::Uint(decode_u16($data, $ind)? as u64)),
U32_TYPEID => Some($enum::Uint(decode_u32($data, $ind)? as u64)),
U64_TYPEID => Some($enum::Uint(decode_u64($data, $ind)?)),
I8_TYPEID => Some($enum::Int(decode_i8($data, $ind)? as i64)),
I16_TYPEID => Some($enum::Int(decode_i16($data, $ind)? as i64)),
I32_TYPEID => Some($enum::Int(decode_i32($data, $ind)? as i64)),
I64_TYPEID => Some($enum::Int(decode_i64($data, $ind)?)),
VUINT_TYPEID => Some($enum::Uint(decode_vuint($data, $ind)?)),
VINT_TYPEID => Some($enum::Int(decode_vint($data, $ind)?)),
BINT_TYPEID => Some($enum::BigInt(decode_bint($data, $ind)?)),
STR_TYPEID => Some($enum::Str(decode_str($data, $ind)?)),
UUID_TYPEID => Some($enum::UUID(decode_uuid($data, $ind)?)),
INST_TYPEID => Some($enum::Inst(decode_inst($data, $ind)?)),
INSTN_TYPEID => Some($enum::Inst(decode_instN($data, $ind)?)),
DUR_TYPEID => Some($enum::Dur(decode_dur($data, $ind)?)),
_ => None,
}
};
}
pub fn decode_any(data: &[u8], ind: &mut usize) -> Option<Value> {
let typeid = decode_u8(data, ind)?;
decode_value(data, ind, typeid)
}
pub fn decode_any_key(data: &[u8], ind: &mut usize) -> Option<Key> {
let typeid = decode_u8(data, ind)?;
decode_value_commons!(Key, typeid, data, ind, decode_any_key)
}
pub fn decode_key(data: &[u8], ind: &mut usize, id: u8) -> Option<Key> {
decode_value_commons!(Key, id, data, ind, decode_any_key)
}
pub fn decode_value(data: &[u8], ind: &mut usize, id: u8) -> Option<Value> {
match id {
F32_TYPEID => Some(Value::Float(decode_f32(data, ind)? as f64)),
F64_TYPEID => Some(Value::Float(decode_f64(data, ind)?)),
ARR_TYPEID => {
let itemid = decode_u8(data, ind)?;
Some(Value::Arr(decode_arr(data, ind, false, |data, ind| {
decode_value(data, ind, itemid)
})?))
}
MAP_TYPEID => {
let keyid = decode_u8(data, ind)?;
let valueid = decode_u8(data, ind)?;
Some(Value::Map(Box::new(decode_map(
data,
ind,
false,
|data, ind| decode_key(data, ind, keyid),
|data, ind| decode_value(data, ind, valueid),
)?)))
}
_ => decode_value_commons!(Value, id, data, ind, decode_any),
}
}