message data header parsing
This commit is contained in:
parent
733d2d61a3
commit
bea68dfa87
@ -25,3 +25,7 @@ thiserror = "1.0.28"
|
|||||||
color-eyre = "0.5.11"
|
color-eyre = "0.5.11"
|
||||||
env_logger = "0.9.0"
|
env_logger = "0.9.0"
|
||||||
eyre = "0.6.5"
|
eyre = "0.6.5"
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
debug = true
|
||||||
|
lto = true
|
@ -1,14 +1,14 @@
|
|||||||
use std::{collections::HashMap, convert::TryFrom, env::args, fs::File, io};
|
use std::{collections::HashMap, convert::TryFrom, env::args, fs::File, io};
|
||||||
|
|
||||||
use eyre::Context;
|
use eyre::{bail, Context};
|
||||||
use log::{error, info, trace};
|
use log::{error, info, trace};
|
||||||
use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
|
use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use ros_message::{MessagePath, Msg};
|
use ros_message::{MessagePath, Msg};
|
||||||
use rsbag::{
|
use rsbag::{
|
||||||
chunk::ChunkHeader,
|
chunk::{ChunkHeader, MessageDataHeader},
|
||||||
index::{BagIndex, ConnInfo, IndexData},
|
index::{BagIndex, ConnInfo, IndexData},
|
||||||
reader::{BagReader, MmapReader},
|
reader::{BagReader, MmapReader, SliceReader},
|
||||||
Result,
|
Result,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -47,11 +47,13 @@ fn parse_message_definitions(conn: &ConnInfo) -> Result<Vec<Msg>> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn read_chunk<R: BagReader>(bag_reader: &mut R, pos: u64) -> Result<Vec<u8>> {
|
fn read_chunk<R: BagReader>(bag_reader: &mut R, pos: u64) -> Result<Vec<u8>> {
|
||||||
let chunk_header = ChunkHeader::read(bag_reader, pos)?;
|
bag_reader.seek(io::SeekFrom::Start(pos))?;
|
||||||
|
|
||||||
|
let chunk_header = ChunkHeader::read(bag_reader)?;
|
||||||
|
|
||||||
let compressed_data = bag_reader.read_data()?;
|
let compressed_data = bag_reader.read_data()?;
|
||||||
let mut data = Vec::with_capacity(chunk_header.uncompressed_size as usize);
|
let mut data = Vec::with_capacity(chunk_header.uncompressed_size as usize);
|
||||||
let mut decompresor = chunk_header.compression.decompress(compressed_data);
|
let mut decompresor = chunk_header.compression.decompress_stream(compressed_data);
|
||||||
io::copy(&mut decompresor, &mut data)?;
|
io::copy(&mut decompresor, &mut data)?;
|
||||||
|
|
||||||
Ok(data)
|
Ok(data)
|
||||||
@ -71,7 +73,6 @@ impl BagInfo {
|
|||||||
}
|
}
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
@ -86,7 +87,7 @@ fn main() -> Result<()> {
|
|||||||
|
|
||||||
let bag_path = &args[1];
|
let bag_path = &args[1];
|
||||||
let bag_file = File::open(bag_path).expect("Could not open bag file");
|
let bag_file = File::open(bag_path).expect("Could not open bag file");
|
||||||
let mut bag_reader = MmapReader::new(bag_file)?;
|
let mut bag_reader = MmapReader::map(bag_file)?;
|
||||||
|
|
||||||
let index = BagIndex::read_all(&mut bag_reader).wrap_err("bag parse error")?;
|
let index = BagIndex::read_all(&mut bag_reader).wrap_err("bag parse error")?;
|
||||||
|
|
||||||
@ -108,45 +109,54 @@ fn main() -> Result<()> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let data = index
|
let info = index
|
||||||
.chunks
|
.chunks
|
||||||
.par_iter()
|
.par_iter()
|
||||||
.try_fold(BagInfo::default, |mut data, chunk| -> rsbag::Result<_> {
|
// .try_fold(BagInfo::default, |mut info, chunk| -> rsbag::Result<_> {
|
||||||
|
// let mut reader = bag_reader.clone();
|
||||||
|
// let chunk_header = ChunkHeader::read(&mut reader, chunk.pos)?;
|
||||||
|
// info.total_uncompressed += chunk_header.uncompressed_size as u64;
|
||||||
|
// reader.skip_data()?;
|
||||||
|
// for _ in &chunk.connections {
|
||||||
|
// let index = IndexData::read(&mut reader)?;
|
||||||
|
// *info.per_connection.entry(index.conn_id).or_insert(0) +=
|
||||||
|
// index.entries.len() as u64;
|
||||||
|
// }
|
||||||
|
// Ok(info)
|
||||||
|
// })
|
||||||
|
.try_fold(BagInfo::default, |mut info, chunk| -> rsbag::Result<_> {
|
||||||
let mut reader = bag_reader.clone();
|
let mut reader = bag_reader.clone();
|
||||||
let chunk_header = ChunkHeader::read(&mut reader, chunk.pos)?;
|
|
||||||
data.total_uncompressed += chunk_header.uncompressed_size as u64;
|
|
||||||
reader.skip_data()?;
|
|
||||||
|
|
||||||
for _ in &chunk.connections {
|
let data = read_chunk(&mut reader, chunk.pos)
|
||||||
let index = IndexData::read(&mut reader)?;
|
.wrap_err_with(|| format!("failed to read chunk: {:#?}", chunk))?;
|
||||||
*data.per_connection.entry(index.conn_id).or_insert(0) +=
|
info.total_uncompressed += data.len() as u64;
|
||||||
index.entries.len() as u64;
|
|
||||||
|
let mut chunk_reader = SliceReader::from(data);
|
||||||
|
|
||||||
|
loop {
|
||||||
|
if chunk_reader.pos() == chunk_reader.as_ref().len() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
let header = chunk_reader.read_header()?;
|
||||||
|
let op = header.read_op()?;
|
||||||
|
match op {
|
||||||
|
rsbag::parse::Op::MsgData => {
|
||||||
|
let header = MessageDataHeader::from_header(header)?;
|
||||||
|
let count = info.per_connection.entry(header.conn_id).or_insert(0);
|
||||||
|
*count += 1;
|
||||||
|
chunk_reader.skip_data()?;
|
||||||
|
}
|
||||||
|
rsbag::parse::Op::Connection => chunk_reader.skip_data()?,
|
||||||
|
_ => bail!("unexpected op in chunk: {:?}", op),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// let data = read_chunk(&mut bag_reader.clone(), chunk.pos)?;
|
Ok(info)
|
||||||
// chunks.push(data);
|
|
||||||
Ok(data)
|
|
||||||
})
|
})
|
||||||
.try_reduce(BagInfo::default, |a, b| Ok(a.combine(b)))
|
.try_reduce(BagInfo::default, |a, b| Ok(a.combine(b)))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
info!("bag data: {:#?}", data);
|
info!("bag info: {:#?}", info);
|
||||||
|
|
||||||
// let total_size = index
|
|
||||||
// .chunks
|
|
||||||
// .par_iter()
|
|
||||||
// .try_fold(
|
|
||||||
// || 0u64,
|
|
||||||
// |total_size, chunk| -> Result<_> {
|
|
||||||
// let data = read_chunk(&mut bag_reader.clone(), chunk.pos)?;
|
|
||||||
// Ok(total_size + data.len() as u64)
|
|
||||||
// },
|
|
||||||
// )
|
|
||||||
// .reduce(
|
|
||||||
// // || Ok(Vec::new()),
|
|
||||||
// || Ok(0),
|
|
||||||
// |a, b| a.and_then(|a| b.map(|b| a + b)),
|
|
||||||
// )?;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
30
src/chunk.rs
30
src/chunk.rs
@ -7,7 +7,7 @@ use eyre::bail;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
error,
|
error,
|
||||||
parse::{Header, Op},
|
parse::{Header, Op, Time},
|
||||||
reader::BagReader,
|
reader::BagReader,
|
||||||
Error, Result,
|
Error, Result,
|
||||||
};
|
};
|
||||||
@ -33,7 +33,7 @@ impl FromStr for Compression {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Compression {
|
impl Compression {
|
||||||
pub fn decompress<'a, R: io::Read + 'a>(self, read: R) -> Box<dyn io::Read + 'a> {
|
pub fn decompress_stream<'a, R: io::Read + 'a>(self, read: R) -> Box<dyn io::Read + 'a> {
|
||||||
match self {
|
match self {
|
||||||
Compression::None => Box::new(read),
|
Compression::None => Box::new(read),
|
||||||
Compression::Bz2 => todo!("bz2 decompression"),
|
Compression::Bz2 => todo!("bz2 decompression"),
|
||||||
@ -49,16 +49,34 @@ pub struct ChunkHeader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ChunkHeader {
|
impl ChunkHeader {
|
||||||
pub fn read<R: BagReader>(reader: &mut R, pos: u64) -> Result<ChunkHeader> {
|
pub fn read<R: BagReader>(reader: &mut R) -> Result<Self> {
|
||||||
reader.seek(SeekFrom::Start(pos))?;
|
|
||||||
let header = reader.read_header_op(Op::Chunk)?;
|
let header = reader.read_header_op(Op::Chunk)?;
|
||||||
ChunkHeader::from_header(header)
|
Self::from_header(header)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_header(header: Header) -> Result<Self> {
|
pub fn from_header(header: Header) -> Result<Self> {
|
||||||
Ok(ChunkHeader {
|
Ok(Self {
|
||||||
compression: header.read_string(b"compression")?.parse()?,
|
compression: header.read_string(b"compression")?.parse()?,
|
||||||
uncompressed_size: header.read_u32(b"size")?,
|
uncompressed_size: header.read_u32(b"size")?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct MessageDataHeader {
|
||||||
|
pub conn_id: u32,
|
||||||
|
pub time: Time,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MessageDataHeader {
|
||||||
|
pub fn read<R: BagReader>(reader: &mut R) -> Result<Self> {
|
||||||
|
let header = reader.read_header_op(Op::MsgData)?;
|
||||||
|
Self::from_header(header)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_header(header: Header) -> Result<Self> {
|
||||||
|
Ok(Self {
|
||||||
|
conn_id: header.read_u32(b"conn")?,
|
||||||
|
time: header.read_time(b"time")?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -6,7 +6,7 @@ use nom::{number::streaming::le_u32, sequence::tuple, Parser};
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
error,
|
error,
|
||||||
parse::{self, header::Time, Header, Op},
|
parse::{self, Header, Op, Time},
|
||||||
reader::BagReader,
|
reader::BagReader,
|
||||||
Result,
|
Result,
|
||||||
};
|
};
|
||||||
|
@ -3,7 +3,7 @@ pub mod header;
|
|||||||
mod version;
|
mod version;
|
||||||
|
|
||||||
pub use error::{Error, ErrorKind};
|
pub use error::{Error, ErrorKind};
|
||||||
pub use header::{Header, Op};
|
pub use header::{Header, Op, Time};
|
||||||
pub use version::Version;
|
pub use version::Version;
|
||||||
|
|
||||||
pub type Input<'a> = &'a [u8];
|
pub type Input<'a> = &'a [u8];
|
||||||
|
@ -10,16 +10,13 @@ pub mod error;
|
|||||||
mod io;
|
mod io;
|
||||||
#[cfg(feature = "mmap")]
|
#[cfg(feature = "mmap")]
|
||||||
mod mmap;
|
mod mmap;
|
||||||
|
mod slice;
|
||||||
|
|
||||||
pub use self::io::IoReader;
|
|
||||||
#[cfg(feature = "mmap")]
|
#[cfg(feature = "mmap")]
|
||||||
pub use self::mmap::MmapReader;
|
pub use self::mmap::MmapReader;
|
||||||
|
pub use self::{io::IoReader, slice::SliceReader};
|
||||||
|
|
||||||
pub trait BagReader {
|
pub trait BagReader {
|
||||||
type Read: std::io::Read;
|
|
||||||
|
|
||||||
fn as_read(&mut self) -> &mut Self::Read;
|
|
||||||
|
|
||||||
fn read_parser<'a, O: 'a, P>(&'a mut self, parser: P) -> Result<O>
|
fn read_parser<'a, O: 'a, P>(&'a mut self, parser: P) -> Result<O>
|
||||||
where
|
where
|
||||||
P: nom::Parser<&'a [u8], O, parse::Error<&'a [u8]>>;
|
P: nom::Parser<&'a [u8], O, parse::Error<&'a [u8]>>;
|
||||||
|
@ -38,12 +38,6 @@ impl<R: io::Read + io::Seek> IoReader<R> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<R: io::Read + io::Seek> BagReader for IoReader<R> {
|
impl<R: io::Read + io::Seek> BagReader for IoReader<R> {
|
||||||
type Read = R;
|
|
||||||
|
|
||||||
fn as_read(&mut self) -> &mut Self::Read {
|
|
||||||
&mut self.read
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read_parser<'a, O: 'a, P>(&'a mut self, mut parser: P) -> Result<O>
|
fn read_parser<'a, O: 'a, P>(&'a mut self, mut parser: P) -> Result<O>
|
||||||
where
|
where
|
||||||
P: nom::Parser<&'a [u8], O, parse::Error<&'a [u8]>>,
|
P: nom::Parser<&'a [u8], O, parse::Error<&'a [u8]>>,
|
||||||
|
@ -1,75 +1,17 @@
|
|||||||
use std::{fs::File, io, sync::Arc};
|
use std::{fs::File, sync::Arc};
|
||||||
|
|
||||||
use eyre::bail;
|
|
||||||
use memmap::Mmap;
|
use memmap::Mmap;
|
||||||
|
|
||||||
use super::{error::UnexpectedEof, BagReader};
|
use super::SliceReader;
|
||||||
use crate::{parse, Result};
|
use crate::Result;
|
||||||
|
|
||||||
#[derive(Clone)]
|
pub type MmapReader = SliceReader<Arc<Mmap>>;
|
||||||
pub struct MmapReader {
|
|
||||||
mmap: Arc<Mmap>,
|
|
||||||
pos: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MmapReader {
|
impl MmapReader {
|
||||||
pub fn new(file: File) -> Result<Self> {
|
pub fn map(file: File) -> Result<Self> {
|
||||||
// Safety: ¯\_(ツ)_/¯
|
// Safety: ¯\_(ツ)_/¯
|
||||||
let mmap = unsafe { Mmap::map(&file) }?;
|
let mmap = unsafe { Mmap::map(&file) }?;
|
||||||
let mmap = Arc::new(mmap);
|
let mmap = Arc::new(mmap);
|
||||||
Ok(Self { mmap, pos: 0 })
|
Ok(SliceReader::from(mmap))
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl io::Read for MmapReader {
|
|
||||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
|
||||||
if let Some(slice) = self
|
|
||||||
.mmap
|
|
||||||
.get(self.pos..)
|
|
||||||
.and_then(|unread| unread.get(..buf.len()))
|
|
||||||
{
|
|
||||||
buf.copy_from_slice(slice);
|
|
||||||
Ok(slice.len())
|
|
||||||
} else {
|
|
||||||
Ok(0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BagReader for MmapReader {
|
|
||||||
type Read = Self;
|
|
||||||
|
|
||||||
fn as_read(&mut self) -> &mut Self::Read {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read_parser<'a, O: 'a, P>(&'a mut self, mut parser: P) -> Result<O>
|
|
||||||
where
|
|
||||||
P: nom::Parser<&'a [u8], O, parse::Error<&'a [u8]>>,
|
|
||||||
{
|
|
||||||
let input = self.mmap.get(self.pos..).ok_or(UnexpectedEof)?;
|
|
||||||
match parser.parse(input) {
|
|
||||||
Ok((rest, output)) => {
|
|
||||||
self.pos += input.len() - rest.len();
|
|
||||||
Ok(output)
|
|
||||||
}
|
|
||||||
Err(nom::Err::Incomplete(_)) => bail!(UnexpectedEof),
|
|
||||||
Err(nom::Err::Error(e) | nom::Err::Failure(e)) => Err(e.into_owned().into()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn seek(&mut self, pos: io::SeekFrom) -> Result<()> {
|
|
||||||
match pos {
|
|
||||||
io::SeekFrom::Start(pos) => {
|
|
||||||
self.pos = pos as usize;
|
|
||||||
}
|
|
||||||
io::SeekFrom::End(pos) => {
|
|
||||||
self.pos = (self.mmap.len() as i64 + pos) as usize;
|
|
||||||
}
|
|
||||||
io::SeekFrom::Current(pos) => {
|
|
||||||
self.pos = ((self.pos as i64) + pos) as usize;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
71
src/reader/slice.rs
Normal file
71
src/reader/slice.rs
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
use std::{io, ops::Deref};
|
||||||
|
|
||||||
|
use eyre::bail;
|
||||||
|
|
||||||
|
use super::{error::UnexpectedEof, BagReader};
|
||||||
|
use crate::{parse, Result};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct SliceReader<T> {
|
||||||
|
slice: T,
|
||||||
|
pos: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> AsRef<T> for SliceReader<T> {
|
||||||
|
fn as_ref(&self) -> &T {
|
||||||
|
&self.slice
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> SliceReader<T> {
|
||||||
|
pub fn into_inner(self) -> T {
|
||||||
|
self.slice
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pos(&self) -> usize {
|
||||||
|
self.pos
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> From<T> for SliceReader<T> {
|
||||||
|
fn from(slice: T) -> Self {
|
||||||
|
Self { slice, pos: 0 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, U> BagReader for SliceReader<T>
|
||||||
|
where
|
||||||
|
T: Deref<Target = U>,
|
||||||
|
U: AsRef<[u8]> + ?Sized + 'static,
|
||||||
|
{
|
||||||
|
fn read_parser<'a, O: 'a, P>(&'a mut self, mut parser: P) -> Result<O>
|
||||||
|
where
|
||||||
|
P: nom::Parser<&'a [u8], O, parse::Error<&'a [u8]>>,
|
||||||
|
{
|
||||||
|
let slice = self.slice.deref().as_ref();
|
||||||
|
let input = slice.get(self.pos..).ok_or(UnexpectedEof)?;
|
||||||
|
match parser.parse(input) {
|
||||||
|
Ok((rest, output)) => {
|
||||||
|
self.pos += input.len() - rest.len();
|
||||||
|
Ok(output)
|
||||||
|
}
|
||||||
|
Err(nom::Err::Incomplete(_)) => bail!(UnexpectedEof),
|
||||||
|
Err(nom::Err::Error(e) | nom::Err::Failure(e)) => Err(e.into_owned().into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn seek(&mut self, pos: io::SeekFrom) -> Result<()> {
|
||||||
|
match pos {
|
||||||
|
io::SeekFrom::Start(pos) => {
|
||||||
|
self.pos = pos as usize;
|
||||||
|
}
|
||||||
|
io::SeekFrom::End(pos) => {
|
||||||
|
self.pos = (self.slice.as_ref().len() as i64 + pos) as usize;
|
||||||
|
}
|
||||||
|
io::SeekFrom::Current(pos) => {
|
||||||
|
self.pos = ((self.pos as i64) + pos) as usize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user