From b207c98df78e991a1355a2a0155063ce1d153297 Mon Sep 17 00:00:00 2001 From: Alex Mikhalev Date: Sun, 21 Nov 2021 13:09:24 -0800 Subject: [PATCH] implement computing message layout --- src/bag.rs | 30 +++-------- src/layout.rs | 142 ++++++++++++++++++++++++++++++++++++++++++++++--- src/message.rs | 11 ++-- src/parse.rs | 2 +- 4 files changed, 153 insertions(+), 32 deletions(-) diff --git a/src/bag.rs b/src/bag.rs index fe16934..0c4185c 100644 --- a/src/bag.rs +++ b/src/bag.rs @@ -1,9 +1,9 @@ use std::{fs::File, path::Path}; use eyre::Context; -use log::{error, trace}; +use log::debug; -use crate::{Result, index::BagIndex, info::BagInfo, message::parse_msgs, reader::MmapReader}; +use crate::{index::BagIndex, info::BagInfo, message::compute_layout, reader::MmapReader, Result}; pub struct Bag { reader: MmapReader, @@ -20,27 +20,13 @@ impl Bag { pub fn compute_message_layouts(&mut self) -> Result<()> { for conn in &self.index.connections { - match parse_msgs(conn) { - Ok(msgs) => { - for msg in &msgs { - trace!( - "message definition parsed: {:#?}", - msg.fields() - .iter() - .filter(|field| !field.is_constant()) - .map(ToString::to_string) - .collect::>() - ); - } - } - Err(err) => error!("could not parse message definition: {}", err), - } + let layout = compute_layout(conn)?; + debug!("message layout: {:#?}", layout); } - Ok(()) + Ok(()) } - pub fn compute_info(&mut self) -> Result { - BagInfo::compute(&mut self.reader, &self.index) - } + pub fn compute_info(&mut self) -> Result { + BagInfo::compute(&mut self.reader, &self.index) + } } - diff --git a/src/layout.rs b/src/layout.rs index 821898f..9c45c22 100644 --- a/src/layout.rs +++ b/src/layout.rs @@ -1,6 +1,39 @@ -use ros_message::I8Variant; +use std::collections::HashMap; -use crate::SmallStr; +use eyre::{bail, eyre}; +use ros_message::{DataType, FieldCase, FieldInfo, I8Variant, MessagePath, Msg, U8Variant}; +use thiserror::Error; + +use crate::{Result, SmallStr}; + +type Dependencies = HashMap; + +#[derive(Clone, Debug, Error)] +#[error("missing dependent message definition: {0}")] +pub struct MissingDependency(MessagePath); + +#[derive(Clone, Debug, Error)] +#[error("dependency cycle involving: {0:?}")] +pub struct DependencyCycle(Vec); + +struct Resolver<'a> { + dependencies: &'a HashMap, + current_path: &'a MessagePath, +} + +impl<'a> Resolver<'a> { + fn get_dep(&self, path: &MessagePath) -> Result { + self.dependencies + .get(path) + .cloned() + .ok_or_else(|| eyre!(MissingDependency(path.clone()))) + } + + fn get_local_dep(&self, name: &str) -> Result { + let full_path = self.current_path.peer(name); + self.get_dep(&full_path) + } +} #[derive(Clone, Debug)] pub enum FieldType { @@ -15,7 +48,7 @@ pub enum FieldType { /// Represents `int64`. I64, /// Represents `uint8` or `char`. - U8(I8Variant), + U8(U8Variant), /// Represents `uint16`. U16, /// Represents `uint32`. @@ -36,6 +69,29 @@ pub enum FieldType { Message(MessageLayoutRef), } +impl FieldType { + fn from_datatype_and_resolver(datatype: &DataType, resolver: &Resolver) -> Result { + Ok(match datatype { + DataType::Bool => FieldType::Bool, + DataType::I8(v) => FieldType::I8(v.clone()), + DataType::I16 => FieldType::I16, + DataType::I32 => FieldType::I32, + DataType::I64 => FieldType::I64, + DataType::U8(v) => FieldType::U8(v.clone()), + DataType::U16 => FieldType::U16, + DataType::U32 => FieldType::U32, + DataType::U64 => FieldType::U64, + DataType::F32 => FieldType::F32, + DataType::F64 => FieldType::F64, + DataType::String => FieldType::String, + DataType::Time => FieldType::Time, + DataType::Duration => FieldType::Duration, + DataType::LocalMessage(path) => FieldType::Message(resolver.get_local_dep(path)?), + DataType::GlobalMessage(path) => FieldType::Message(resolver.get_dep(path)?), + }) + } +} + #[derive(Clone, Debug)] pub struct FieldLayout { pub typ: FieldType, @@ -43,11 +99,26 @@ pub struct FieldLayout { pub multiplicity: Multiplicity, } +impl FieldLayout { + fn from_info_and_resolver(info: &FieldInfo, resolver: &Resolver) -> Result { + Ok(Self { + typ: FieldType::from_datatype_and_resolver(info.datatype(), resolver)?, + name: info.name().into(), + multiplicity: match info.case() { + FieldCase::Unit => Multiplicity::Unit, + FieldCase::Vector => Multiplicity::Dynamic, + FieldCase::Array(n) => Multiplicity::Fixed(*n), + FieldCase::Const(_) => unreachable!(), + }, + }) + } +} + #[derive(Clone, Debug, PartialEq, Eq)] pub enum Multiplicity { - Unit, - Fixed(usize), - Dynamic, + Unit, + Fixed(usize), + Dynamic, } #[derive(Clone, Debug)] @@ -56,3 +127,62 @@ pub struct MessageLayout { } pub type MessageLayoutRef = std::sync::Arc; + +impl MessageLayout { + pub fn from_msgs<'a, M, I>(msgs: M) -> Result + where + M: IntoIterator, + I: Iterator + DoubleEndedIterator, + { + let mut dependencies: Dependencies = Default::default(); + let mut msgs = msgs.into_iter(); + let main = msgs + .next() + .ok_or_else(|| eyre!("at least one msg required"))?; + + let mut deps: Vec<_> = msgs.collect(); + + while !deps.is_empty() { + let mut remaining = Vec::new(); + let orig_length = deps.len(); + for msg in deps.drain(..) { + let layout = match MessageLayout::from_msg_and_deps(msg, &dependencies) { + Ok(layout) => layout, + Err(err) => { + if err.root_cause().is::() { + // Has a missing dependency, resovle it on the next iteration + remaining.push(msg); + continue; + } else { + return Err(err); + } + } + }; + dependencies.insert(msg.path().clone(), MessageLayoutRef::from(layout)); + } + if remaining.len() == orig_length { + // Made no progress, there is a dependency cycle + bail!(DependencyCycle( + remaining.iter().map(|msg| msg.path().to_string()).collect() + )); + } + deps = remaining; + } + + MessageLayout::from_msg_and_deps(main, &dependencies) + } + + fn from_msg_and_deps(msg: &Msg, dependencies: &Dependencies) -> Result { + let resolver = Resolver { + dependencies, + current_path: msg.path(), + }; + let fields = msg + .fields() + .iter() + .filter(|field| !field.is_constant()) + .map(|field| FieldLayout::from_info_and_resolver(field, &resolver)) + .collect::, _>>()?; + Ok(MessageLayout { fields }) + } +} diff --git a/src/message.rs b/src/message.rs index 350d692..f4dd21f 100644 --- a/src/message.rs +++ b/src/message.rs @@ -5,10 +5,10 @@ use log::trace; use regex::Regex; use ros_message::{MessagePath, Msg}; -use crate::{index::ConnInfo, Result}; +use crate::{index::ConnInfo, layout::MessageLayout, Result}; fn parse_msg(message_name: &str, msgdef: &str) -> Result { - trace!("message definition: {}", msgdef); + trace!("message definition {}: {}", message_name, msgdef); let path = MessagePath::try_from(message_name)?; let msgtype = Msg::new(path, msgdef)?; Ok(msgtype) @@ -18,7 +18,7 @@ lazy_static! { static ref BOUNDARY_RE: Regex = Regex::new(r"\r?\n==+\r?\nMSG: ([^\r\n]+)\r?\n").unwrap(); } -pub fn parse_msgs(conn: &ConnInfo) -> Result> { +fn parse_msgs(conn: &ConnInfo) -> Result> { let msgdefs = &conn.message_definition; let mut name = conn.datatype.clone(); let mut begin = 0usize; @@ -43,3 +43,8 @@ pub fn parse_msgs(conn: &ConnInfo) -> Result> { Ok(msgs) } + +pub fn compute_layout(conn: &ConnInfo) -> Result { + let msgs = parse_msgs(conn)?; + MessageLayout::from_msgs(&msgs) +} diff --git a/src/parse.rs b/src/parse.rs index e935464..f9b0273 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -13,4 +13,4 @@ pub type IResult<'a, T> = nom::IResult, T, Error>>; pub type InputStr<'a> = &'a str; pub type IResultStr<'a, T> = nom::IResult, T, Error>>; -pub type SmallStr = String; +pub type SmallStr = crate::SmallStr;