use crate::module::{Constant, Metadata, MetadataId, Module, Type, TypeId, Value, ValueId};
use crate::{Result, error};
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Element {
pub id: u32,
pub name: String,
pub index: u32,
pub component_type: u8,
pub system_value: u8,
pub interpolation: u8,
pub row: u32,
pub column: u8,
pub rows: u32,
pub columns: u8,
pub placed: bool,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Range {
pub class: u8,
pub id: u32,
pub lower: u32,
pub count: u32,
pub extra: u32,
pub space: u32,
pub element: Option<u32>,
pub stride: Option<u32>,
pub counter: bool,
pub variable: Option<ValueId>,
}
#[derive(Clone, Copy, Debug)]
struct Node<'a> {
m: &'a Module,
id: MetadataId,
ops: &'a [Option<MetadataId>],
what: &'static str,
}
impl<'a> Node<'a> {
fn new(m: &'a Module, at: usize, id: MetadataId, what: &'static str) -> Result<Self> {
match m.metadata.get(id) {
Some(Metadata::Node(ops)) => Ok(Node { m, id, ops, what }),
_ => Err(error(at, format!("{what}: expected a metadata node"))),
}
}
fn bit(&self) -> usize {
self.m.metadata_at.get(self.id).copied().unwrap_or(self.m.at)
}
fn fail(&self, detail: impl std::fmt::Display) -> crate::Error {
error(self.bit(), format!("{}: {detail}", self.what))
}
fn node(&self, id: Option<MetadataId>, what: &'static str) -> Result<Node<'a>> {
let id = id.ok_or_else(|| self.fail(format!("{what} is null")))?;
Node::new(self.m, self.bit(), id, what)
}
fn op(&self, i: usize) -> Result<Option<MetadataId>> {
self.ops
.get(i)
.copied()
.ok_or_else(|| self.fail(format!("node has no operand {i}")))
}
fn maybe(&self, i: usize) -> Option<MetadataId> {
self.ops.get(i).copied().flatten()
}
fn int_of(&self, id: Option<MetadataId>, what: &str) -> Result<i64> {
match id.and_then(|i| self.m.metadata.get(i)) {
Some(Metadata::Value { value, .. }) => match self.m.values.get(*value) {
Some(Value::Constant(Constant::Integer(v))) => Ok(*v),
Some(Value::Constant(Constant::Null)) => Ok(0),
_ => Err(self.fail(format!("{what}: expected an integer constant"))),
},
_ => Err(self.fail(format!("{what}: expected an integer"))),
}
}
fn int(&self, i: usize, what: &str) -> Result<i64> {
self.int_of(self.op(i)?, what)
}
fn number_of<T: TryFrom<i64>>(&self, id: Option<MetadataId>, what: &str) -> Result<T> {
let v = self.int_of(id, what)?;
T::try_from(v).map_err(|_| self.fail(format!("{what}: {v} is out of range")))
}
fn number<T: TryFrom<i64>>(&self, i: usize, what: &str) -> Result<T> {
self.number_of(self.op(i)?, what)
}
fn string_of(&self, id: Option<MetadataId>, what: &str) -> Result<String> {
match id.and_then(|i| self.m.metadata.get(i)) {
Some(Metadata::String(s)) => std::str::from_utf8(s).map(str::to_owned).map_err(|e| {
self.fail(format!(
"{what}: not UTF-8 (valid up to byte {})",
e.valid_up_to()
))
}),
_ => Ok(String::new()),
}
}
}
fn named<'a>(m: &'a Module, name: &str) -> Option<(usize, &'a [MetadataId])> {
let i = m.named_metadata.iter().position(|(n, _)| n == name)?;
let (_, ids) = m.named_metadata.get(i)?;
Some((
m.named_metadata_at.get(i).copied().unwrap_or(m.at),
ids.as_slice(),
))
}
pub fn library_kind(m: &Module) -> Result<u32> {
let entry = the_entry(m)?;
let properties = entry
.node(entry.maybe(4), "entry point properties")
.map_err(|_| entry.fail("a library entry point without properties"))?;
for pair in properties.ops.chunks(2) {
let [tag, value] = pair else {
return Err(properties.fail("a tag without a value"));
};
if properties.int_of(*tag, "property tag")? == 8 {
return properties.number_of(*value, "shader kind");
}
}
Err(properties.fail("a library entry point that states no shader kind"))
}
fn the_entry(m: &Module) -> Result<Node<'_>> {
let (at, entries) =
named(m, "dx.entryPoints").ok_or_else(|| error(m.at, "the module has no dx.entryPoints metadata"))?;
let mut with_function = Vec::new();
for &id in entries {
let entry = Node::new(m, at, id, "entry point")?;
if entry.op(0)?.is_some() {
with_function.push(entry);
}
}
match with_function.as_slice() {
[entry] => Ok(*entry),
_ => Err(error(
at,
format!(
"dx.entryPoints: expected exactly one entry point with a function, found {}",
with_function.len()
),
)),
}
}
pub fn entry(m: &Module) -> Result<ValueId> {
let entry = the_entry(m)?;
match entry.op(0)?.and_then(|i| m.metadata.get(i)) {
Some(Metadata::Value { value, .. }) => Ok(*value),
_ => Err(entry.fail("operand 0 names no function")),
}
}
pub fn num_threads(m: &Module) -> Result<Option<[u32; 3]>> {
let entry = the_entry(m)?;
let Some(properties) = entry.maybe(4) else {
return Ok(None);
};
let properties = entry.node(Some(properties), "entry point properties")?;
for pair in properties.ops.chunks(2) {
let [tag, value] = pair else {
return Err(properties.fail("a tag without a value"));
};
let tag = properties.int_of(*tag, "property tag")?;
if tag != 4 && tag != 9 && tag != 10 {
continue;
}
let mut size = properties.node(*value, "numthreads")?;
if tag == 9 || tag == 10 {
size = size.node(size.op(0)?, "numthreads")?;
}
return Ok(Some([
size.number(0, "numthreads x")?,
size.number(1, "numthreads y")?,
size.number(2, "numthreads z")?,
]));
}
Ok(None)
}
pub fn shader_flags(m: &Module) -> Result<Option<u64>> {
let entry = the_entry(m)?;
let Some(properties) = entry.maybe(4) else {
return Ok(None);
};
let properties = entry.node(Some(properties), "entry point properties")?;
for pair in properties.ops.chunks(2) {
let [tag, value] = pair else {
return Err(properties.fail("a tag without a value"));
};
if properties.int_of(*tag, "property tag")? == 0 {
let flags = properties.int_of(*value, "shader flags")?;
return Ok(Some(u64::from_le_bytes(flags.to_le_bytes())));
}
}
Ok(None)
}
pub fn usage_masks(m: &Module) -> Result<[Vec<Option<u8>>; 3]> {
Ok(element_tags(m)?.map(|list| list.into_iter().map(|tags| tags.usage_mask).collect()))
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct ElementTags {
pub stream: Option<u32>,
pub dynamic_index_mask: Option<u8>,
pub usage_mask: Option<u8>,
pub other: Vec<(i64, i64)>,
}
pub fn element_tags(m: &Module) -> Result<[Vec<ElementTags>; 3]> {
let entry = the_entry(m)?;
let Some(signatures) = entry.op(2)? else {
return Ok([Vec::new(), Vec::new(), Vec::new()]);
};
let signatures = entry.node(Some(signatures), "signatures")?;
let list = |i: usize| -> Result<Vec<ElementTags>> {
let Some(list) = signatures.op(i)? else {
return Ok(Vec::new());
};
let list = signatures.node(Some(list), "signature")?;
list.ops
.iter()
.map(|e| {
let e = list.node(*e, "signature element")?;
let Some(tail) = e.maybe(10) else {
return Ok(ElementTags::default());
};
let tail = e.node(Some(tail), "element tags")?;
let (pairs, rest) = tail.ops.as_chunks::<2>();
if !rest.is_empty() {
return Err(tail.fail("a tag without its value"));
}
let mut tags = ElementTags::default();
for [tag, value] in pairs {
match tail.int_of(*tag, "element tag")? {
0 => tags.stream = Some(tail.number_of(*value, "element stream")?),
2 => tags.dynamic_index_mask = Some(tail.number_of(*value, "dynamic index mask")?),
3 => tags.usage_mask = Some(tail.number_of(*value, "usage mask")?),
other => {
if let Ok(v) = tail.int_of(*value, "element tag value") {
tags.other.push((other, v));
}
}
}
}
Ok(tags)
})
.collect()
};
Ok([list(0)?, list(1)?, list(2)?])
}
pub fn elements(m: &Module) -> Result<(Vec<Element>, Vec<Element>)> {
let [inputs, outputs, _] = signature_lists(m)?;
Ok((inputs, outputs))
}
pub fn patch_elements(m: &Module) -> Result<Vec<Element>> {
let [_, _, patch] = signature_lists(m)?;
Ok(patch)
}
#[derive(Clone, Debug, PartialEq)]
pub struct HullState {
pub patch_constant: ValueId,
pub input_control_points: u32,
pub output_control_points: u32,
pub domain: u32,
pub partitioning: u32,
pub output_primitive: u32,
pub max_tess_factor: f32,
}
fn entry_property(m: &Module, wanted: i64) -> Result<Option<Node<'_>>> {
let entry = the_entry(m)?;
let Some(properties) = entry.maybe(4) else {
return Ok(None);
};
let properties = entry.node(Some(properties), "entry point properties")?;
for pair in properties.ops.chunks(2) {
let [tag, value] = pair else {
return Err(properties.fail("a tag without a value"));
};
if properties.int_of(*tag, "property tag")? == wanted {
return properties.node(*value, "property").map(Some);
}
}
Ok(None)
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct GeometryState {
pub input: u32,
pub max_vertices: u32,
pub streams: u32,
pub topology: u32,
pub instances: u32,
}
pub fn geometry_state(m: &Module) -> Result<GeometryState> {
let state = entry_property(m, 1)?.ok_or_else(|| {
the_entry(m).map_or_else(
|e| e,
|e| e.fail("a geometry program without its state (properties tag 1)"),
)
})?;
Ok(GeometryState {
input: state.number(0, "geometry input primitive")?,
max_vertices: state.number(1, "geometry maximum vertices")?,
streams: state.number(2, "geometry streams")?,
topology: state.number(3, "geometry topology")?,
instances: state.number(4, "geometry instances")?,
})
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct MeshState {
pub max_vertices: u32,
pub max_primitives: u32,
pub topology: u32,
pub payload: u32,
}
pub fn mesh_state(m: &Module) -> Result<MeshState> {
let state = entry_property(m, 9)?.ok_or_else(|| {
the_entry(m).map_or_else(
|e| e,
|e| e.fail("a mesh program without its state (properties tag 9)"),
)
})?;
let field = |k: usize, what: &str| -> Result<u32> {
match state.op(k)? {
None => Ok(0),
some => state.number_of(some, what),
}
};
Ok(MeshState {
max_vertices: field(1, "mesh maximum vertices")?,
max_primitives: field(2, "mesh maximum primitives")?,
topology: field(3, "mesh topology")?,
payload: field(4, "mesh payload size")?,
})
}
pub fn hull_state(m: &Module) -> Result<HullState> {
let state = entry_property(m, 3)?.ok_or_else(|| {
the_entry(m).map_or_else(
|e| e,
|e| e.fail("a hull program without its patch state (properties tag 3)"),
)
})?;
let small = |k: usize, what: &str| -> Result<u32> { state.number_of(state.maybe(k), what) };
let patch_constant = match state.maybe(0).and_then(|i| m.metadata.get(i)) {
Some(Metadata::Value { value, .. }) => *value,
_ => return Err(state.fail("a hull program without its patch-constant function")),
};
let max = match state.maybe(6).and_then(|i| m.metadata.get(i)) {
Some(Metadata::Value { value, .. }) => match m.values.get(*value) {
Some(Value::Constant(Constant::Float(bits))) => {
let bits = u32::try_from(*bits)
.map_err(|_| state.fail("the maximum tessellation factor is wider than a float"))?;
f32::from_bits(bits)
}
_ => return Err(state.fail("the maximum tessellation factor is not a float")),
},
_ => return Err(state.fail("hull state without a maximum tessellation factor")),
};
Ok(HullState {
patch_constant,
input_control_points: small(1, "hull input control points")?,
output_control_points: small(2, "hull output control points")?,
domain: small(3, "hull domain")?,
partitioning: small(4, "hull partitioning")?,
output_primitive: small(5, "hull output primitive")?,
max_tess_factor: max,
})
}
pub fn domain_state(m: &Module) -> Result<(u32, u32)> {
let state = entry_property(m, 2)?.ok_or_else(|| {
the_entry(m).map_or_else(
|e| e,
|e| e.fail("a domain program without its patch state (properties tag 2)"),
)
})?;
Ok((
state.number_of(state.maybe(0), "domain")?,
state.number_of(state.maybe(1), "domain input control points")?,
))
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct EntryProperty {
pub tag: i64,
pub value: Option<MetadataId>,
}
pub fn entry_properties(m: &Module) -> Result<Vec<EntryProperty>> {
let entry = the_entry(m)?;
properties_of(entry)
}
fn properties_of(entry: Node<'_>) -> Result<Vec<EntryProperty>> {
let Some(properties) = entry.maybe(4) else {
return Ok(Vec::new());
};
let properties = entry.node(Some(properties), "entry point properties")?;
let (pairs, rest) = properties.ops.as_chunks::<2>();
if !rest.is_empty() {
return Err(properties.fail("a tag without a value"));
}
pairs
.iter()
.map(|[tag, value]| {
Ok(EntryProperty {
tag: properties.int_of(*tag, "property tag")?,
value: *value,
})
})
.collect()
}
fn property_integer(m: &Module, wanted: i64) -> Result<Option<u32>> {
let entry = the_entry(m)?;
for property in properties_of(entry)? {
if property.tag != wanted {
continue;
}
let record = entry.node(property.value, "property").ok();
return match record {
Some(node) => node.number(0, "property value").map(Some),
None => entry.number_of(property.value, "property value").map(Some),
};
}
Ok(None)
}
pub fn ray_payload_size(m: &Module) -> Result<Option<u32>> {
property_integer(m, 6)
}
pub fn ray_attribute_size(m: &Module) -> Result<Option<u32>> {
property_integer(m, 7)
}
pub fn auto_binding_space(m: &Module) -> Result<Option<u32>> {
property_integer(m, 5)
}
pub fn library_binding_space(m: &Module) -> Result<Option<u32>> {
let (at, entries) =
named(m, "dx.entryPoints").ok_or_else(|| error(m.at, "the module has no dx.entryPoints metadata"))?;
for &id in entries {
let entry = Node::new(m, at, id, "entry point")?;
if entry.op(0)?.is_some() {
continue;
}
for property in properties_of(entry)? {
if property.tag == 5 {
let node = entry.node(property.value, "auto binding space")?;
return node.number(0, "auto binding space").map(Some);
}
}
}
Ok(None)
}
pub fn wave_size(m: &Module) -> Result<Option<Vec<u32>>> {
let Some(node) = entry_property(m, 11)? else {
return Ok(None);
};
node.ops
.iter()
.map(|id| node.number_of(*id, "wave size"))
.collect::<Result<Vec<u32>>>()
.map(Some)
}
pub fn amplification_payload_size(m: &Module) -> Result<Option<u32>> {
let Some(state) = entry_property(m, 10)? else {
return Ok(None);
};
state.number(1, "amplification payload size").map(Some)
}
pub fn fp32_denorm_mode(m: &Module) -> Result<Option<String>> {
let function = entry(m)?;
Ok(m.string_attribute(function, "fp32-denorm-mode")
.map(str::to_owned))
}
fn signature_lists(m: &Module) -> Result<[Vec<Element>; 3]> {
let entry = the_entry(m)?;
let Some(signatures) = entry.op(2)? else {
return Ok([Vec::new(), Vec::new(), Vec::new()]);
};
let signatures = entry.node(Some(signatures), "signatures")?;
let list = |i: usize| -> Result<Vec<Element>> {
let Some(list) = signatures.op(i)? else {
return Ok(Vec::new());
};
let list = signatures.node(Some(list), "signature")?;
list.ops
.iter()
.map(|e| {
let e = list.node(*e, "signature element")?;
let row = e.int(8, "row")?;
let element = Element {
id: e.number(0, "element id")?,
name: e.string_of(e.op(1)?, "element name")?,
index: match e.op(4)? {
Some(list) => match e.node(Some(list), "semantic indices")?.ops.first() {
Some(first) => e.number_of(*first, "semantic index")?,
None => 0,
},
None => 0,
},
component_type: e.number(2, "component type")?,
system_value: e.number(3, "system value")?,
interpolation: e.number(5, "interpolation")?,
rows: e.number(6, "rows")?,
columns: e.number(7, "columns")?,
placed: row >= 0,
row: u32::try_from(row.max(0))
.map_err(|_| e.fail(format!("row {row} is out of range")))?,
column: u8::try_from(e.int(9, "column")?.max(0))
.map_err(|_| e.fail("column is out of range"))?,
};
if element.rows == 0
|| element.columns == 0
|| u32::from(element.column)
.checked_add(u32::from(element.columns))
.is_none_or(|end| end > 4)
|| element.row.checked_add(element.rows).is_none_or(|end| end > 32)
{
return Err(e.fail("the element lies outside the 32 four-lane registers"));
}
Ok(element)
})
.collect()
};
Ok([list(0)?, list(1)?, list(2)?])
}
fn tags(resource: Node<'_>, list: Option<MetadataId>) -> Result<(Option<u32>, Option<u32>)> {
let Some(list) = list else {
return Ok((None, None));
};
let ops = resource.node(Some(list), "resource tags")?;
let (pairs, rest) = ops.ops.as_chunks::<2>();
if !rest.is_empty() {
return Err(ops.fail("a tag without its value"));
}
let (mut element, mut stride) = (None, None);
for [tag, value] in pairs {
let value = ops.number_of(*value, "tag value")?;
match ops.int_of(*tag, "tag")? {
0 => element = Some(value),
1 => stride = Some(value),
_ => {}
}
}
Ok((element, stride))
}
fn variable_of(m: &Module, id: Option<MetadataId>) -> Option<ValueId> {
let Some(Metadata::Value { value, .. }) = id.and_then(|i| m.metadata.get(i)) else {
return None;
};
match m.values.get(*value)? {
Value::Constant(Constant::Cast { value, .. }) => Some(*value),
Value::Global { .. } => Some(*value),
_ => None,
}
}
pub fn ranges(m: &Module) -> Result<Vec<(Range, String, Option<TypeId>)>> {
let Some((at, [resources])) = named(m, "dx.resources") else {
return Ok(Vec::new());
};
let lists = Node::new(m, at, *resources, "dx.resources")?;
let mut out = Vec::new();
for (class, list) in lists.ops.iter().enumerate() {
let Some(list) = list else { continue };
let class =
u8::try_from(class).map_err(|_| lists.fail(format!("resource class {class} is out of range")))?;
let list = lists.node(Some(*list), "resource list")?;
for r in list.ops {
let r = list.node(*r, "resource")?;
let ty = match r.op(1)?.and_then(|i| m.metadata.get(i)) {
Some(Metadata::Value { ty, .. }) => match m.types.get(*ty) {
Some(Type::Pointer(t, _)) => Some(*t),
_ => None,
},
_ => None,
};
let extra = r.number(6, "resource field")?;
let (element, stride) = match class {
0 => tags(r, r.maybe(8))?,
1 => tags(r, r.maybe(10))?,
_ => (None, None),
};
out.push((
Range {
class,
id: r.number(0, "resource id")?,
lower: r.number(4, "lower bound")?,
count: {
let v = r.int(5, "range size")?;
u32::try_from(v)
.or_else(|_| i32::try_from(v).map(|v| u32::from_ne_bytes(v.to_ne_bytes())))
.map_err(|_| r.fail(format!("range size {v} is out of range")))?
},
extra,
space: r.number(3, "register space")?,
element,
stride,
counter: class == 1 && r.maybe(8).is_some() && r.int(8, "counter")? != 0,
variable: variable_of(m, r.op(1)?),
},
r.string_of(r.op(2)?, "resource name")?,
ty,
));
}
}
Ok(out)
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct ResourceExtras {
pub class: u8,
pub id: u32,
pub sample_count: Option<u32>,
pub globally_coherent: bool,
pub rasterizer_ordered: bool,
pub sampler_feedback: Option<u32>,
pub atomic64: bool,
pub tags: Vec<(i64, i64)>,
}
pub fn resource_extras(m: &Module) -> Result<Vec<ResourceExtras>> {
let Some((at, [resources])) = named(m, "dx.resources") else {
return Ok(Vec::new());
};
let lists = Node::new(m, at, *resources, "dx.resources")?;
let mut out = Vec::new();
for (class, list) in lists.ops.iter().enumerate() {
let Some(list) = list else { continue };
let class =
u8::try_from(class).map_err(|_| lists.fail(format!("resource class {class} is out of range")))?;
let list = lists.node(Some(*list), "resource list")?;
for r in list.ops {
let r = list.node(*r, "resource")?;
let tag_operand = match class {
0 => 8,
1 => 10,
_ => 7,
};
let mut tags = Vec::new();
if let Some(tag_list) = r.maybe(tag_operand) {
let tag_list = r.node(Some(tag_list), "resource tags")?;
let (pairs, rest) = tag_list.ops.as_chunks::<2>();
if !rest.is_empty() {
return Err(tag_list.fail("a tag without its value"));
}
for [tag, value] in pairs {
tags.push((
tag_list.int_of(*tag, "tag")?,
tag_list.int_of(*value, "tag value")?,
));
}
}
let tag_value = |wanted: i64| tags.iter().find(|(t, _)| *t == wanted).map(|(_, v)| *v);
out.push(ResourceExtras {
class,
id: r.number(0, "resource id")?,
sample_count: match (class, r.maybe(7)) {
(0, Some(_)) => Some(r.number(7, "sample count")?),
_ => None,
},
globally_coherent: class == 1 && r.maybe(7).is_some() && r.int(7, "globally coherent")? != 0,
rasterizer_ordered: class == 1
&& r.maybe(9).is_some()
&& r.int(9, "rasterizer ordered")? != 0,
sampler_feedback: match tag_value(2) {
Some(v) => Some(
u32::try_from(v)
.map_err(|_| r.fail(format!("sampler feedback kind {v} is out of range")))?,
),
None => None,
},
atomic64: tag_value(3).is_some_and(|v| v != 0),
tags,
});
}
}
Ok(out)
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct FieldAnnotation {
pub name: String,
pub offset: u32,
pub component: Option<u32>,
pub matrix: Option<(u32, u32, u32)>,
}
pub fn fields(m: &Module, t: TypeId) -> Result<Vec<FieldAnnotation>> {
let Some((at, annotations)) = named(m, "dx.typeAnnotations") else {
return Ok(Vec::new());
};
for &a in annotations {
let a = Node::new(m, at, a, "type annotations")?;
if a.int(0, "annotation kind")? != 0 {
continue;
}
let (pairs, rest) = a.ops.get(1..).unwrap_or_default().as_chunks::<2>();
if !rest.is_empty() {
return Err(a.fail("a struct type without its annotation"));
}
for [struct_type, annotation] in pairs {
let ty = match struct_type.and_then(|i| m.metadata.get(i)) {
Some(Metadata::Value { ty, .. }) => *ty,
_ => continue,
};
if ty != t {
continue;
}
let annotation = a.node(*annotation, "struct annotation")?;
let fields = annotation
.ops
.get(1..)
.ok_or_else(|| annotation.fail("a struct annotation without its first operand"))?;
return fields
.iter()
.map(|f| {
let f = annotation.node(*f, "field annotation")?;
let (mut name, mut offset, mut component, mut matrix) = (String::new(), 0, None, None);
let (tags, rest) = f.ops.as_chunks::<2>();
if !rest.is_empty() {
return Err(f.fail("a tag without its value"));
}
for [tag, value] in tags {
match f.int_of(*tag, "field tag")? {
6 => name = f.string_of(*value, "field name")?,
3 => offset = f.number_of(*value, "field offset")?,
2 => {
let ops = f.node(*value, "matrix annotation")?;
matrix = Some((
ops.number(0, "matrix rows")?,
ops.number(1, "matrix columns")?,
ops.number(2, "matrix orientation")?,
));
}
7 => component = Some(f.number_of(*value, "field component type")?),
_ => {}
}
}
Ok(FieldAnnotation {
name,
offset,
component,
matrix,
})
})
.collect();
}
}
Ok(Vec::new())
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct SignatureElement {
pub name: String,
pub index: u32,
pub system_value: u32,
pub component_type: u32,
pub register: u32,
pub mask: u8,
pub read_write_mask: u8,
pub bit: usize,
}
pub fn signature(part: &[u8]) -> Result<Vec<SignatureElement>> {
let bit = |byte: usize| byte.saturating_mul(8);
let v = primase_foundations::bytes::View::new(part);
let word = |at: usize| -> Result<u32> {
v.array::<4>(at)
.map(u32::from_le_bytes)
.map_err(|e| error(bit(at), e.to_string()))
};
let field = |record: usize, delta: usize| -> Result<u32> {
word(
record
.checked_add(delta)
.ok_or_else(|| error(bit(record), "signature element offset"))?,
)
};
let n = word(0)? as usize;
let first = word(4)? as usize;
let table = n
.checked_mul(32)
.and_then(|size| first.checked_add(size))
.filter(|&end| end <= part.len())
.ok_or_else(|| {
error(
0,
format!(
"{n} records of 32 bytes from byte {first} do not fit the part's {} bytes",
part.len()
),
)
})?;
let mut out = Vec::with_capacity(n);
for i in 0..n {
let e = i
.checked_mul(32)
.and_then(|o| o.checked_add(first))
.filter(|&e| e < table)
.ok_or_else(|| error(32, "signature element offset"))?;
let name_at = field(e, 4)? as usize;
let tail = part
.get(name_at..)
.ok_or_else(|| error(bit(name_at), "signature name offset"))?;
let end = tail
.iter()
.position(|&b| b == 0)
.filter(|&n| n <= 256)
.ok_or_else(|| error(bit(name_at), "signature name"))?;
let name = tail
.get(..end)
.and_then(|name| std::str::from_utf8(name).ok())
.ok_or_else(|| error(bit(name_at), "signature name is not UTF-8"))?;
let [mask, read_write_mask] = v
.array::<2>(
e.checked_add(24)
.ok_or_else(|| error(bit(e), "signature element offset"))?,
)
.map_err(|x| error(bit(e), x.to_string()))?;
out.push(SignatureElement {
name: name.to_owned(),
index: field(e, 8)?,
system_value: field(e, 12)?,
component_type: field(e, 16)?,
register: field(e, 20)?,
mask,
read_write_mask,
bit: bit(e),
});
}
Ok(out)
}