use super::flatbuffers::{Packages, ProjectionError};
use crate::v2;
pub const PREAMBLE_SIZE: u32 = 8;
pub const HEAD_ALIGN: u32 = 8;
pub const DESCRIPTOR_SIZE: u32 = 8;
pub const DESCRIPTOR_ALIGN: u32 = 4;
pub const MAX_COUNT: u64 = 65_535;
pub const MAX_ELEM_SIZE: u64 = 65_535;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Head {
pub size: u32,
pub fields: Vec<Placement>,
pub pads: Vec<Pad>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Placement {
pub source: Source,
pub offset: u32,
pub size: u32,
pub align: u32,
pub form: Form,
pub optional: Option<FlagBit>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Source {
Field { name: String, ordinal: u32 },
Flags { index: u32 },
Tag,
Arm,
Position { position: u32, name: String },
Key,
Value,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct FlagBit {
pub flags: u32,
pub bit: u8,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Form {
Bool,
Int {
width: v2::IntWidth,
signed: bool,
},
Float(v2::FloatWidth),
Enum {
width: v2::IntWidth,
signed: bool,
reference: String,
},
EnumSet {
width: v2::IntWidth,
reference: String,
},
Inline {
head: Box<Head>,
of: Inlined,
},
InlineArray {
element: Box<Form>,
count: u32,
},
Descriptor(DescriptorKind),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Inlined {
Declaration { package: String, name: String },
Tuple,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum DescriptorKind {
Text { min: u64, max: u64 },
Bytes { min: u64, max: u64 },
Array {
element: Box<Form>,
elem_size: u32,
min: u64,
max: u64,
},
Map {
entry: Box<Head>,
elem_size: u32,
min: u64,
max: u64,
},
Arm { arms: Vec<(u32, Head)> },
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Pad {
pub offset: u32,
pub size: u32,
}
#[must_use]
pub fn place(items: &[(u32, u32)]) -> Vec<u64> {
let mut placed: Vec<(u64, u64)> = Vec::with_capacity(items.len());
let mut offsets = Vec::with_capacity(items.len());
for &(size, align) in items {
let (size, align) = (u64::from(size), u64::from(align));
let mut off = 0u64;
loop {
off = off.div_ceil(align) * align;
match placed.iter().find(|&&(s, e)| off < e && s < off + size) {
None => break,
Some(&(_, end)) => off = end,
}
}
placed.push((off, off + size));
offsets.push(off);
}
offsets
}
pub fn struct_head(
packages: Packages<'_>,
owner: &str,
def: &v2::StructDef,
) -> Result<Head, ProjectionError> {
Layout::new(packages).struct_head(packages.package, owner, def)
}
pub fn tuple_head(
packages: Packages<'_>,
name: &str,
tuple: &v2::TupleType,
) -> Result<Head, ProjectionError> {
Layout::new(packages).tuple_head(packages.package, name, tuple)
}
pub fn union_head(
packages: Packages<'_>,
owner: &str,
def: &v2::UnionDef,
) -> Result<Head, ProjectionError> {
Layout::new(packages).union_head(packages.package, owner, def)
}
#[must_use]
pub fn union_tag_width(def: &v2::UnionDef) -> v2::IntWidth {
let largest = def.arms.iter().map(|arm| arm.ordinal).max().unwrap_or(0);
if u8::try_from(largest).is_ok() {
v2::IntWidth::U8
} else if u16::try_from(largest).is_ok() {
v2::IntWidth::U16
} else {
v2::IntWidth::U32
}
}
#[must_use]
pub fn enum_width(def: &v2::EnumDef) -> (v2::IntWidth, bool) {
let min = def
.values
.iter()
.map(|value| value.value)
.min()
.unwrap_or(0);
let max = def
.values
.iter()
.map(|value| value.value)
.max()
.unwrap_or(0);
if min < 0 {
let width = if i8::try_from(min).is_ok() && i8::try_from(max).is_ok() {
v2::IntWidth::I8
} else if i16::try_from(min).is_ok() && i16::try_from(max).is_ok() {
v2::IntWidth::I16
} else if i32::try_from(min).is_ok() && i32::try_from(max).is_ok() {
v2::IntWidth::I32
} else {
v2::IntWidth::I64
};
(width, true)
} else {
let width = if u8::try_from(max).is_ok() {
v2::IntWidth::U8
} else if u16::try_from(max).is_ok() {
v2::IntWidth::U16
} else if u32::try_from(max).is_ok() {
v2::IntWidth::U32
} else {
v2::IntWidth::U64
};
(width, false)
}
}
pub fn field_form(packages: Packages<'_>, ty: &v2::FieldType) -> Result<Form, ProjectionError> {
Layout::new(packages).form(packages.package, ty)
}
#[must_use]
pub fn shape_holds_inline(
packages: Packages<'_>,
ty: &v2::FieldType,
package: &str,
name: &str,
) -> bool {
shape_holds(packages, ty, package, name, true)
}
#[must_use]
pub fn root_shape_holds_inline(
packages: Packages<'_>,
ty: &v2::FieldType,
package: &str,
name: &str,
) -> bool {
match ty.kind.as_ref() {
Some(v2::field_type::Kind::Array(array)) if array.min == array.max => array
.element
.as_ref()
.is_some_and(|element| root_shape_holds_inline(packages, element, package, name)),
_ => shape_holds(packages, ty, package, name, false),
}
}
fn shape_holds(
packages: Packages<'_>,
ty: &v2::FieldType,
package: &str,
name: &str,
fixed: bool,
) -> bool {
let holds = |ty: &v2::FieldType, fixed: bool| shape_holds(packages, ty, package, name, fixed);
match ty.kind.as_ref() {
Some(v2::field_type::Kind::Named(reference)) => {
fixed
&& packages
.resolve(packages.package, reference)
.is_some_and(|(decl, declaring)| declaring.name == package && decl.name == name)
}
Some(v2::field_type::Kind::Tuple(tuple)) => tuple
.fields
.iter()
.any(|field| field.r#type.as_ref().is_some_and(|ty| holds(ty, true))),
Some(v2::field_type::Kind::Array(array)) => array
.element
.as_ref()
.is_some_and(|element| holds(element, array.min == array.max)),
Some(v2::field_type::Kind::Map(map)) => {
map.key.as_ref().is_some_and(|key| holds(key, false))
|| map.value.as_ref().is_some_and(|value| holds(value, false))
}
Some(
v2::field_type::Kind::Primitive(_)
| v2::field_type::Kind::InlineScalar(_)
| v2::field_type::Kind::Stream(_),
)
| None => false,
}
}
#[must_use]
pub fn elem_size(form: &Form) -> u32 {
match form {
Form::Bool => 1,
Form::Int { width, .. } | Form::Enum { width, .. } | Form::EnumSet { width, .. } => {
int_bytes(*width)
}
Form::Float(width) => float_bytes(*width),
Form::Inline { head, .. } => head.size,
Form::InlineArray { element, count } => elem_size(element).saturating_mul(*count),
Form::Descriptor(_) => DESCRIPTOR_SIZE,
}
}
#[must_use]
pub fn max_size(packages: Packages<'_>, decl: &v2::Decl) -> Option<u64> {
let mut layout = Layout::new(packages);
let home = packages.package;
let head = match &decl.kind {
Some(v2::decl::Kind::StructDef(def)) => layout.struct_head(home, &decl.name, def).ok()?,
Some(v2::decl::Kind::UnionDef(def)) => layout.union_head(home, &decl.name, def).ok()?,
Some(v2::decl::Kind::TypeDef(_))
| Some(v2::decl::Kind::EnumDef(_))
| Some(v2::decl::Kind::EnumSetDef(_)) => {
boxed_head(layout.named_form(home, &decl.name).ok()?).ok()?
}
_ => return None,
};
head_bound(&head)
}
#[must_use]
pub fn boxed_head_of(packages: Packages<'_>, decl: &v2::Decl) -> Option<Head> {
match &decl.kind {
Some(
v2::decl::Kind::TypeDef(_) | v2::decl::Kind::EnumDef(_) | v2::decl::Kind::EnumSetDef(_),
) => {
let mut layout = Layout::new(packages);
boxed_head(layout.named_form(packages.package, &decl.name).ok()?).ok()
}
_ => None,
}
}
#[must_use]
pub fn tuple_max_size(packages: Packages<'_>, tuple: &v2::TupleType) -> Option<u64> {
let head = Layout::new(packages)
.tuple_head(packages.package, "a tuple", tuple)
.ok()?;
head_bound(&head)
}
fn head_bound(head: &Head) -> Option<u64> {
let (tail, _) = head_tail(head, HEAD_ALIGN)?;
let bound = u64::from(PREAMBLE_SIZE)
.checked_add(u64::from(head.size))?
.checked_add(tail)?;
(bound <= u64::from(u32::MAX)).then_some(bound)
}
#[must_use]
pub fn exceeds_descriptor_limits(head: &Head) -> bool {
head.fields
.iter()
.any(|placement| form_exceeds_descriptor_limits(&placement.form))
}
#[must_use]
pub fn form_exceeds_descriptor_limits(form: &Form) -> bool {
match form {
Form::Bool
| Form::Int { .. }
| Form::Float(_)
| Form::Enum { .. }
| Form::EnumSet { .. } => false,
Form::Inline { head, .. } => exceeds_descriptor_limits(head),
Form::InlineArray { element, .. } => form_exceeds_descriptor_limits(element),
Form::Descriptor(kind) => match kind {
DescriptorKind::Text { .. } | DescriptorKind::Bytes { .. } => false,
DescriptorKind::Array {
element,
elem_size,
max,
..
} => {
*max > MAX_COUNT
|| u64::from(*elem_size) > MAX_ELEM_SIZE
|| form_exceeds_descriptor_limits(element)
}
DescriptorKind::Map {
entry,
elem_size,
max,
..
} => {
*max > MAX_COUNT
|| u64::from(*elem_size) > MAX_ELEM_SIZE
|| exceeds_descriptor_limits(entry)
}
DescriptorKind::Arm { arms } => {
arms.iter().any(|(_, head)| exceeds_descriptor_limits(head))
}
},
}
}
fn error(message: String) -> ProjectionError {
ProjectionError { message }
}
struct Layout<'a> {
packages: Packages<'a>,
visiting: Vec<String>,
}
struct Item {
source: Source,
form: Form,
optional: Option<FlagBit>,
}
#[derive(Default)]
struct Members {
items: Vec<Item>,
optionals: u32,
}
impl Members {
fn push(&mut self, source: Source, form: Form, optional: bool) {
let optional = optional.then(|| {
let n = self.optionals;
if n.is_multiple_of(8) {
self.items.push(Item {
source: Source::Flags { index: n / 8 },
form: Form::Int {
width: v2::IntWidth::U8,
signed: false,
},
optional: None,
});
}
self.optionals = n + 1;
FlagBit {
flags: n / 8,
bit: (n % 8) as u8,
}
});
self.items.push(Item {
source,
form,
optional,
});
}
}
fn boxed_head(form: Form) -> Result<Head, ProjectionError> {
lay_out(vec![Item {
source: Source::Arm,
form,
optional: None,
}])
}
fn lay_out(items: Vec<Item>) -> Result<Head, ProjectionError> {
let sizes: Vec<(u32, u32)> = items
.iter()
.map(|item| (elem_size(&item.form), align_of(&item.form)))
.collect();
let offsets = place(&sizes);
let end = offsets
.iter()
.zip(&sizes)
.map(|(offset, &(size, _))| offset + u64::from(size))
.max()
.unwrap_or(0);
let size = end.div_ceil(u64::from(HEAD_ALIGN)) * u64::from(HEAD_ALIGN);
let size = u32::try_from(size).map_err(|_| {
error(format!(
"a head of {size} bytes is larger than a u32 offset can address"
))
})?;
let mut fields: Vec<Placement> = items
.into_iter()
.zip(sizes)
.zip(offsets)
.map(|((item, (size, align)), offset)| {
let offset = u32::try_from(offset)
.map_err(|_| error(format!("offset {offset} does not fit a u32")))?;
Ok(Placement {
source: item.source,
offset,
size,
align,
form: item.form,
optional: item.optional,
})
})
.collect::<Result<_, ProjectionError>>()?;
fields.sort_by_key(|placement| placement.offset);
let mut pads = Vec::new();
let mut cursor = 0u32;
for placement in &fields {
if placement.offset > cursor {
pads.push(Pad {
offset: cursor,
size: placement.offset - cursor,
});
}
cursor = cursor.max(placement.offset + placement.size);
}
if size > cursor {
pads.push(Pad {
offset: cursor,
size: size - cursor,
});
}
Ok(Head { size, fields, pads })
}
impl<'a> Layout<'a> {
fn new(packages: Packages<'a>) -> Self {
Self {
packages,
visiting: Vec::new(),
}
}
fn guarded(
&mut self,
key: String,
body: impl FnOnce(&mut Self) -> Result<Head, ProjectionError>,
) -> Result<Head, ProjectionError> {
if self.visiting.contains(&key) {
return Err(error(format!("{key} reaches itself, so it has no layout")));
}
self.visiting.push(key);
let out = body(self);
self.visiting.pop();
out
}
fn resolve(
&self,
home: &'a v2::Package,
reference: &str,
) -> Result<(&'a v2::Decl, &'a v2::Package), ProjectionError> {
self.packages
.resolve(home, reference)
.ok_or_else(|| error(format!("{reference} does not resolve")))
}
fn struct_head(
&mut self,
home: &'a v2::Package,
owner: &str,
def: &v2::StructDef,
) -> Result<Head, ProjectionError> {
self.guarded(format!("{}.{owner}", home.name), |layout| {
let mut members = Members::default();
for member in &def.members {
match &member.member {
Some(v2::struct_member::Member::Field(field)) => {
let ty = field
.r#type
.as_ref()
.ok_or_else(|| error(format!("{owner}.{} has no type", field.name)))?;
let form = layout.form(home, ty)?;
members.push(
Source::Field {
name: field.name.clone(),
ordinal: field.ordinal,
},
form,
ty.optional,
);
}
Some(v2::struct_member::Member::Reserved(_)) | None => {}
}
}
lay_out(members.items)
})
}
fn tuple_head(
&mut self,
home: &'a v2::Package,
name: &str,
tuple: &v2::TupleType,
) -> Result<Head, ProjectionError> {
let mut members = Members::default();
for (index, field) in tuple.fields.iter().enumerate() {
let position = u32::try_from(index + 1)
.map_err(|_| error(format!("{name} has more fields than u32 positions")))?;
let ty = field
.r#type
.as_ref()
.ok_or_else(|| error(format!("{name}.{} has no type", field.name)))?;
let form = self.form(home, ty)?;
members.push(
Source::Position {
position,
name: field.name.clone(),
},
form,
ty.optional,
);
}
lay_out(members.items)
}
fn union_head(
&mut self,
home: &'a v2::Package,
owner: &str,
def: &v2::UnionDef,
) -> Result<Head, ProjectionError> {
self.guarded(format!("{}.{owner}", home.name), |layout| {
let mut arms = Vec::with_capacity(def.arms.len());
for arm in &def.arms {
arms.push((arm.ordinal, layout.arm_head(home, arm)?));
}
lay_out(vec![
Item {
source: Source::Tag,
form: Form::Int {
width: union_tag_width(def),
signed: false,
},
optional: None,
},
Item {
source: Source::Arm,
form: Form::Descriptor(DescriptorKind::Arm { arms }),
optional: None,
},
])
})
}
fn arm_head(
&mut self,
home: &'a v2::Package,
arm: &v2::UnionArm,
) -> Result<Head, ProjectionError> {
let (decl, declaring) = self.resolve(home, &arm.type_ref)?;
match &decl.kind {
Some(v2::decl::Kind::StructDef(def)) => self.struct_head(declaring, &decl.name, def),
Some(v2::decl::Kind::UnionDef(def)) => self.union_head(declaring, &decl.name, def),
Some(v2::decl::Kind::TypeDef(_))
| Some(v2::decl::Kind::EnumDef(_))
| Some(v2::decl::Kind::EnumSetDef(_)) => {
boxed_head(self.named_form(home, &arm.type_ref)?)
}
_ => Err(error(format!(
"arm {} names {}, which is not a type",
arm.name, arm.type_ref
))),
}
}
fn form(&mut self, home: &'a v2::Package, ty: &v2::FieldType) -> Result<Form, ProjectionError> {
match ty.kind.as_ref() {
None => Err(error("a field with no type has no form".to_string())),
Some(v2::field_type::Kind::Primitive(primitive)) => {
match v2::PrimitiveType::try_from(*primitive) {
Ok(v2::PrimitiveType::Boolean) => Ok(Form::Bool),
Ok(v2::PrimitiveType::Integer) => Ok(Form::Int {
width: v2::IntWidth::I64,
signed: true,
}),
Ok(v2::PrimitiveType::Float) => Ok(Form::Float(v2::FloatWidth::F64)),
_ => Err(error(
"a bare string or bytes, or an unspecified primitive, has no form"
.to_string(),
)),
}
}
Some(v2::field_type::Kind::InlineScalar(def)) => scalar_form(def),
Some(v2::field_type::Kind::Named(reference)) => self.named_form(home, reference),
Some(v2::field_type::Kind::Tuple(tuple)) => Ok(Form::Inline {
head: Box::new(self.tuple_head(home, "a tuple", tuple)?),
of: Inlined::Tuple,
}),
Some(v2::field_type::Kind::Array(array)) => {
let element_ty = array.element.as_ref().ok_or_else(|| {
error("an array with no element type has no form".to_string())
})?;
let element = self.form(home, element_ty)?;
if array.min == array.max {
let count = u32::try_from(array.max).map_err(|_| {
error(format!(
"an array of {} elements is larger than a head",
array.max
))
})?;
Ok(Form::InlineArray {
element: Box::new(element),
count,
})
} else {
let elem_size = elem_size(&element);
Ok(Form::Descriptor(DescriptorKind::Array {
element: Box::new(element),
elem_size,
min: array.min,
max: array.max,
}))
}
}
Some(v2::field_type::Kind::Map(map)) => {
let key_ty = map
.key
.as_ref()
.ok_or_else(|| error("a map with no key type has no form".to_string()))?;
let value_ty = map
.value
.as_ref()
.ok_or_else(|| error("a map with no value type has no form".to_string()))?;
let mut members = Members::default();
let key = self.form(home, key_ty)?;
members.push(Source::Key, key, key_ty.optional);
let value = self.form(home, value_ty)?;
members.push(Source::Value, value, value_ty.optional);
let entry = lay_out(members.items)?;
let elem_size = entry.size;
Ok(Form::Descriptor(DescriptorKind::Map {
entry: Box::new(entry),
elem_size,
min: map.min,
max: map.max,
}))
}
Some(v2::field_type::Kind::Stream(_)) => Err(error("a stream has no form".to_string())),
}
}
fn named_form(
&mut self,
home: &'a v2::Package,
reference: &str,
) -> Result<Form, ProjectionError> {
let (decl, declaring) = self.resolve(home, reference)?;
match &decl.kind {
Some(v2::decl::Kind::TypeDef(def)) => scalar_form(def),
Some(v2::decl::Kind::StructDef(def)) => Ok(Form::Inline {
head: Box::new(self.struct_head(declaring, &decl.name, def)?),
of: Inlined::Declaration {
package: declaring.name.clone(),
name: decl.name.clone(),
},
}),
Some(v2::decl::Kind::UnionDef(def)) => Ok(Form::Inline {
head: Box::new(self.union_head(declaring, &decl.name, def)?),
of: Inlined::Declaration {
package: declaring.name.clone(),
name: decl.name.clone(),
},
}),
Some(v2::decl::Kind::EnumDef(def)) => {
let (width, signed) = enum_width(def);
Ok(Form::Enum {
width,
signed,
reference: reference.to_string(),
})
}
Some(v2::decl::Kind::EnumSetDef(def)) => Ok(Form::EnumSet {
width: int_width(def.width)
.ok_or_else(|| error(format!("{reference} has no width")))?,
reference: reference.to_string(),
}),
_ => Err(error(format!("{reference} is not a type"))),
}
}
}
fn scalar_form(def: &v2::TypeDef) -> Result<Form, ProjectionError> {
match &def.width {
Some(v2::type_def::Width::IntWidth(width)) => {
let width = int_width(*width)
.ok_or_else(|| error("an integer with no width has no form".to_string()))?;
Ok(Form::Int {
width,
signed: matches!(
width,
v2::IntWidth::I8 | v2::IntWidth::I16 | v2::IntWidth::I32 | v2::IntWidth::I64
),
})
}
Some(v2::type_def::Width::FloatWidth(width)) => match v2::FloatWidth::try_from(*width) {
Ok(width @ (v2::FloatWidth::F32 | v2::FloatWidth::F64)) => Ok(Form::Float(width)),
_ => Err(error("a float with no width has no form".to_string())),
},
None => {
let primitive = match def
.backing
.as_ref()
.and_then(|backing| backing.kind.as_ref())
{
Some(v2::backing::Kind::Primitive(primitive)) => {
v2::PrimitiveType::try_from(*primitive).ok()
}
_ => None,
};
match primitive {
Some(v2::PrimitiveType::Boolean) => Ok(Form::Bool),
Some(v2::PrimitiveType::String) => {
let (min, max) = length_bounds(def, "string")?;
Ok(Form::Descriptor(DescriptorKind::Text { min, max }))
}
Some(v2::PrimitiveType::Bytes) => {
let (min, max) = length_bounds(def, "bytes")?;
if min == max {
let count = u32::try_from(max).map_err(|_| {
error(format!("bytes of {max} bytes are larger than a head"))
})?;
Ok(Form::InlineArray {
element: Box::new(Form::Int {
width: v2::IntWidth::U8,
signed: false,
}),
count,
})
} else {
Ok(Form::Descriptor(DescriptorKind::Bytes { min, max }))
}
}
_ => Err(error("a scalar with no width has no form".to_string())),
}
}
}
}
fn length_bounds(def: &v2::TypeDef, what: &str) -> Result<(u64, u64), ProjectionError> {
let constraint = def.constraint.as_ref();
let max = constraint
.and_then(|constraint| constraint.len_max)
.ok_or_else(|| error(format!("a {what} with no length bound has no form")))?;
let min = constraint
.and_then(|constraint| constraint.len_min)
.unwrap_or(0);
Ok((min, max))
}
fn int_width(width: i32) -> Option<v2::IntWidth> {
match v2::IntWidth::try_from(width) {
Ok(v2::IntWidth::Unspecified) | Err(_) => None,
Ok(width) => Some(width),
}
}
fn int_bytes(width: v2::IntWidth) -> u32 {
match width {
v2::IntWidth::U8 | v2::IntWidth::I8 => 1,
v2::IntWidth::U16 | v2::IntWidth::I16 => 2,
v2::IntWidth::U32 | v2::IntWidth::I32 => 4,
v2::IntWidth::U64 | v2::IntWidth::I64 | v2::IntWidth::Unspecified => 8,
}
}
fn float_bytes(width: v2::FloatWidth) -> u32 {
match width {
v2::FloatWidth::F32 => 4,
v2::FloatWidth::F64 | v2::FloatWidth::Unspecified => 8,
}
}
fn align_of(form: &Form) -> u32 {
match form {
Form::Bool
| Form::Int { .. }
| Form::Enum { .. }
| Form::EnumSet { .. }
| Form::Float(_) => elem_size(form),
Form::Inline { .. } => HEAD_ALIGN,
Form::InlineArray { element, .. } => align_of(element),
Form::Descriptor(_) => DESCRIPTOR_ALIGN,
}
}
fn head_tail(head: &Head, aligned: u32) -> Option<(u64, u32)> {
let mut bytes = 0u64;
let mut aligned = aligned;
for placement in &head.fields {
let (more, after) = form_tail(&placement.form, aligned)?;
bytes = bytes.checked_add(more)?;
aligned = after;
}
Some((bytes, aligned))
}
fn form_tail(form: &Form, aligned: u32) -> Option<(u64, u32)> {
match form {
Form::Bool
| Form::Int { .. }
| Form::Float(_)
| Form::Enum { .. }
| Form::EnumSet { .. } => Some((0, aligned)),
Form::Inline { head, .. } => head_tail(head, aligned),
Form::InlineArray { element, count } => repeated(u64::from(*count), aligned, |aligned| {
form_tail(element, aligned)
}),
Form::Descriptor(kind) => match kind {
DescriptorKind::Text { max, .. } => {
let bytes = if *max == 0 {
0
} else {
max.checked_mul(4)?.checked_add(1)?
};
block(aligned, 1, bytes, 1)
}
DescriptorKind::Bytes { max, .. } => block(aligned, 1, *max, 1),
DescriptorKind::Array {
element,
elem_size,
max,
..
} => {
if *max > MAX_COUNT || u64::from(*elem_size) > MAX_ELEM_SIZE {
return None;
}
let (bytes, aligned) = block(
aligned,
align_of(element),
max.checked_mul(u64::from(*elem_size))?,
*elem_size,
)?;
let (more, aligned) =
repeated(*max, aligned, |aligned| form_tail(element, aligned))?;
Some((bytes.checked_add(more)?, aligned))
}
DescriptorKind::Map {
entry,
elem_size,
max,
..
} => {
if *max > MAX_COUNT || u64::from(*elem_size) > MAX_ELEM_SIZE {
return None;
}
let (bytes, aligned) = block(
aligned,
HEAD_ALIGN,
max.checked_mul(u64::from(*elem_size))?,
*elem_size,
)?;
let (more, aligned) = repeated(*max, aligned, |aligned| head_tail(entry, aligned))?;
Some((bytes.checked_add(more)?, aligned))
}
DescriptorKind::Arm { arms } => {
if arms.is_empty() {
return Some((0, aligned));
}
let mut worst = 0u64;
let mut after = 0u32;
for (_, head) in arms {
let (bytes, arm_aligned) =
block(aligned, HEAD_ALIGN, u64::from(head.size), head.size)?;
let (more, arm_aligned) = head_tail(head, arm_aligned)?;
worst = worst.max(bytes.checked_add(more)?);
after = gcd(after, arm_aligned);
}
Some((worst, after))
}
},
}
}
fn block(aligned: u32, align: u32, bytes: u64, granularity: u32) -> Option<(u64, u32)> {
if bytes == 0 {
return Some((0, aligned));
}
let gap = align.saturating_sub(aligned);
let at = align.max(aligned);
Some((u64::from(gap).checked_add(bytes)?, gcd(at, granularity)))
}
fn repeated(
count: u64,
aligned: u32,
tail: impl Fn(u32) -> Option<(u64, u32)>,
) -> Option<(u64, u32)> {
if count == 0 {
return Some((0, aligned));
}
let (bytes, after) = tail(1)?;
if bytes == 0 {
return Some((0, aligned));
}
Some((bytes.checked_mul(count)?, after))
}
fn gcd(a: u32, b: u32) -> u32 {
if b == 0 { a } else { gcd(b, a % b) }
}
#[cfg(test)]
mod tests {
use super::*;
fn field(name: &str, ordinal: u32, ty: v2::FieldType) -> v2::StructMember {
v2::StructMember {
member: Some(v2::struct_member::Member::Field(Box::new(v2::Field {
name: name.to_string(),
ordinal,
r#type: Some(ty),
..Default::default()
}))),
}
}
fn retired(ordinal: u32) -> v2::StructMember {
v2::StructMember {
member: Some(v2::struct_member::Member::Reserved(v2::Reserved {
ordinal,
..Default::default()
})),
}
}
fn optional(mut ty: v2::FieldType) -> v2::FieldType {
ty.optional = true;
ty
}
fn scalar(def: v2::TypeDef) -> v2::FieldType {
v2::FieldType {
optional: false,
kind: Some(v2::field_type::Kind::InlineScalar(Box::new(def))),
}
}
fn backing(primitive: v2::PrimitiveType) -> Option<v2::Backing> {
Some(v2::Backing {
kind: Some(v2::backing::Kind::Primitive(primitive as i32)),
})
}
fn int(width: v2::IntWidth) -> v2::FieldType {
scalar(v2::TypeDef {
backing: backing(v2::PrimitiveType::Integer),
width: Some(v2::type_def::Width::IntWidth(width as i32)),
..Default::default()
})
}
fn byte() -> v2::FieldType {
int(v2::IntWidth::U8)
}
fn boolean() -> v2::FieldType {
v2::FieldType {
optional: false,
kind: Some(v2::field_type::Kind::Primitive(
v2::PrimitiveType::Boolean as i32,
)),
}
}
fn string(max: u64) -> v2::FieldType {
scalar(v2::TypeDef {
backing: backing(v2::PrimitiveType::String),
constraint: Some(v2::Constraint {
len_min: Some(0),
len_max: Some(max),
..Default::default()
}),
..Default::default()
})
}
fn bytes(min: u64, max: u64) -> v2::FieldType {
scalar(v2::TypeDef {
backing: backing(v2::PrimitiveType::Bytes),
constraint: Some(v2::Constraint {
len_min: Some(min),
len_max: Some(max),
..Default::default()
}),
..Default::default()
})
}
fn named(reference: &str) -> v2::FieldType {
v2::FieldType {
optional: false,
kind: Some(v2::field_type::Kind::Named(reference.to_string())),
}
}
fn array(element: v2::FieldType, min: u64, max: u64) -> v2::FieldType {
v2::FieldType {
optional: false,
kind: Some(v2::field_type::Kind::Array(Box::new(v2::ArrayType {
element: Some(Box::new(element)),
min,
max,
}))),
}
}
fn map(key: v2::FieldType, value: v2::FieldType, min: u64, max: u64) -> v2::FieldType {
v2::FieldType {
optional: false,
kind: Some(v2::field_type::Kind::Map(Box::new(v2::MapType {
key: Some(Box::new(key)),
value: Some(Box::new(value)),
min,
max,
}))),
}
}
fn tuple(fields: Vec<(&str, v2::FieldType)>) -> v2::FieldType {
v2::FieldType {
optional: false,
kind: Some(v2::field_type::Kind::Tuple(v2::TupleType {
fields: fields
.into_iter()
.map(|(name, ty)| v2::TupleField {
name: name.to_string(),
r#type: Some(ty),
})
.collect(),
})),
}
}
fn struct_decl(name: &str, members: Vec<v2::StructMember>) -> v2::Decl {
v2::Decl {
name: name.to_string(),
kind: Some(v2::decl::Kind::StructDef(v2::StructDef {
members,
..Default::default()
})),
..Default::default()
}
}
fn arm(name: &str, ordinal: u32, type_ref: &str) -> v2::UnionArm {
v2::UnionArm {
name: name.to_string(),
ordinal,
type_ref: type_ref.to_string(),
..Default::default()
}
}
fn union_decl(name: &str, arms: Vec<v2::UnionArm>) -> v2::Decl {
v2::Decl {
name: name.to_string(),
kind: Some(v2::decl::Kind::UnionDef(v2::UnionDef {
arms,
..Default::default()
})),
..Default::default()
}
}
fn values(values: &[i64]) -> v2::EnumDef {
v2::EnumDef {
values: values
.iter()
.map(|value| v2::EnumValue {
name: format!("V{value}"),
value: *value,
..Default::default()
})
.collect(),
..Default::default()
}
}
struct Fixture {
package: v2::Package,
subject: &'static str,
}
fn fixture(subject: &'static str, decls: Vec<v2::Decl>) -> Fixture {
Fixture {
package: v2::Package {
name: "veh".to_string(),
decls,
..Default::default()
},
subject,
}
}
fn subject(fixture: &Fixture) -> &v2::Decl {
fixture
.package
.decls
.iter()
.find(|decl| decl.name == fixture.subject)
.expect("the fixture declares its subject")
}
fn packages(fixture: &Fixture) -> Packages<'_> {
Packages {
package: &fixture.package,
others: &[],
}
}
fn head(fixture: Fixture) -> Head {
let decl = subject(&fixture);
let Some(v2::decl::Kind::StructDef(def)) = &decl.kind else {
panic!("{} is a struct", fixture.subject);
};
struct_head(packages(&fixture), &decl.name, def).expect("layout")
}
fn head_of_union(fixture: Fixture) -> Head {
let decl = subject(&fixture);
let Some(v2::decl::Kind::UnionDef(def)) = &decl.kind else {
panic!("{} is a union", fixture.subject);
};
union_head(packages(&fixture), &decl.name, def).expect("layout")
}
fn max_size_of(fixture: Fixture) -> Option<u64> {
max_size(packages(&fixture), subject(&fixture))
}
fn offsets(head: &Head) -> Vec<u32> {
head.fields
.iter()
.map(|placement| placement.offset)
.collect()
}
fn pads(head: &Head) -> Vec<(u32, u32)> {
head.pads.iter().map(|pad| (pad.offset, pad.size)).collect()
}
fn flag_offsets(head: &Head) -> Vec<u32> {
head.fields
.iter()
.filter(|placement| matches!(placement.source, Source::Flags { .. }))
.map(|placement| placement.offset)
.collect()
}
fn is_field(placement: &Placement, field: &str) -> bool {
matches!(&placement.source, Source::Field { name, .. } if name == field)
}
fn text(min: u64, max: u64) -> DescriptorKind {
DescriptorKind::Text { min, max }
}
fn door_payload() -> Fixture {
fixture(
"DoorPayload",
vec![struct_decl(
"DoorPayload",
vec![
field("sensor_id", 1, byte()),
field("is_open", 2, boolean()),
],
)],
)
}
fn climate_report() -> Fixture {
fixture(
"ClimateReport",
vec![struct_decl(
"ClimateReport",
vec![
field("cabin", 1, int(v2::IntWidth::I16)),
field("setpoint", 2, int(v2::IntWidth::I16)),
field("fan", 3, byte()),
],
)],
)
}
fn diag_filter() -> Fixture {
fixture(
"DiagFilter",
vec![struct_decl(
"DiagFilter",
vec![
field("severity", 1, byte()),
field("category", 2, optional(string(256))),
],
)],
)
}
fn fault_event_decl() -> v2::Decl {
struct_decl(
"FaultEvent",
vec![
field("code", 1, int(v2::IntWidth::U16)),
field("message", 2, string(256)),
field("timestamp", 3, int(v2::IntWidth::I64)),
],
)
}
fn fault_event() -> Fixture {
fixture("FaultEvent", vec![fault_event_decl()])
}
fn fault_page() -> Fixture {
fixture(
"FaultPage",
vec![
fault_event_decl(),
struct_decl(
"FaultPage",
vec![field("faults", 1, array(named("FaultEvent"), 0, 64))],
),
],
)
}
fn nine_optionals() -> Fixture {
let members = (1..=9)
.map(|n| field(&format!("f{n}"), n, optional(byte())))
.collect();
fixture("Nine", vec![struct_decl("Nine", members)])
}
fn sensor_result() -> Fixture {
fixture(
"SensorResult",
vec![
struct_decl(
"SensorReading",
vec![field("value", 1, int(v2::IntWidth::I16))],
),
struct_decl(
"SensorFault",
vec![field("code", 1, int(v2::IntWidth::U16))],
),
union_decl(
"SensorResult",
vec![arm("ok", 1, "SensorReading"), arm("err", 2, "SensorFault")],
),
],
)
}
fn array_of(max: u64, element: v2::FieldType) -> Fixture {
fixture(
"Holder",
vec![struct_decl(
"Holder",
vec![field("items", 1, array(element, 0, max))],
)],
)
}
fn struct_of_size(size: u64) -> v2::FieldType {
tuple(vec![("raw", array(byte(), size, size))])
}
fn map_of(max: u64) -> Fixture {
fixture(
"Holder",
vec![struct_decl(
"Holder",
vec![field("items", 1, map(byte(), byte(), 0, max))],
)],
)
}
#[test]
fn place_follows_the_first_fit_law() {
assert_eq!(place(&[(2, 2), (8, 4), (8, 8)]), vec![0, 4, 16]);
assert_eq!(place(&[(1, 1), (1, 1), (8, 4)]), vec![0, 1, 4]);
assert_eq!(place(&[(1, 1), (8, 8), (2, 2)]), vec![0, 8, 2]);
}
#[test]
fn the_worked_examples_of_the_design_lay_out_as_measured() {
assert_eq!(head(door_payload()).size, 8);
assert_eq!(head(climate_report()).size, 8);
let filter = head(diag_filter());
assert_eq!(filter.size, 16);
assert_eq!(offsets(&filter), vec![0, 1, 4]);
assert_eq!(pads(&filter), vec![(2, 2), (12, 4)]);
let event = head(fault_event());
assert_eq!(event.size, 24);
assert_eq!(offsets(&event), vec![0, 4, 16]);
assert_eq!(pads(&event), vec![(2, 2), (12, 4)]);
let page = head(fault_page());
assert_eq!(page.size, 8);
assert!(matches!(
page.fields[0].form,
Form::Descriptor(DescriptorKind::Array {
elem_size: 24,
min: 0,
max: 64,
..
})
));
}
#[test]
fn flags_are_placed_before_optional_field_8k_plus_1() {
let h = head(nine_optionals());
assert_eq!(h.size, 16);
assert_eq!(flag_offsets(&h), vec![0, 9]);
let f9 = h.fields.iter().find(|p| is_field(p, "f9")).unwrap();
assert_eq!(f9.offset, 10);
assert_eq!(f9.optional, Some(FlagBit { flags: 1, bit: 0 }));
let f8 = h.fields.iter().find(|p| is_field(p, "f8")).unwrap();
assert_eq!(f8.optional, Some(FlagBit { flags: 0, bit: 7 }));
}
#[test]
fn an_enum_takes_the_narrowest_width_and_the_signedness_of_its_values() {
assert_eq!(
enum_width(&values(&[0, 1, 2, 3])),
(v2::IntWidth::U8, false)
);
assert_eq!(enum_width(&values(&[-1, 200])), (v2::IntWidth::I16, true));
assert_eq!(enum_width(&values(&[70_000])), (v2::IntWidth::U32, false));
assert_eq!(enum_width(&values(&[])), (v2::IntWidth::U8, false));
}
#[test]
fn elem_size_is_a_multiple_of_8_for_a_struct_and_exact_otherwise() {
assert_eq!(
elem_size(&Form::Inline {
head: Box::new(head(fault_event())),
of: Inlined::Tuple,
}),
24
);
assert_eq!(
elem_size(&Form::Int {
width: v2::IntWidth::U16,
signed: false
}),
2
);
assert_eq!(elem_size(&Form::Descriptor(text(0, 16))), 8);
}
#[test]
fn a_union_head_is_a_tag_and_an_arm_descriptor() {
let h = head_of_union(sensor_result());
assert_eq!(h.size, 16);
assert_eq!(offsets(&h), vec![0, 4]);
assert_eq!(h.fields[0].source, Source::Tag);
assert!(matches!(
h.fields[0].form,
Form::Int {
width: v2::IntWidth::U8,
signed: false
}
));
let Form::Descriptor(DescriptorKind::Arm { arms }) = &h.fields[1].form else {
panic!("the second field is the arm descriptor");
};
assert_eq!(
arms.iter().map(|(ordinal, _)| *ordinal).collect::<Vec<_>>(),
vec![1, 2]
);
assert!(arms.iter().all(|(_, arm)| arm.size == 8));
}
#[test]
fn the_descriptor_count_limit_is_inclusive_at_65535() {
assert!(!exceeds_descriptor_limits(&head(array_of(65_535, byte()))));
assert!(exceeds_descriptor_limits(&head(array_of(65_536, byte()))));
assert!(!exceeds_descriptor_limits(&head(map_of(65_535))));
assert!(exceeds_descriptor_limits(&head(map_of(65_536))));
assert_eq!(max_size_of(map_of(65_535)), Some(8 + 8 + 65_535 * 8));
assert_eq!(max_size_of(map_of(65_536)), None);
}
#[test]
fn the_alignment_after_a_union_is_the_gcd_over_its_arms() {
for arms in [
vec![arm("text", 1, "Text"), arm("plain", 2, "Plain")],
vec![arm("plain", 1, "Plain"), arm("text", 2, "Text")],
] {
let holder = fixture(
"Holder",
vec![
struct_decl("Text", vec![field("s", 1, string(1))]),
struct_decl("Plain", vec![field("n", 1, byte())]),
union_decl("Either", arms),
struct_decl(
"Holder",
vec![
field("e", 1, named("Either")),
field("xs", 2, array(int(v2::IntWidth::U64), 0, 1)),
],
),
],
);
assert_eq!(max_size_of(holder), Some(8 + 24 + 13 + 7 + 8));
}
}
#[test]
fn max_size_sums_preamble_head_and_largest_tail() {
assert_eq!(max_size_of(fault_page()), Some(8 + 8 + 64 * 24 + 64 * 1025));
assert_eq!(max_size_of(array_of(65_536, byte())), None);
assert_eq!(max_size_of(array_of(65_535, byte())), Some(8 + 8 + 65_535));
assert_eq!(max_size_of(array_of(1, struct_of_size(65_536))), None);
assert_eq!(
max_size_of(array_of(1, struct_of_size(65_528))),
Some(8 + 8 + 65_528)
);
}
#[test]
fn max_size_counts_the_alignment_gap_before_an_aligned_tail_block() {
let gapped = fixture(
"Gapped",
vec![struct_decl(
"Gapped",
vec![
field("name", 1, string(16)),
field("stamps", 2, array(int(v2::IntWidth::I64), 0, 4)),
],
)],
);
assert_eq!(max_size_of(gapped), Some(8 + 16 + (4 * 16 + 1) + 7 + 4 * 8));
let ordered = fixture(
"Ordered",
vec![struct_decl(
"Ordered",
vec![
field("stamps", 1, array(int(v2::IntWidth::I64), 0, 4)),
field("name", 2, string(16)),
],
)],
);
assert_eq!(max_size_of(ordered), Some(8 + 16 + 4 * 8 + (4 * 16 + 1)));
}
#[test]
fn every_array_element_pays_its_tail_at_the_worst_alignment() {
let recs = fixture(
"Recs",
vec![
struct_decl(
"Rec",
vec![
field("stamps", 1, array(int(v2::IntWidth::I64), 0, 1)),
field("name", 2, string(1)),
],
),
struct_decl("Recs", vec![field("items", 1, array(named("Rec"), 0, 2))]),
],
);
assert_eq!(max_size_of(recs), Some(88));
}
#[test]
fn a_union_pays_its_largest_arm_whatever_the_arm_order() {
let either = |first: (&str, u32, &str), second: (&str, u32, &str)| {
fixture(
"Either",
vec![
struct_decl("Big", vec![field("s", 1, string(100))]),
struct_decl("Small", vec![field("s", 1, string(1))]),
union_decl(
"Either",
vec![
arm(first.0, first.1, first.2),
arm(second.0, second.1, second.2),
],
),
],
)
};
assert_eq!(
max_size_of(either(("big", 1, "Big"), ("small", 2, "Small"))),
Some(433)
);
assert_eq!(
max_size_of(either(("small", 1, "Small"), ("big", 2, "Big"))),
Some(433)
);
}
#[test]
fn a_head_whose_padded_end_passes_u32_is_refused() {
let huge = fixture(
"Huge",
vec![struct_decl(
"Huge",
vec![
field("a", 1, array(byte(), 4_294_967_235, 4_294_967_235)),
field("b", 2, int(v2::IntWidth::I64)),
field("c", 3, array(byte(), 9, 9)),
field("d", 4, int(v2::IntWidth::I64)),
field("e", 5, array(byte(), 9, 9)),
field("f", 6, int(v2::IntWidth::I64)),
],
)],
);
let decl = subject(&huge);
let Some(v2::decl::Kind::StructDef(def)) = &decl.kind else {
unreachable!()
};
let error = struct_head(packages(&huge), "Huge", def).expect_err("too large");
assert!(error.message.contains("u32"), "{}", error.message);
assert_eq!(max_size_of(huge), None);
}
#[test]
fn a_tombstone_holds_no_bytes() {
let h = head(fixture(
"Trimmed",
vec![struct_decl(
"Trimmed",
vec![field("a", 1, byte()), retired(2), field("c", 3, byte())],
)],
));
assert_eq!(offsets(&h), vec![0, 1]);
assert_eq!(h.size, 8);
}
#[test]
fn an_exact_length_array_and_exact_bytes_are_inline() {
let h = head(fixture(
"Fixed",
vec![struct_decl(
"Fixed",
vec![
field("samples", 1, array(int(v2::IntWidth::U16), 3, 3)),
field("mac", 2, bytes(6, 6)),
field("stamp", 3, int(v2::IntWidth::I64)),
],
)],
));
assert_eq!(offsets(&h), vec![0, 6, 16]);
assert_eq!(pads(&h), vec![(12, 4)]);
assert_eq!(h.size, 24);
assert!(matches!(
h.fields[0].form,
Form::InlineArray { count: 3, .. }
));
assert!(matches!(
h.fields[1].form,
Form::InlineArray { count: 6, .. }
));
}
#[test]
fn a_map_entry_is_a_head_of_key_and_value() {
let h = head(fixture(
"Table",
vec![struct_decl(
"Table",
vec![field("rows", 1, map(byte(), int(v2::IntWidth::I32), 0, 10))],
)],
));
let Form::Descriptor(DescriptorKind::Map {
entry,
elem_size,
max: 10,
..
}) = &h.fields[0].form
else {
panic!("a map descriptor");
};
assert_eq!(*elem_size, 8);
assert_eq!(offsets(entry), vec![0, 4]);
assert_eq!(entry.fields[0].source, Source::Key);
assert_eq!(entry.fields[1].source, Source::Value);
}
#[test]
fn an_inline_head_records_the_type_it_lays_out() {
let h = head(fixture(
"Both",
vec![
struct_decl("A", vec![field("x", 1, byte())]),
struct_decl("B", vec![field("x", 1, byte())]),
struct_decl(
"Both",
vec![
field("a", 1, named("A")),
field("b", 2, named("B")),
field("t", 3, tuple(vec![("x", byte())])),
],
),
],
));
let of: Vec<_> = h
.fields
.iter()
.map(|placement| match &placement.form {
Form::Inline { of, .. } => of.clone(),
other => panic!("an inline form, got {other:?}"),
})
.collect();
let declared = |name: &str| Inlined::Declaration {
package: "veh".to_string(),
name: name.to_string(),
};
assert_eq!(of, vec![declared("A"), declared("B"), Inlined::Tuple]);
}
#[test]
fn a_scalar_arm_is_a_one_field_head() {
let h = head_of_union(fixture(
"Reading",
vec![
v2::Decl {
name: "Percent".to_string(),
kind: Some(v2::decl::Kind::TypeDef(v2::TypeDef {
backing: backing(v2::PrimitiveType::Integer),
width: Some(v2::type_def::Width::IntWidth(v2::IntWidth::U8 as i32)),
..Default::default()
})),
..Default::default()
},
struct_decl("Empty", vec![]),
union_decl(
"Reading",
vec![arm("level", 1, "Percent"), arm("none", 2, "Empty")],
),
],
));
let Form::Descriptor(DescriptorKind::Arm { arms }) = &h.fields[1].form else {
panic!("the arm descriptor");
};
assert_eq!(arms[0].1.size, 8);
assert_eq!(arms[0].1.fields[0].source, Source::Arm);
assert_eq!(arms[0].1.fields[0].offset, 0);
assert_eq!(arms[1].1.size, 0);
}
#[test]
fn a_type_that_reaches_itself_has_no_layout() {
let loop_back = fixture(
"Loop",
vec![struct_decl("Loop", vec![field("again", 1, named("Loop"))])],
);
let decl = subject(&loop_back);
let Some(v2::decl::Kind::StructDef(def)) = &decl.kind else {
unreachable!()
};
let error = struct_head(packages(&loop_back), "Loop", def).expect_err("a cycle");
assert!(error.message.contains("Loop"), "{}", error.message);
assert_eq!(max_size_of(loop_back), None);
}
}