gstreamer: Improve support for dumping memories and add same functionality to byte slices

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/-/merge_requests/1362>
This commit is contained in:
Sebastian Dröge 2023-12-10 16:22:27 +02:00 committed by GStreamer Marge Bot
parent 34fee6b691
commit e72a3bfc8d
5 changed files with 220 additions and 22 deletions

7
Cargo.lock generated
View file

@ -845,7 +845,6 @@ dependencies = [
"option-operations", "option-operations",
"paste", "paste",
"pin-project-lite", "pin-project-lite",
"pretty-hex",
"ron", "ron",
"serde", "serde",
"serde_bytes", "serde_bytes",
@ -2017,12 +2016,6 @@ dependencies = [
"windows-sys 0.52.0", "windows-sys 0.52.0",
] ]
[[package]]
name = "pretty-hex"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23c6b968ed37d62e35b4febaba13bfa231b0b7929d68b8a94e65445a17e2d35f"
[[package]] [[package]]
name = "proc-macro-crate" name = "proc-macro-crate"
version = "2.0.1" version = "2.0.1"

View file

@ -28,7 +28,6 @@ opt-ops = { package = "option-operations", version = "0.5" }
serde = { version = "1.0", optional = true, features = ["derive"] } serde = { version = "1.0", optional = true, features = ["derive"] }
serde_bytes = { version = "0.11", optional = true } serde_bytes = { version = "0.11", optional = true }
paste = "1.0" paste = "1.0"
pretty-hex = "0.4"
thiserror = "1" thiserror = "1"
smallvec = { version = "1.0", features = ["write"] } smallvec = { version = "1.0", features = ["write"] }
itertools = "0.12" itertools = "0.12"

View file

@ -125,6 +125,7 @@ mod memory_wrapped;
pub use crate::memory::{MappedMemory, Memory, MemoryMap, MemoryRef}; pub use crate::memory::{MappedMemory, Memory, MemoryMap, MemoryRef};
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
mod buffer_serde; mod buffer_serde;
pub mod slice;
pub mod sample; pub mod sample;
pub use crate::sample::{Sample, SampleRef}; pub use crate::sample::{Sample, SampleRef};
@ -342,6 +343,7 @@ pub mod prelude {
param_spec::GstParamSpecBuilderExt, param_spec::GstParamSpecBuilderExt,
pipeline::GstPipelineExtManual, pipeline::GstPipelineExtManual,
plugin_feature::PluginFeatureExtManual, plugin_feature::PluginFeatureExtManual,
slice::Dump,
tag_setter::TagSetterExtManual, tag_setter::TagSetterExtManual,
tags::{CustomTag, Tag}, tags::{CustomTag, Tag},
task_pool::{TaskHandle, TaskPoolExtManual}, task_pool::{TaskHandle, TaskPoolExtManual},

View file

@ -4,7 +4,7 @@ use std::{
fmt, fmt,
marker::PhantomData, marker::PhantomData,
mem, mem,
ops::{Deref, DerefMut}, ops::{Bound, Deref, DerefMut, RangeBounds},
ptr, slice, ptr, slice,
}; };
@ -284,8 +284,22 @@ impl MemoryRef {
unsafe { ffi::gst_memory_resize(self.as_mut_ptr(), offset, size) } unsafe { ffi::gst_memory_resize(self.as_mut_ptr(), offset, size) }
} }
pub fn dump(&self, size: Option<usize>) -> Dump { #[doc(alias = "gst_util_dump_mem")]
Dump { memory: self, size } pub fn dump(&self) -> Dump {
Dump {
memory: self,
start: Bound::Unbounded,
end: Bound::Unbounded,
}
}
#[doc(alias = "gst_util_dump_mem")]
pub fn dump_range(&self, range: impl RangeBounds<usize>) -> Dump {
Dump {
memory: self,
start: range.start_bound().cloned(),
end: range.end_bound().cloned(),
}
} }
} }
@ -478,22 +492,25 @@ unsafe impl<T> Sync for MappedMemory<T> {}
pub struct Dump<'a> { pub struct Dump<'a> {
memory: &'a MemoryRef, memory: &'a MemoryRef,
size: Option<usize>, start: Bound<usize>,
end: Bound<usize>,
} }
impl<'a> Dump<'a> { impl<'a> Dump<'a> {
fn fmt(&self, f: &mut fmt::Formatter, debug: bool) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter, debug: bool) -> fmt::Result {
use pretty_hex::*;
let map = self.memory.map_readable().expect("Failed to map memory"); let map = self.memory.map_readable().expect("Failed to map memory");
let data = map.as_slice(); let data = map.as_slice();
let size = self.size.unwrap_or_else(|| self.memory.size());
let data = &data[0..size]; let dump = crate::slice::Dump {
data,
start: self.start,
end: self.end,
};
if debug { if debug {
write!(f, "{:?}", data.hex_dump()) <crate::slice::Dump as fmt::Debug>::fmt(&dump, f)
} else { } else {
write!(f, "{}", data.hex_dump()) <crate::slice::Dump as fmt::Display>::fmt(&dump, f)
} }
} }
} }
@ -920,16 +937,56 @@ mod tests {
#[test] #[test]
fn test_dump() { fn test_dump() {
use std::fmt::Write;
crate::init().unwrap(); crate::init().unwrap();
let mut s = String::new();
let mem = crate::Memory::from_slice(vec![1, 2, 3, 4]); let mem = crate::Memory::from_slice(vec![1, 2, 3, 4]);
println!("{}", mem.dump(Some(mem.size()))); write!(&mut s, "{:?}", mem.dump()).unwrap();
assert_eq!(
s,
"0000: 01 02 03 04 ...."
);
s.clear();
write!(&mut s, "{}", mem.dump()).unwrap();
assert_eq!(s, "01 02 03 04");
s.clear();
let mem = crate::Memory::from_slice(vec![1, 2, 3, 4]); let mem = crate::Memory::from_slice(vec![1, 2, 3, 4]);
println!("{:?}", mem.dump(Some(2))); write!(&mut s, "{:?}", mem.dump_range(..)).unwrap();
assert_eq!(
s,
"0000: 01 02 03 04 ...."
);
s.clear();
write!(&mut s, "{:?}", mem.dump_range(..2)).unwrap();
assert_eq!(
s,
"0000: 01 02 .."
);
s.clear();
write!(&mut s, "{:?}", mem.dump_range(2..=3)).unwrap();
assert_eq!(
s,
"0002: 03 04 .."
);
s.clear();
write!(&mut s, "{:?}", mem.dump_range(..100)).unwrap();
assert_eq!(s, "<end out of range>",);
s.clear();
write!(&mut s, "{:?}", mem.dump_range(90..100)).unwrap();
assert_eq!(s, "<start out of range>",);
s.clear();
let mem = crate::Memory::from_slice(vec![0; 64]); let mem = crate::Memory::from_slice(vec![0; 19]);
dbg!(mem.dump(None)); write!(&mut s, "{:?}", mem.dump()).unwrap();
assert_eq!(
s,
"0000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................\n\
0010: 00 00 00 ..."
);
s.clear();
} }
#[test] #[test]

147
gstreamer/src/slice.rs Normal file
View file

@ -0,0 +1,147 @@
// Take a look at the license at the top of the repository in the LICENSE file.
use std::{
fmt,
ops::{Bound, RangeBounds},
};
pub trait ByteSliceExt {
#[doc(alias = "gst_util_dump_mem")]
fn dump(&self) -> Dump;
#[doc(alias = "gst_util_dump_mem")]
fn dump_range(&self, range: impl RangeBounds<usize>) -> Dump;
}
impl<'a> ByteSliceExt for &'a [u8] {
fn dump(&self) -> Dump {
self.dump_range(..)
}
fn dump_range(&self, range: impl RangeBounds<usize>) -> Dump {
Dump {
data: self,
start: range.start_bound().cloned(),
end: range.end_bound().cloned(),
}
}
}
pub struct Dump<'a> {
pub(crate) data: &'a [u8],
pub(crate) start: Bound<usize>,
pub(crate) end: Bound<usize>,
}
impl<'a> Dump<'a> {
fn fmt(&self, f: &mut fmt::Formatter, debug: bool) -> fmt::Result {
use std::fmt::Write;
let data = self.data;
let len = data.len();
// Kind of re-implementation of slice indexing to allow handling out of range values better
// with specific output strings
let mut start_idx = match self.start {
Bound::Included(idx) if idx >= len => {
write!(f, "<start out of range>")?;
return Ok(());
}
Bound::Excluded(idx) if idx.checked_add(1).map_or(true, |idx| idx >= len) => {
write!(f, "<start out of range>")?;
return Ok(());
}
Bound::Included(idx) => idx,
Bound::Excluded(idx) => idx + 1,
Bound::Unbounded => 0,
};
let end_idx = match self.end {
Bound::Included(idx) if idx.checked_add(1).map_or(true, |idx| idx > len) => {
write!(f, "<end out of range>")?;
return Ok(());
}
Bound::Excluded(idx) if idx > len => {
write!(f, "<end out of range>")?;
return Ok(());
}
Bound::Included(idx) => idx + 1,
Bound::Excluded(idx) => idx,
Bound::Unbounded => len,
};
if start_idx >= end_idx {
write!(f, "<empty range>")?;
return Ok(());
}
let data = &data[start_idx..end_idx];
if debug {
for line in data.chunks(16) {
match end_idx {
0x00_00..=0xff_ff => write!(f, "{:04x}: ", start_idx)?,
0x01_00_00..=0xff_ff_ff => write!(f, "{:06x}: ", start_idx)?,
0x01_00_00_00..=0xff_ff_ff_ff => write!(f, "{:08x}: ", start_idx)?,
_ => write!(f, "{:016x}: ", start_idx)?,
}
for (i, v) in line.iter().enumerate() {
if i > 0 {
write!(f, " {:02x}", v)?;
} else {
write!(f, "{:02x}", v)?;
}
}
for _ in line.len()..16 {
write!(f, " ")?;
}
write!(f, " ")?;
for v in line {
if v.is_ascii() && !v.is_ascii_control() {
f.write_char((*v).into())?;
} else {
f.write_char('.')?;
}
}
start_idx = start_idx.saturating_add(16);
if start_idx < end_idx {
writeln!(f)?;
}
}
Ok(())
} else {
for line in data.chunks(16) {
for (i, v) in line.iter().enumerate() {
if i > 0 {
write!(f, " {:02x}", v)?;
} else {
write!(f, "{:02x}", v)?;
}
}
start_idx = start_idx.saturating_add(16);
if start_idx < end_idx {
writeln!(f)?;
}
}
Ok(())
}
}
}
impl<'a> fmt::Display for Dump<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.fmt(f, false)
}
}
impl<'a> fmt::Debug for Dump<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.fmt(f, true)
}
}