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",
|
"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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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},
|
||||||
|
|
|
@ -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
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