1#[derive(Default, Clone)]
34pub struct DgramBuffer {
35 data: Vec<u8>,
36 start: usize,
37}
38
39impl DgramBuffer {
40 pub fn new() -> Self {
42 Default::default()
43 }
44
45 pub fn from_slice(data: &[u8]) -> Self {
48 DgramBuffer {
49 data: data.into(),
50 start: 0,
51 }
52 }
53
54 pub fn with_capacity(capacity: usize) -> Self {
57 DgramBuffer {
58 data: Vec::with_capacity(capacity),
59 start: 0,
60 }
61 }
62
63 pub fn with_capacity_and_headroom(capacity: usize, headroom: usize) -> Self {
70 assert!(capacity >= headroom);
71 let mut v = Vec::with_capacity(capacity);
72 v.resize(headroom, 0);
73 DgramBuffer {
74 data: v,
75 start: headroom,
76 }
77 }
78
79 pub fn from_vec_with_headroom(v: Vec<u8>, headroom: usize) -> Self {
83 assert!(headroom <= v.len());
84 DgramBuffer {
85 data: v,
86 start: headroom,
87 }
88 }
89
90 pub fn truncate(&mut self, count: usize) {
94 self.data.truncate(self.start + count);
95 }
96
97 pub fn advance(&mut self, count: usize) {
100 assert!(self.start + count <= self.data.len());
101 self.start += count;
102 }
103
104 #[allow(
108 clippy::result_unit_err,
109 reason = "There is only a single error case, adding a custom error type doesn't make sense"
110 )]
111 pub fn try_add_prefix(&mut self, prefix: &[u8]) -> Result<(), ()> {
112 if self.start < prefix.len() {
113 return Err(());
114 }
115
116 self.start -= prefix.len();
117 self.data[self.start..self.start + prefix.len()].copy_from_slice(prefix);
118 Ok(())
119 }
120
121 pub fn splice_headroom(&mut self, headroom: usize) {
125 if self.start >= headroom {
126 return;
127 }
128 self.data
129 .splice(0..self.start, std::iter::repeat_n(0u8, headroom));
130 self.start = headroom;
131 }
132
133 pub fn clear(&mut self) {
135 self.start = 0;
136 self.data.clear();
137 }
138
139 pub fn len(&self) -> usize {
141 self.data.len() - self.start
142 }
143
144 pub fn is_empty(&self) -> bool {
146 self.len() == 0
147 }
148
149 pub fn as_slice(&self) -> &[u8] {
151 &self.data[self.start..]
152 }
153
154 pub fn as_mut_slice(&mut self) -> &mut [u8] {
156 &mut self.data[self.start..]
157 }
158
159 pub fn spare_capacity(&self) -> usize {
162 self.data.capacity() - self.data.len()
163 }
164
165 pub fn into_parts(self) -> (Vec<u8>, usize) {
168 (self.data, self.start)
169 }
170}
171
172impl AsRef<[u8]> for DgramBuffer {
173 fn as_ref(&self) -> &[u8] {
174 self.as_slice()
175 }
176}
177
178impl AsMut<[u8]> for DgramBuffer {
179 fn as_mut(&mut self) -> &mut [u8] {
180 self.as_mut_slice()
181 }
182}
183
184impl std::fmt::Debug for DgramBuffer {
185 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
186 if self.is_empty() {
187 return f.write_str("[]");
188 }
189
190 write!(f, "[0x")?;
191 for (i, byte) in self.as_slice().iter().enumerate() {
194 if i > 0 && i % 4 == 0 {
195 f.write_str("_")?;
196 }
197 write!(f, "{:02x}", byte)?;
198 }
199 write!(f, ", len={}]", self.len())?;
200
201 Ok(())
202 }
203}
204
205unsafe impl bytes::BufMut for DgramBuffer {
208 fn remaining_mut(&self) -> usize {
209 self.data.remaining_mut()
210 }
211
212 unsafe fn advance_mut(&mut self, cnt: usize) {
213 self.data.advance_mut(cnt);
215 }
216
217 fn chunk_mut(&mut self) -> &mut bytes::buf::UninitSlice {
218 self.data.chunk_mut()
219 }
220
221 fn put<T: bytes::Buf>(&mut self, src: T)
224 where
225 Self: Sized,
226 {
227 self.data.put(src);
228 }
229
230 fn put_slice(&mut self, src: &[u8]) {
231 self.data.put_slice(src);
232 }
233
234 fn put_bytes(&mut self, val: u8, cnt: usize) {
235 self.data.put_bytes(val, cnt);
236 }
237}
238
239impl From<Vec<u8>> for DgramBuffer {
240 fn from(v: Vec<u8>) -> Self {
241 DgramBuffer { data: v, start: 0 }
242 }
243}
244
245#[cfg(test)]
246mod tests {
247 use bytes::BufMut;
248
249 use super::*;
250
251 #[test]
252 fn new_is_empty() {
253 let b = DgramBuffer::new();
254 assert_eq!(b.as_slice(), &[]);
255 assert_eq!(b.len(), 0);
256 assert!(b.is_empty());
257 }
258
259 #[test]
260 fn from_slice_copies_data() {
261 let b = DgramBuffer::from_slice(&[1, 2, 3]);
262 assert_eq!(b.as_slice(), &[1, 2, 3]);
263 assert_eq!(b.len(), 3);
264 assert!(!b.is_empty());
265 }
266
267 #[test]
268 fn with_capacity_is_empty_with_allocation() {
269 let b = DgramBuffer::with_capacity(64);
270 assert_eq!(b.as_slice(), &[]);
271 assert_eq!(b.spare_capacity(), 64);
272 assert!(b.is_empty());
273 }
274
275 #[test]
276 fn with_capacity_and_headroom_positions_cursor() {
277 let b = DgramBuffer::with_capacity_and_headroom(16, 4);
278 assert_eq!(b.as_slice(), &[]);
279 assert_eq!(b.len(), 0);
280 assert_eq!(b.spare_capacity(), 12);
281 }
282
283 #[test]
284 fn from_vec_with_headroom_exposes_payload() {
285 let v = vec![0u8, 0, 0, 1, 2, 3];
286 let b = DgramBuffer::from_vec_with_headroom(v, 3);
287 assert_eq!(b.as_slice(), &[1, 2, 3]);
288 assert_eq!(b.len(), 3);
289 }
290
291 #[test]
292 fn truncate_shortens_readable_view() {
293 let mut b = DgramBuffer::from_slice(&[1, 2, 3, 4, 5]);
294 b.truncate(3);
295 assert_eq!(b.as_slice(), &[1, 2, 3]);
296 assert_eq!(b.len(), 3);
297 }
298
299 #[test]
300 fn advance_moves_cursor() {
301 let mut b = DgramBuffer::from_slice(&[1, 2, 3, 4]);
302 b.advance(2);
303 assert_eq!(b.as_slice(), &[3, 4]);
304 assert_eq!(b.len(), 2);
305 }
306
307 #[test]
308 #[should_panic]
309 fn advance_past_end_panics() {
310 let mut b = DgramBuffer::from_slice(&[1, 2]);
311 b.advance(3);
312 }
313
314 #[test]
315 fn try_add_prefix() {
316 let mut b = DgramBuffer::with_capacity_and_headroom(8, 4);
317 b.put_slice(&[0xaa, 0xbb]);
318 b.try_add_prefix(&[0x01, 0x02]).unwrap();
319 assert_eq!(b.as_slice(), &[0x01, 0x02, 0xaa, 0xbb]);
320 assert_eq!(b.len(), 4);
321 b.try_add_prefix(&[0x42, 0x23]).unwrap();
322 assert_eq!(b.as_slice(), &[0x42, 0x23, 0x01, 0x02, 0xaa, 0xbb]);
323 assert_eq!(b.len(), 6);
324 assert!(b.try_add_prefix(&[0x01]).is_err());
325 }
326
327 #[test]
328 fn try_add_prefix_fails_when_headroom_insufficient() {
329 let mut b = DgramBuffer::with_capacity_and_headroom(8, 1);
330 assert!(b.try_add_prefix(&[0x01, 0x02]).is_err());
331 }
332
333 #[test]
334 fn clear_resets_to_empty() {
335 let mut b = DgramBuffer::from_slice(&[1, 2, 3]);
336 b.clear();
337 assert_eq!(b.as_slice(), &[]);
338 assert_eq!(b.len(), 0);
339 assert!(b.is_empty());
340 }
341
342 #[test]
343 fn from_vec() {
344 let b = DgramBuffer::from(vec![7, 8, 9]);
345 assert_eq!(b.as_slice(), &[7, 8, 9]);
346 }
347
348 #[test]
349 fn into_parts_returns_data_and_pos() {
350 let mut b = DgramBuffer::with_capacity_and_headroom(8, 3);
351 b.put_slice(&[1, 2, 3]);
352 let (data, pos) = b.into_parts();
353 assert_eq!(pos, 3);
354 assert_eq!(&data, &[0, 0, 0, 1, 2, 3]);
355 }
356
357 #[test]
358 fn bufmut_put_slice_appends() {
359 let mut b = DgramBuffer::new();
360 b.put_slice(&[0x0a, 0x0b, 0x0c]);
361 assert_eq!(b.as_slice(), &[0x0a, 0x0b, 0x0c]);
362 }
363
364 #[test]
365 fn as_ref_matches_as_slice() {
366 let b = DgramBuffer::from_slice(&[1, 2, 3]);
367 assert_eq!(b.as_ref(), b.as_slice());
368 }
369
370 #[test]
371 fn as_mut_slice_allows_mutation() {
372 let mut b = DgramBuffer::from_slice(&[1, 2, 3]);
373 b.as_mut_slice()[1] = 0xff;
374 assert_eq!(b.as_slice(), &[1, 0xff, 3]);
375 }
376
377 #[test]
378 fn as_mut_via_trait_allows_mutation() {
379 let mut b = DgramBuffer::from_slice(&[1, 2, 3]);
380 b.as_mut()[1] = 0xff;
381 assert_eq!(b.as_slice(), &[1, 0xff, 3]);
382 }
383
384 #[test]
387 fn debug_empty() {
388 let b = DgramBuffer::new();
389 assert_eq!(format!("{:?}", b), "[]");
390 }
391
392 #[test]
393 fn debug_payload_hex_with_group_separator() {
394 let b = DgramBuffer::from_slice(&[
395 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
396 ]);
397 assert_eq!(format!("{:?}", b), "[0x00010203_04050607_08, len=9]");
398 }
399
400 #[test]
401 fn debug_payload_exact_group_boundary() {
402 let b = DgramBuffer::from_slice(&[0xaa, 0xbb, 0xcc, 0xdd]);
403 assert_eq!(format!("{:?}", b), "[0xaabbccdd, len=4]");
404 }
405
406 #[test]
408 fn splice_headroom_noop() {
409 let mut b = DgramBuffer::with_capacity_and_headroom(16, 4);
411 b.put_slice(&[1, 2, 3]);
412 let (data_before, pos_before) = b.into_parts();
413
414 let mut b =
415 DgramBuffer::from_vec_with_headroom(data_before.clone(), pos_before);
416 b.splice_headroom(3); let (data_after, pos_after) = b.clone().into_parts();
418 assert_eq!(pos_after, pos_before);
419 assert_eq!(data_after, data_before);
420
421 b.splice_headroom(4); assert_eq!(pos_after, pos_before);
423 assert_eq!(data_after, data_before);
424 }
425
426 #[test]
429 fn splice_headroom_inserts_headroom_when_none_exists() {
430 let mut b = DgramBuffer::from_slice(&[1, 2, 3]);
432 b.splice_headroom(4);
433
434 assert_eq!(b.as_slice(), &[1, 2, 3]);
436 let (data, pos) = b.into_parts();
437 assert_eq!(pos, 4);
438 assert_eq!(&data, &[0, 0, 0, 0, 1, 2, 3]);
440 }
441
442 #[test]
445 fn splice_headroom_grows_insufficient_headroom() {
446 let mut b = DgramBuffer::with_capacity_and_headroom(16, 2);
448 b.put_slice(&[10, 20, 30]);
449 b.splice_headroom(6);
451
452 assert_eq!(b.as_slice(), &[10, 20, 30]);
453 let (data, pos) = b.into_parts();
454 assert_eq!(pos, 6);
455 assert_eq!(&data[..6], &[0u8; 6]);
456 assert_eq!(&data[6..], &[10, 20, 30]);
457 }
458}