use armour_core::{Fields, ScalarTyp, Typ};
use crate::codec::InspectionEncoding;
pub(crate) fn typ_fixed_width(typ: &Typ) -> Option<usize> {
match typ {
Typ::Scalar(scalar) => match scalar {
ScalarTyp::Void => Some(0),
ScalarTyp::Bool | ScalarTyp::U8 => Some(1),
ScalarTyp::U16 => Some(2),
ScalarTyp::U32 | ScalarTyp::I32 | ScalarTyp::F32 | ScalarTyp::Id32 => Some(4),
ScalarTyp::U64
| ScalarTyp::U64BE
| ScalarTyp::TimestampBE
| ScalarTyp::I64
| ScalarTyp::F64
| ScalarTyp::Datetime
| ScalarTyp::Timestamp
| ScalarTyp::Id64
| ScalarTyp::Fuid => Some(8),
ScalarTyp::Decimal => Some(16),
ScalarTyp::ArrayBytes(n) => usize::try_from(*n).ok(),
ScalarTyp::LowId
| ScalarTyp::Str
| ScalarTyp::Bytes
| ScalarTyp::RustJson
| ScalarTyp::JsonBytes => None,
},
Typ::Array(n, inner) => {
let inner = typ_fixed_width(inner)?;
let inner_u32 = u32::try_from(inner).ok()?;
n.checked_mul(inner_u32)
.and_then(|width| usize::try_from(width).ok())
}
Typ::Struct(s) => {
let mut total = 0usize;
match s.fields {
Fields::Named(fields) => {
for (_, field_ty) in fields {
total = total.checked_add(typ_fixed_width(field_ty)?)?;
}
}
Fields::Unnamed(fields) => {
for field_ty in fields {
total = total.checked_add(typ_fixed_width(field_ty)?)?;
}
}
}
Some(total)
}
Typ::Vec(_) | Typ::Optional(_) | Typ::SimpleEnum(_) | Typ::Enum(_) | Typ::Custom(_, _) => {
None
}
}
}
pub(crate) fn zero_inspection_encoding(ty: &Typ, value_width: usize) -> InspectionEncoding {
if cfg!(target_endian = "little") && typ_fixed_width(ty) == Some(value_width) {
InspectionEncoding::Rapira
} else {
InspectionEncoding::RawLayout
}
}
#[cfg(test)]
mod tests {
use armour_core::{EnumType, Fields, ScalarTyp, SimpleEnumType, StructType, Typ};
use crate::codec::InspectionEncoding;
use super::{typ_fixed_width, zero_inspection_encoding};
#[test]
fn scalar_widths() {
assert_eq!(typ_fixed_width(&Typ::Scalar(ScalarTyp::Void)), Some(0));
assert_eq!(typ_fixed_width(&Typ::Scalar(ScalarTyp::Bool)), Some(1));
assert_eq!(typ_fixed_width(&Typ::Scalar(ScalarTyp::U8)), Some(1));
assert_eq!(typ_fixed_width(&Typ::Scalar(ScalarTyp::U16)), Some(2));
assert_eq!(typ_fixed_width(&Typ::Scalar(ScalarTyp::U32)), Some(4));
assert_eq!(typ_fixed_width(&Typ::Scalar(ScalarTyp::I32)), Some(4));
assert_eq!(typ_fixed_width(&Typ::Scalar(ScalarTyp::F32)), Some(4));
assert_eq!(typ_fixed_width(&Typ::Scalar(ScalarTyp::Id32)), Some(4));
assert_eq!(typ_fixed_width(&Typ::Scalar(ScalarTyp::U64)), Some(8));
assert_eq!(typ_fixed_width(&Typ::Scalar(ScalarTyp::I64)), Some(8));
assert_eq!(typ_fixed_width(&Typ::Scalar(ScalarTyp::F64)), Some(8));
assert_eq!(typ_fixed_width(&Typ::Scalar(ScalarTyp::Datetime)), Some(8));
assert_eq!(typ_fixed_width(&Typ::Scalar(ScalarTyp::Timestamp)), Some(8));
assert_eq!(typ_fixed_width(&Typ::Scalar(ScalarTyp::Id64)), Some(8));
assert_eq!(typ_fixed_width(&Typ::Scalar(ScalarTyp::Fuid)), Some(8));
assert_eq!(typ_fixed_width(&Typ::Scalar(ScalarTyp::U64BE)), Some(8));
assert_eq!(
typ_fixed_width(&Typ::Scalar(ScalarTyp::TimestampBE)),
Some(8)
);
assert_eq!(typ_fixed_width(&Typ::Scalar(ScalarTyp::Decimal)), Some(16));
assert_eq!(
typ_fixed_width(&Typ::Scalar(ScalarTyp::ArrayBytes(7))),
Some(7)
);
}
#[test]
fn low_id_and_variable_shapes_are_none() {
assert_eq!(typ_fixed_width(&Typ::Scalar(ScalarTyp::LowId)), None);
assert_eq!(typ_fixed_width(&Typ::Scalar(ScalarTyp::Str)), None);
assert_eq!(typ_fixed_width(&Typ::Scalar(ScalarTyp::Bytes)), None);
assert_eq!(typ_fixed_width(&Typ::Scalar(ScalarTyp::RustJson)), None);
assert_eq!(typ_fixed_width(&Typ::Scalar(ScalarTyp::JsonBytes)), None);
assert_eq!(
typ_fixed_width(&Typ::Vec(&Typ::Scalar(ScalarTyp::U8))),
None
);
assert_eq!(
typ_fixed_width(&Typ::Optional(&Typ::Scalar(ScalarTyp::U8))),
None
);
assert_eq!(
typ_fixed_width(&Typ::SimpleEnum(SimpleEnumType {
name: "E",
variants: &[(0, "A")],
})),
None
);
assert_eq!(
typ_fixed_width(&Typ::Enum(EnumType {
name: "E",
variants: &[(0, ("A", Typ::Scalar(ScalarTyp::U8)))],
})),
None
);
assert_eq!(typ_fixed_width(&Typ::Custom("X", &[])), None);
}
#[test]
fn nested_array_and_struct() {
const INNER: Typ = Typ::Scalar(ScalarTyp::U16);
assert_eq!(typ_fixed_width(&Typ::Array(3, &INNER)), Some(6));
assert_eq!(
typ_fixed_width(&Typ::Struct(StructType {
name: "S",
fields: Fields::Named(&[
("a", Typ::Scalar(ScalarTyp::Id32)),
("b", Typ::Scalar(ScalarTyp::U64)),
]),
})),
Some(12)
);
assert_eq!(
typ_fixed_width(&Typ::Struct(StructType {
name: "T",
fields: Fields::Unnamed(&[Typ::Scalar(ScalarTyp::U8), Typ::Array(2, &INNER),]),
})),
Some(5)
);
}
#[test]
fn unsupported_nested_and_overflow_return_none() {
assert_eq!(
typ_fixed_width(&Typ::Array(2, &Typ::Scalar(ScalarTyp::Str))),
None
);
assert_eq!(
typ_fixed_width(&Typ::Struct(StructType {
name: "Bad",
fields: Fields::Named(&[("x", Typ::Scalar(ScalarTyp::LowId))]),
})),
None
);
assert_eq!(
typ_fixed_width(&Typ::Array(u32::MAX, &Typ::Scalar(ScalarTyp::U64))),
None
);
}
#[test]
fn zero_encoding_rapira_when_width_matches_on_little_endian() {
let ty = Typ::Scalar(ScalarTyp::U32);
let encoding = zero_inspection_encoding(&ty, 4);
if cfg!(target_endian = "little") {
assert_eq!(encoding, InspectionEncoding::Rapira);
} else {
assert_eq!(encoding, InspectionEncoding::RawLayout);
}
}
#[test]
fn zero_encoding_raw_layout_on_width_mismatch() {
let ty = Typ::Scalar(ScalarTyp::U32); assert_eq!(
zero_inspection_encoding(&ty, 8),
InspectionEncoding::RawLayout
);
}
}