gstreamer/
slice.rs
1use std::{
4 fmt,
5 ops::{Bound, RangeBounds},
6};
7
8pub trait ByteSliceExt {
9 #[doc(alias = "gst_util_dump_mem")]
10 fn dump(&self) -> Dump;
11 #[doc(alias = "gst_util_dump_mem")]
12 fn dump_range(&self, range: impl RangeBounds<usize>) -> Dump;
13}
14
15impl<T: AsRef<[u8]>> ByteSliceExt for T {
16 fn dump(&self) -> Dump {
17 self.dump_range(..)
18 }
19
20 fn dump_range(&self, range: impl RangeBounds<usize>) -> Dump {
21 Dump {
22 data: self.as_ref(),
23 start: range.start_bound().cloned(),
24 end: range.end_bound().cloned(),
25 }
26 }
27}
28
29pub struct Dump<'a> {
30 pub(crate) data: &'a [u8],
31 pub(crate) start: Bound<usize>,
32 pub(crate) end: Bound<usize>,
33}
34
35impl Dump<'_> {
36 fn fmt(&self, f: &mut fmt::Formatter, debug: bool) -> fmt::Result {
37 use std::fmt::Write;
38
39 let data = self.data;
40 let len = data.len();
41
42 let mut start_idx = match self.start {
45 Bound::Included(idx) if idx >= len => {
46 write!(f, "<start out of range>")?;
47 return Ok(());
48 }
49 Bound::Excluded(idx) if idx.checked_add(1).map_or(true, |idx| idx >= len) => {
50 write!(f, "<start out of range>")?;
51 return Ok(());
52 }
53 Bound::Included(idx) => idx,
54 Bound::Excluded(idx) => idx + 1,
55 Bound::Unbounded => 0,
56 };
57
58 let end_idx = match self.end {
59 Bound::Included(idx) if idx.checked_add(1).map_or(true, |idx| idx > len) => {
60 write!(f, "<end out of range>")?;
61 return Ok(());
62 }
63 Bound::Excluded(idx) if idx > len => {
64 write!(f, "<end out of range>")?;
65 return Ok(());
66 }
67 Bound::Included(idx) => idx + 1,
68 Bound::Excluded(idx) => idx,
69 Bound::Unbounded => len,
70 };
71
72 if start_idx >= end_idx {
73 write!(f, "<empty range>")?;
74 return Ok(());
75 }
76
77 let data = &data[start_idx..end_idx];
78
79 if debug {
80 for line in data.chunks(16) {
81 match end_idx {
82 0x00_00..=0xff_ff => write!(f, "{:04x}: ", start_idx)?,
83 0x01_00_00..=0xff_ff_ff => write!(f, "{:06x}: ", start_idx)?,
84 0x01_00_00_00..=0xff_ff_ff_ff => write!(f, "{:08x}: ", start_idx)?,
85 _ => write!(f, "{:016x}: ", start_idx)?,
86 }
87
88 for (i, v) in line.iter().enumerate() {
89 if i > 0 {
90 write!(f, " {:02x}", v)?;
91 } else {
92 write!(f, "{:02x}", v)?;
93 }
94 }
95
96 for _ in line.len()..16 {
97 write!(f, " ")?;
98 }
99 write!(f, " ")?;
100
101 for v in line {
102 if v.is_ascii() && !v.is_ascii_control() {
103 f.write_char((*v).into())?;
104 } else {
105 f.write_char('.')?;
106 }
107 }
108
109 start_idx = start_idx.saturating_add(16);
110 if start_idx < end_idx {
111 writeln!(f)?;
112 }
113 }
114
115 Ok(())
116 } else {
117 for line in data.chunks(16) {
118 for (i, v) in line.iter().enumerate() {
119 if i > 0 {
120 write!(f, " {:02x}", v)?;
121 } else {
122 write!(f, "{:02x}", v)?;
123 }
124 }
125
126 start_idx = start_idx.saturating_add(16);
127 if start_idx < end_idx {
128 writeln!(f)?;
129 }
130 }
131
132 Ok(())
133 }
134 }
135}
136
137impl fmt::Display for Dump<'_> {
138 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
139 self.fmt(f, false)
140 }
141}
142
143impl fmt::Debug for Dump<'_> {
144 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
145 self.fmt(f, true)
146 }
147}
148
149#[cfg(test)]
150mod tests {
151 use super::ByteSliceExt;
152
153 #[test]
154 fn dump_u8_slice() {
155 let mut b: [u8; 4] = [1u8, 2, 3, 4];
156 let _ = b.dump();
157 let b1 = b.as_slice();
158 let _ = b1.dump();
159 let b1 = b.as_mut_slice();
160 let _ = b1.dump();
161 }
162
163 #[test]
164 fn dump_u8_vec() {
165 let mut b = vec![1u8, 2, 3, 4];
166 let _ = b.dump();
167 let b1 = b.as_slice();
168 let _ = b1.dump();
169 let b1 = b.as_mut_slice();
170 let _ = b1.dump();
171 }
172}