diff options
| author | Patrick Oppenlander <[email protected]> | 2023-02-07 14:49:44 +1100 |
|---|---|---|
| committer | Patrick Oppenlander <[email protected]> | 2023-03-02 14:11:49 +1100 |
| commit | 78974dfeb4db4a36bf9478397fd65370eb1dcf0c (patch) | |
| tree | 60655ced836cb846fc667e3dca8d57aab0ea2c2a /embassy-hal-common/src/atomic_ring_buffer.rs | |
| parent | 4ac257adb90ba25b12528f7ffed365f59ee60a46 (diff) | |
hal-common/atomic_ring_buffer: add push_bufs() push_slices()
This allows a writer to access all of the free space in the buffer, even
when it's wrapping around.
Diffstat (limited to 'embassy-hal-common/src/atomic_ring_buffer.rs')
| -rw-r--r-- | embassy-hal-common/src/atomic_ring_buffer.rs | 147 |
1 files changed, 147 insertions, 0 deletions
diff --git a/embassy-hal-common/src/atomic_ring_buffer.rs b/embassy-hal-common/src/atomic_ring_buffer.rs index ccbc37362..afd3ce1de 100644 --- a/embassy-hal-common/src/atomic_ring_buffer.rs +++ b/embassy-hal-common/src/atomic_ring_buffer.rs | |||
| @@ -153,6 +153,14 @@ impl<'a> Writer<'a> { | |||
| 153 | unsafe { slice::from_raw_parts_mut(data, len) } | 153 | unsafe { slice::from_raw_parts_mut(data, len) } |
| 154 | } | 154 | } |
| 155 | 155 | ||
| 156 | /// Get up to two buffers where data can be pushed to. | ||
| 157 | /// | ||
| 158 | /// Equivalent to [`Self::push_bufs`] but returns slices. | ||
| 159 | pub fn push_slices(&mut self) -> [&mut [u8]; 2] { | ||
| 160 | let [(d0, l0), (d1, l1)] = self.push_bufs(); | ||
| 161 | unsafe { [slice::from_raw_parts_mut(d0, l0), slice::from_raw_parts_mut(d1, l1)] } | ||
| 162 | } | ||
| 163 | |||
| 156 | /// Get a buffer where data can be pushed to. | 164 | /// Get a buffer where data can be pushed to. |
| 157 | /// | 165 | /// |
| 158 | /// Write data to the start of the buffer, then call `push_done` with | 166 | /// Write data to the start of the buffer, then call `push_done` with |
| @@ -191,6 +199,45 @@ impl<'a> Writer<'a> { | |||
| 191 | (unsafe { buf.add(end) }, n) | 199 | (unsafe { buf.add(end) }, n) |
| 192 | } | 200 | } |
| 193 | 201 | ||
| 202 | /// Get up to two buffers where data can be pushed to. | ||
| 203 | /// | ||
| 204 | /// Write data starting at the beginning of the first buffer, then call | ||
| 205 | /// `push_done` with however many bytes you've pushed. | ||
| 206 | /// | ||
| 207 | /// The buffers are suitable to DMA to. | ||
| 208 | /// | ||
| 209 | /// If the ringbuf is full, both buffers will be zero length. | ||
| 210 | /// If there is only area available, the second buffer will be zero length. | ||
| 211 | /// | ||
| 212 | /// The buffer stays valid as long as no other `Writer` method is called | ||
| 213 | /// and `init`/`deinit` aren't called on the ringbuf. | ||
| 214 | pub fn push_bufs(&mut self) -> [(*mut u8, usize); 2] { | ||
| 215 | // Ordering: as per push_buf() | ||
| 216 | let mut start = self.0.start.load(Ordering::Acquire); | ||
| 217 | let buf = self.0.buf.load(Ordering::Relaxed); | ||
| 218 | let len = self.0.len.load(Ordering::Relaxed); | ||
| 219 | let mut end = self.0.end.load(Ordering::Relaxed); | ||
| 220 | |||
| 221 | let empty = start == end; | ||
| 222 | |||
| 223 | if start >= len { | ||
| 224 | start -= len | ||
| 225 | } | ||
| 226 | if end >= len { | ||
| 227 | end -= len | ||
| 228 | } | ||
| 229 | |||
| 230 | if start == end && !empty { | ||
| 231 | // full | ||
| 232 | return [(buf, 0), (buf, 0)]; | ||
| 233 | } | ||
| 234 | let n0 = if start > end { start - end } else { len - end }; | ||
| 235 | let n1 = if start <= end { start } else { 0 }; | ||
| 236 | |||
| 237 | trace!(" ringbuf: push_bufs [{:?}..{:?}, {:?}..{:?}]", end, end + n0, 0, n1); | ||
| 238 | [(unsafe { buf.add(end) }, n0), (buf, n1)] | ||
| 239 | } | ||
| 240 | |||
| 194 | pub fn push_done(&mut self, n: usize) { | 241 | pub fn push_done(&mut self, n: usize) { |
| 195 | trace!(" ringbuf: push {:?}", n); | 242 | trace!(" ringbuf: push {:?}", n); |
| 196 | let end = self.0.end.load(Ordering::Relaxed); | 243 | let end = self.0.end.load(Ordering::Relaxed); |
| @@ -408,4 +455,104 @@ mod tests { | |||
| 408 | }); | 455 | }); |
| 409 | } | 456 | } |
| 410 | } | 457 | } |
| 458 | |||
| 459 | #[test] | ||
| 460 | fn push_slices() { | ||
| 461 | init(); | ||
| 462 | |||
| 463 | let mut b = [0; 4]; | ||
| 464 | let rb = RingBuffer::new(); | ||
| 465 | unsafe { | ||
| 466 | rb.init(b.as_mut_ptr(), 4); | ||
| 467 | |||
| 468 | /* push 3 -> [1 2 3 x] */ | ||
| 469 | let mut w = rb.writer(); | ||
| 470 | let ps = w.push_slices(); | ||
| 471 | assert_eq!(4, ps[0].len()); | ||
| 472 | assert_eq!(0, ps[1].len()); | ||
| 473 | ps[0][0] = 1; | ||
| 474 | ps[0][1] = 2; | ||
| 475 | ps[0][2] = 3; | ||
| 476 | w.push_done(3); | ||
| 477 | drop(w); | ||
| 478 | |||
| 479 | /* pop 2 -> [x x 3 x] */ | ||
| 480 | rb.reader().pop(|buf| { | ||
| 481 | assert_eq!(3, buf.len()); | ||
| 482 | assert_eq!(1, buf[0]); | ||
| 483 | assert_eq!(2, buf[1]); | ||
| 484 | assert_eq!(3, buf[2]); | ||
| 485 | 2 | ||
| 486 | }); | ||
| 487 | |||
| 488 | /* push 3 -> [5 6 3 4] */ | ||
| 489 | let mut w = rb.writer(); | ||
| 490 | let ps = w.push_slices(); | ||
| 491 | assert_eq!(1, ps[0].len()); | ||
| 492 | assert_eq!(2, ps[1].len()); | ||
| 493 | ps[0][0] = 4; | ||
| 494 | ps[1][0] = 5; | ||
| 495 | ps[1][1] = 6; | ||
| 496 | w.push_done(3); | ||
| 497 | drop(w); | ||
| 498 | |||
| 499 | /* buf is now full */ | ||
| 500 | let mut w = rb.writer(); | ||
| 501 | let ps = w.push_slices(); | ||
| 502 | assert_eq!(0, ps[0].len()); | ||
| 503 | assert_eq!(0, ps[1].len()); | ||
| 504 | |||
| 505 | /* pop 2 -> [5 6 x x] */ | ||
| 506 | rb.reader().pop(|buf| { | ||
| 507 | assert_eq!(2, buf.len()); | ||
| 508 | assert_eq!(3, buf[0]); | ||
| 509 | assert_eq!(4, buf[1]); | ||
| 510 | 2 | ||
| 511 | }); | ||
| 512 | |||
| 513 | /* should now have one push slice again */ | ||
| 514 | let mut w = rb.writer(); | ||
| 515 | let ps = w.push_slices(); | ||
| 516 | assert_eq!(2, ps[0].len()); | ||
| 517 | assert_eq!(0, ps[1].len()); | ||
| 518 | drop(w); | ||
| 519 | |||
| 520 | /* pop 2 -> [x x x x] */ | ||
| 521 | rb.reader().pop(|buf| { | ||
| 522 | assert_eq!(2, buf.len()); | ||
| 523 | assert_eq!(5, buf[0]); | ||
| 524 | assert_eq!(6, buf[1]); | ||
| 525 | 2 | ||
| 526 | }); | ||
| 527 | |||
| 528 | /* should now have two push slices */ | ||
| 529 | let mut w = rb.writer(); | ||
| 530 | let ps = w.push_slices(); | ||
| 531 | assert_eq!(2, ps[0].len()); | ||
| 532 | assert_eq!(2, ps[1].len()); | ||
| 533 | drop(w); | ||
| 534 | |||
| 535 | /* make sure we exercise all wrap around cases properly */ | ||
| 536 | for _ in 0..10 { | ||
| 537 | /* should be empty, push 1 */ | ||
| 538 | let mut w = rb.writer(); | ||
| 539 | let ps = w.push_slices(); | ||
| 540 | assert_eq!(4, ps[0].len() + ps[1].len()); | ||
| 541 | w.push_done(1); | ||
| 542 | drop(w); | ||
| 543 | |||
| 544 | /* should have 1 element */ | ||
| 545 | let mut w = rb.writer(); | ||
| 546 | let ps = w.push_slices(); | ||
| 547 | assert_eq!(3, ps[0].len() + ps[1].len()); | ||
| 548 | drop(w); | ||
| 549 | |||
| 550 | /* pop 1 */ | ||
| 551 | rb.reader().pop(|buf| { | ||
| 552 | assert_eq!(1, buf.len()); | ||
| 553 | 1 | ||
| 554 | }); | ||
| 555 | } | ||
| 556 | } | ||
| 557 | } | ||
| 411 | } | 558 | } |
