implement computing message layout

This commit is contained in:
Alex Mikhalev 2021-11-21 13:09:24 -08:00
parent 50fa2b43e3
commit b207c98df7
4 changed files with 153 additions and 32 deletions

View File

@ -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::<Vec<_>>()
);
}
}
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> {
BagInfo::compute(&mut self.reader, &self.index)
}
pub fn compute_info(&mut self) -> Result<BagInfo> {
BagInfo::compute(&mut self.reader, &self.index)
}
}

View File

@ -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<MessagePath, MessageLayoutRef>;
#[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<String>);
struct Resolver<'a> {
dependencies: &'a HashMap<MessagePath, MessageLayoutRef>,
current_path: &'a MessagePath,
}
impl<'a> Resolver<'a> {
fn get_dep(&self, path: &MessagePath) -> Result<MessageLayoutRef> {
self.dependencies
.get(path)
.cloned()
.ok_or_else(|| eyre!(MissingDependency(path.clone())))
}
fn get_local_dep(&self, name: &str) -> Result<MessageLayoutRef> {
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<Self> {
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<Self> {
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<MessageLayout>;
impl MessageLayout {
pub fn from_msgs<'a, M, I>(msgs: M) -> Result<MessageLayout>
where
M: IntoIterator<IntoIter = I>,
I: Iterator<Item = &'a Msg> + 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::<MissingDependency>() {
// 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<MessageLayout> {
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::<Result<Vec<_>, _>>()?;
Ok(MessageLayout { fields })
}
}

View File

@ -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<Msg> {
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<Vec<Msg>> {
fn parse_msgs(conn: &ConnInfo) -> Result<Vec<Msg>> {
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<Vec<Msg>> {
Ok(msgs)
}
pub fn compute_layout(conn: &ConnInfo) -> Result<MessageLayout> {
let msgs = parse_msgs(conn)?;
MessageLayout::from_msgs(&msgs)
}

View File

@ -13,4 +13,4 @@ pub type IResult<'a, T> = nom::IResult<Input<'a>, T, Error<Input<'a>>>;
pub type InputStr<'a> = &'a str;
pub type IResultStr<'a, T> = nom::IResult<InputStr<'a>, T, Error<InputStr<'a>>>;
pub type SmallStr = String;
pub type SmallStr = crate::SmallStr;