implement computing message layout
This commit is contained in:
		
							parent
							
								
									50fa2b43e3
								
							
						
					
					
						commit
						b207c98df7
					
				
							
								
								
									
										30
									
								
								src/bag.rs
									
									
									
									
									
								
							
							
						
						
									
										30
									
								
								src/bag.rs
									
									
									
									
									
								
							@ -1,9 +1,9 @@
 | 
				
			|||||||
use std::{fs::File, path::Path};
 | 
					use std::{fs::File, path::Path};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use eyre::Context;
 | 
					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 {
 | 
					pub struct Bag {
 | 
				
			||||||
    reader: MmapReader,
 | 
					    reader: MmapReader,
 | 
				
			||||||
@ -20,27 +20,13 @@ impl Bag {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    pub fn compute_message_layouts(&mut self) -> Result<()> {
 | 
					    pub fn compute_message_layouts(&mut self) -> Result<()> {
 | 
				
			||||||
        for conn in &self.index.connections {
 | 
					        for conn in &self.index.connections {
 | 
				
			||||||
            match parse_msgs(conn) {
 | 
					            let layout = compute_layout(conn)?;
 | 
				
			||||||
                Ok(msgs) => {
 | 
					            debug!("message layout: {:#?}", layout);
 | 
				
			||||||
                    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),
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
		Ok(())
 | 
					        Ok(())
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	pub fn compute_info(&mut self) -> Result<BagInfo> {
 | 
					    pub fn compute_info(&mut self) -> Result<BagInfo> {
 | 
				
			||||||
		BagInfo::compute(&mut self.reader, &self.index)
 | 
					        BagInfo::compute(&mut self.reader, &self.index)
 | 
				
			||||||
	}
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										142
									
								
								src/layout.rs
									
									
									
									
									
								
							
							
						
						
									
										142
									
								
								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<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)]
 | 
					#[derive(Clone, Debug)]
 | 
				
			||||||
pub enum FieldType {
 | 
					pub enum FieldType {
 | 
				
			||||||
@ -15,7 +48,7 @@ pub enum FieldType {
 | 
				
			|||||||
    /// Represents `int64`.
 | 
					    /// Represents `int64`.
 | 
				
			||||||
    I64,
 | 
					    I64,
 | 
				
			||||||
    /// Represents `uint8` or `char`.
 | 
					    /// Represents `uint8` or `char`.
 | 
				
			||||||
    U8(I8Variant),
 | 
					    U8(U8Variant),
 | 
				
			||||||
    /// Represents `uint16`.
 | 
					    /// Represents `uint16`.
 | 
				
			||||||
    U16,
 | 
					    U16,
 | 
				
			||||||
    /// Represents `uint32`.
 | 
					    /// Represents `uint32`.
 | 
				
			||||||
@ -36,6 +69,29 @@ pub enum FieldType {
 | 
				
			|||||||
    Message(MessageLayoutRef),
 | 
					    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)]
 | 
					#[derive(Clone, Debug)]
 | 
				
			||||||
pub struct FieldLayout {
 | 
					pub struct FieldLayout {
 | 
				
			||||||
    pub typ: FieldType,
 | 
					    pub typ: FieldType,
 | 
				
			||||||
@ -43,11 +99,26 @@ pub struct FieldLayout {
 | 
				
			|||||||
    pub multiplicity: Multiplicity,
 | 
					    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)]
 | 
					#[derive(Clone, Debug, PartialEq, Eq)]
 | 
				
			||||||
pub enum Multiplicity {
 | 
					pub enum Multiplicity {
 | 
				
			||||||
	Unit,
 | 
					    Unit,
 | 
				
			||||||
	Fixed(usize),
 | 
					    Fixed(usize),
 | 
				
			||||||
	Dynamic,
 | 
					    Dynamic,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Clone, Debug)]
 | 
					#[derive(Clone, Debug)]
 | 
				
			||||||
@ -56,3 +127,62 @@ pub struct MessageLayout {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub type MessageLayoutRef = std::sync::Arc<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 })
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -5,10 +5,10 @@ use log::trace;
 | 
				
			|||||||
use regex::Regex;
 | 
					use regex::Regex;
 | 
				
			||||||
use ros_message::{MessagePath, Msg};
 | 
					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> {
 | 
					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 path = MessagePath::try_from(message_name)?;
 | 
				
			||||||
    let msgtype = Msg::new(path, msgdef)?;
 | 
					    let msgtype = Msg::new(path, msgdef)?;
 | 
				
			||||||
    Ok(msgtype)
 | 
					    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();
 | 
					    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 msgdefs = &conn.message_definition;
 | 
				
			||||||
    let mut name = conn.datatype.clone();
 | 
					    let mut name = conn.datatype.clone();
 | 
				
			||||||
    let mut begin = 0usize;
 | 
					    let mut begin = 0usize;
 | 
				
			||||||
@ -43,3 +43,8 @@ pub fn parse_msgs(conn: &ConnInfo) -> Result<Vec<Msg>> {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    Ok(msgs)
 | 
					    Ok(msgs)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub fn compute_layout(conn: &ConnInfo) -> Result<MessageLayout> {
 | 
				
			||||||
 | 
					    let msgs = parse_msgs(conn)?;
 | 
				
			||||||
 | 
					    MessageLayout::from_msgs(&msgs)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -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 InputStr<'a> = &'a str;
 | 
				
			||||||
pub type IResultStr<'a, T> = nom::IResult<InputStr<'a>, T, Error<InputStr<'a>>>;
 | 
					pub type IResultStr<'a, T> = nom::IResult<InputStr<'a>, T, Error<InputStr<'a>>>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub type SmallStr = String;
 | 
					pub type SmallStr = crate::SmallStr;
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user