forked from mirrors/gstreamer-rs
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:
parent
34fee6b691
commit
e72a3bfc8d
5 changed files with 220 additions and 22 deletions
7
Cargo.lock
generated
7
Cargo.lock
generated
|
@ -845,7 +845,6 @@ dependencies = [
|
|||
"option-operations",
|
||||
"paste",
|
||||
"pin-project-lite",
|
||||
"pretty-hex",
|
||||
"ron",
|
||||
"serde",
|
||||
"serde_bytes",
|
||||
|
@ -2017,12 +2016,6 @@ dependencies = [
|
|||
"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]]
|
||||
name = "proc-macro-crate"
|
||||
version = "2.0.1"
|
||||
|
|
|
@ -28,7 +28,6 @@ opt-ops = { package = "option-operations", version = "0.5" }
|
|||
serde = { version = "1.0", optional = true, features = ["derive"] }
|
||||
serde_bytes = { version = "0.11", optional = true }
|
||||
paste = "1.0"
|
||||
pretty-hex = "0.4"
|
||||
thiserror = "1"
|
||||
smallvec = { version = "1.0", features = ["write"] }
|
||||
itertools = "0.12"
|
||||
|
|
|
@ -125,6 +125,7 @@ mod memory_wrapped;
|
|||
pub use crate::memory::{MappedMemory, Memory, MemoryMap, MemoryRef};
|
||||
#[cfg(feature = "serde")]
|
||||
mod buffer_serde;
|
||||
pub mod slice;
|
||||
|
||||
pub mod sample;
|
||||
pub use crate::sample::{Sample, SampleRef};
|
||||
|
@ -342,6 +343,7 @@ pub mod prelude {
|
|||
param_spec::GstParamSpecBuilderExt,
|
||||
pipeline::GstPipelineExtManual,
|
||||
plugin_feature::PluginFeatureExtManual,
|
||||
slice::Dump,
|
||||
tag_setter::TagSetterExtManual,
|
||||
tags::{CustomTag, Tag},
|
||||
task_pool::{TaskHandle, TaskPoolExtManual},
|
||||
|
|
|
@ -4,7 +4,7 @@ use std::{
|
|||
fmt,
|
||||
marker::PhantomData,
|
||||
mem,
|
||||
ops::{Deref, DerefMut},
|
||||
ops::{Bound, Deref, DerefMut, RangeBounds},
|
||||
ptr, slice,
|
||||
};
|
||||
|
||||
|
@ -284,8 +284,22 @@ impl MemoryRef {
|
|||
unsafe { ffi::gst_memory_resize(self.as_mut_ptr(), offset, size) }
|
||||
}
|
||||
|
||||
pub fn dump(&self, size: Option<usize>) -> Dump {
|
||||
Dump { memory: self, size }
|
||||
#[doc(alias = "gst_util_dump_mem")]
|
||||
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> {
|
||||
memory: &'a MemoryRef,
|
||||
size: Option<usize>,
|
||||
start: Bound<usize>,
|
||||
end: Bound<usize>,
|
||||
}
|
||||
|
||||
impl<'a> Dump<'a> {
|
||||
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 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 {
|
||||
write!(f, "{:?}", data.hex_dump())
|
||||
<crate::slice::Dump as fmt::Debug>::fmt(&dump, f)
|
||||
} else {
|
||||
write!(f, "{}", data.hex_dump())
|
||||
<crate::slice::Dump as fmt::Display>::fmt(&dump, f)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -920,16 +937,56 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_dump() {
|
||||
use std::fmt::Write;
|
||||
|
||||
crate::init().unwrap();
|
||||
|
||||
let mut s = String::new();
|
||||
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]);
|
||||
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]);
|
||||
dbg!(mem.dump(None));
|
||||
let mem = crate::Memory::from_slice(vec![0; 19]);
|
||||
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]
|
||||
|
|
147
gstreamer/src/slice.rs
Normal file
147
gstreamer/src/slice.rs
Normal 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)
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue