1#![no_std]
8
9use core::sync::atomic::{AtomicU64, Ordering};
10use core::{cmp, ptr, slice};
11pub use kalloc::NoOpAllocator;
12use kalloc::{Allocator, Box, DefaultAllocator};
13use zx_status::Status;
14
15#[derive(Copy, Clone, Debug, PartialEq, Eq)]
17struct RingPointers {
18 read: u32,
19 write: u32,
20}
21
22impl RingPointers {
23 const fn new(read: u32, write: u32) -> Self {
25 Self { read, write }
26 }
27
28 const fn from_combined(combined: u64) -> Self {
30 Self::new((combined >> 32) as u32, combined as u32)
31 }
32
33 const fn as_combined(&self) -> u64 {
35 ((self.read as u64) << 32) | (self.write as u64)
36 }
37
38 const fn available_data(&self) -> u32 {
40 self.write.wrapping_sub(self.read)
41 }
42}
43
44#[derive(Debug)]
51pub struct Reservation<'a> {
52 combined_pointers: &'a AtomicU64,
53 storage_len: u32,
54 initial_ring_pointers: RingPointers,
55 region1: &'a mut [u8],
56 region2: &'a mut [u8],
57 write_offset: u32,
58 committed: bool,
59}
60
61impl<'a> Drop for Reservation<'a> {
62 fn drop(&mut self) {
63 debug_assert!(self.committed, "Reservation dropped without being committed");
64 }
65}
66
67impl<'a> Reservation<'a> {
68 pub fn write(&mut self, data: &[u8]) -> Result<(), Status> {
70 if self.committed {
71 return Err(Status::BAD_STATE);
72 }
73
74 let mut bytes_to_copy = data.len();
75 let mut region1_copy_amount = 0;
76 let mut write_offset = self.write_offset as usize;
77
78 let region1_len = self.region1.len();
79 if write_offset < region1_len {
80 let space_left_in_region1 = region1_len - write_offset;
81 region1_copy_amount = cmp::min(bytes_to_copy, space_left_in_region1);
82
83 self.region1[write_offset..write_offset + region1_copy_amount]
84 .copy_from_slice(&data[..region1_copy_amount]);
85
86 write_offset += region1_copy_amount;
87 bytes_to_copy -= region1_copy_amount;
88 }
89
90 if bytes_to_copy > 0 {
91 if write_offset < region1_len {
92 return Err(Status::BAD_STATE);
93 }
94 let region2_len = self.region2.len();
95 let region2_offset = write_offset - region1_len;
96 if region2_len < region2_offset {
97 return Err(Status::BAD_STATE);
98 }
99 if region2_len - region2_offset < bytes_to_copy {
100 return Err(Status::BUFFER_TOO_SMALL);
101 }
102
103 self.region2[region2_offset..region2_offset + bytes_to_copy]
104 .copy_from_slice(&data[region1_copy_amount..region1_copy_amount + bytes_to_copy]);
105
106 write_offset += bytes_to_copy;
107 }
108
109 self.write_offset = write_offset as u32;
110 Ok(())
111 }
112
113 pub fn commit(mut self) -> Result<(), Status> {
118 if self.committed {
119 return Err(Status::BAD_STATE);
120 }
121 self.committed = true;
122
123 let total_len =
124 self.region1.len().checked_add(self.region2.len()).ok_or(Status::BAD_STATE)? as u32;
125 if self.write_offset != total_len {
126 return Err(Status::BAD_STATE);
127 }
128
129 advance_write_pointer(
130 self.combined_pointers,
131 self.storage_len,
132 self.initial_ring_pointers,
133 total_len,
134 )
135 }
136
137 pub fn cancel(mut self) {
140 self.committed = true;
141 }
142}
143
144#[repr(C, align(8))]
154pub struct Buffer<A: Allocator + Default = DefaultAllocator> {
155 combined_pointers: AtomicU64,
158 storage: *mut u8,
160 size: usize,
163 _phantom: core::marker::PhantomData<A>,
164}
165
166impl<A: Allocator + Default> Drop for Buffer<A> {
167 fn drop(&mut self) {
168 if !self.storage.is_null() {
169 let slice_ptr = ptr::slice_from_raw_parts_mut(self.storage, self.size);
170 unsafe {
171 let _ = Box::from_raw_in(slice_ptr, A::default());
172 }
173 }
174 }
175}
176
177impl<A: Allocator + Default> Buffer<A> {
178 const MAX_STORAGE_SIZE: u32 = 1 << 31;
180
181 pub fn try_new_in(size: u32, allocator: A) -> Result<Self, Status> {
184 if size > Self::MAX_STORAGE_SIZE {
185 return Err(Status::INVALID_ARGS);
186 }
187 if !size.is_power_of_two() {
188 return Err(Status::INVALID_ARGS);
189 }
190
191 let storage_box = Box::<[u8], A>::try_new_zeroed_slice_in(size as usize, allocator)
192 .map_err(|_| Status::NO_MEMORY)?;
193 let (storage_ptr, _) = Box::into_raw_with_allocator(storage_box);
194
195 Ok(Self {
196 combined_pointers: AtomicU64::new(0),
197 storage: storage_ptr as *mut u8,
198 size: size as usize,
199 _phantom: core::marker::PhantomData,
200 })
201 }
202
203 pub fn is_valid(&self) -> bool {
205 !self.storage.is_null()
206 && self.size > 0
207 && self.size <= Self::MAX_STORAGE_SIZE as usize
208 && self.size.is_power_of_two()
209 }
210
211 pub fn size(&self) -> u32 {
213 self.size as u32
214 }
215
216 pub fn reserve(&mut self, size: u32) -> Result<Reservation<'_>, Status> {
221 if size == 0 || size > Self::MAX_STORAGE_SIZE {
222 return Err(Status::INVALID_ARGS);
223 }
224
225 if self.storage.is_null() {
226 return Err(Status::BAD_STATE);
227 }
228
229 let storage_len = self.size as u32;
230 if size > storage_len {
231 return Err(Status::NO_SPACE);
232 }
233
234 let initial_state = self.load_pointers();
235 let available_space = self.available_space(initial_state);
236 if available_space < size {
237 return Err(Status::NO_SPACE);
238 }
239
240 let write_offset = self.pointer_to_offset(initial_state.write);
241 let ring_break_distance = storage_len - write_offset;
242 let bytes_before_break = cmp::min(size, ring_break_distance);
243
244 let storage_slice = unsafe { slice::from_raw_parts_mut(self.storage, self.size) };
247 let (left, right) = storage_slice.split_at_mut(write_offset as usize);
248 let region1 = &mut right[..bytes_before_break as usize];
249
250 let region2 = if bytes_before_break < size {
251 let region2_len = size - bytes_before_break;
252 &mut left[..region2_len as usize]
253 } else {
254 &mut []
255 };
256
257 Ok(Reservation {
258 combined_pointers: &self.combined_pointers,
259 storage_len,
260 initial_ring_pointers: initial_state,
261 region1,
262 region2,
263 write_offset: 0,
264 committed: false,
265 })
266 }
267
268 pub fn read<F>(&self, mut copy_fn: F, len: u32) -> Result<u32, Status>
282 where
283 F: FnMut(u32, &[u8]) -> Result<(), Status>,
284 {
285 if len > Self::MAX_STORAGE_SIZE {
286 return Err(Status::INVALID_ARGS);
287 }
288 if len == 0 {
289 return Ok(0);
290 }
291
292 if self.storage.is_null() {
293 return Err(Status::BAD_STATE);
294 }
295
296 let initial_state = self.load_pointers();
297 let available_data = initial_state.available_data();
298 if available_data == 0 {
299 return Ok(0);
300 }
301
302 let amount_to_copy = cmp::min(available_data, len) as usize;
303 let read_offset = self.pointer_to_offset(initial_state.read) as usize;
304 let ring_break_distance = self.size - read_offset;
305 let bytes_before_break = cmp::min(amount_to_copy, ring_break_distance);
306
307 let storage_slice = unsafe { slice::from_raw_parts(self.storage, self.size) };
310 let slice1 = &storage_slice[read_offset..read_offset + bytes_before_break];
311 copy_fn(0, slice1)?;
312
313 if bytes_before_break < amount_to_copy {
314 let bytes_after_break = amount_to_copy - bytes_before_break;
315 let slice2 = &storage_slice[..bytes_after_break];
316 copy_fn(bytes_before_break as u32, slice2)?;
317 }
318
319 self.advance_read_pointer(initial_state, amount_to_copy as u32)?;
320 Ok(amount_to_copy as u32)
321 }
322
323 pub fn drain(&self) -> Result<(), Status> {
328 let initial_state = self.load_pointers();
329 let available_data = initial_state.available_data();
330 if available_data == 0 {
331 return Ok(());
332 }
333 self.advance_read_pointer(initial_state, available_data)
334 }
335
336 fn pointer_to_offset(&self, pointer: u32) -> u32 {
341 let storage_len = self.size as u32;
342 pointer & (storage_len - 1)
343 }
344
345 fn available_space(&self, pointers: RingPointers) -> u32 {
347 let storage_len = self.size as u32;
348 storage_len.wrapping_sub(pointers.available_data())
349 }
350
351 fn load_pointers(&self) -> RingPointers {
353 let combined = self.combined_pointers.load(Ordering::Acquire);
354 RingPointers::from_combined(combined)
355 }
356
357 fn advance_read_pointer(&self, initial: RingPointers, delta: u32) -> Result<(), Status> {
368 if delta > initial.available_data() {
369 return Err(Status::INVALID_ARGS);
370 }
371
372 let target_read = initial.read.wrapping_add(delta);
373 let mut starting_pointers = initial.as_combined();
374 let mut target_pointers = RingPointers::new(target_read, initial.write).as_combined();
375
376 loop {
377 match self.combined_pointers.compare_exchange_weak(
378 starting_pointers,
379 target_pointers,
380 Ordering::Release,
381 Ordering::Relaxed,
382 ) {
383 Ok(_) => break,
384 Err(observed_combined) => {
385 starting_pointers = observed_combined;
386 let observed = RingPointers::from_combined(observed_combined);
387 debug_assert_eq!(
388 observed.read, initial.read,
389 "potential concurrent read detected; expected read pointer {}, got {}",
390 initial.read, observed.read
391 );
392 target_pointers = RingPointers::new(target_read, observed.write).as_combined();
393 }
394 }
395 }
396 Ok(())
397 }
398}
399
400fn advance_write_pointer(
411 combined_pointers: &AtomicU64,
412 storage_len: u32,
413 initial: RingPointers,
414 delta: u32,
415) -> Result<(), Status> {
416 let available_data = initial.available_data();
417 if delta > storage_len.checked_sub(available_data).ok_or(Status::INVALID_ARGS)? {
418 return Err(Status::INVALID_ARGS);
419 }
420
421 let target_write = initial.write.wrapping_add(delta);
422 let mut starting_pointers = initial.as_combined();
423 let mut target_pointers = RingPointers::new(initial.read, target_write).as_combined();
424
425 loop {
426 match combined_pointers.compare_exchange_weak(
427 starting_pointers,
428 target_pointers,
429 Ordering::Release,
430 Ordering::Relaxed,
431 ) {
432 Ok(_) => break,
433 Err(observed_combined) => {
434 starting_pointers = observed_combined;
435 let observed = RingPointers::from_combined(observed_combined);
436 debug_assert_eq!(
437 observed.write, initial.write,
438 "potential concurrent write detected; expected write pointer {}, got {}",
439 initial.write, observed.write
440 );
441 target_pointers = RingPointers::new(observed.read, target_write).as_combined();
442 }
443 }
444 }
445 Ok(())
446}
447
448impl Buffer<DefaultAllocator> {
449 pub fn try_new(size: u32) -> Result<Self, Status> {
452 Self::try_new_in(size, DefaultAllocator)
453 }
454}
455
456impl Buffer<NoOpAllocator> {
457 pub unsafe fn from_raw_parts(storage: *mut u8, size: usize) -> Self {
466 assert!(!storage.is_null());
467 assert!(size <= Self::MAX_STORAGE_SIZE as usize);
468 assert!(size.is_power_of_two());
469 Self {
470 combined_pointers: AtomicU64::new(0),
471 storage,
472 size,
473 _phantom: core::marker::PhantomData,
474 }
475 }
476
477 pub const fn empty() -> Self {
479 Self {
480 combined_pointers: AtomicU64::new(0),
481 storage: core::ptr::null_mut(),
482 size: 0,
483 _phantom: core::marker::PhantomData,
484 }
485 }
486}
487
488#[cfg(test)]
489mod tests {
490 use super::*;
491
492 #[test]
493 fn test_ring_pointers_from_combined() {
494 struct TestCase {
495 combined_pointers: u64,
496 expected: RingPointers,
497 }
498 let test_cases = [
499 TestCase {
500 combined_pointers: 0x01320087_01005382,
501 expected: RingPointers::new(0x1320087, 0x1005382),
502 },
503 TestCase { combined_pointers: 0, expected: RingPointers::new(0, 0) },
504 TestCase {
505 combined_pointers: 0xFFFFFFFF_00004587,
506 expected: RingPointers::new(0xFFFFFFFF, 0x4587),
507 },
508 TestCase {
509 combined_pointers: 0x00004587_FFFFFFFF,
510 expected: RingPointers::new(0x4587, 0xFFFFFFFF),
511 },
512 ];
513
514 for tc in &test_cases {
515 let actual = RingPointers::from_combined(tc.combined_pointers);
516 assert_eq!(tc.expected, actual);
517 }
518 }
519
520 #[test]
521 fn test_ring_pointers_as_combined() {
522 struct TestCase {
523 pointers: RingPointers,
524 combined: u64,
525 }
526 let test_cases = [
527 TestCase {
528 pointers: RingPointers::new(0x1320087, 0x1005382),
529 combined: 0x01320087_01005382,
530 },
531 TestCase { pointers: RingPointers::new(0, 0), combined: 0 },
532 TestCase {
533 pointers: RingPointers::new(0xFFFFFFFF, 0x123),
534 combined: 0xFFFFFFFF_00000123,
535 },
536 TestCase {
537 pointers: RingPointers::new(0x123, 0xFFFFFFFF),
538 combined: 0x00000123_FFFFFFFF,
539 },
540 ];
541
542 for tc in &test_cases {
543 let actual = tc.pointers.as_combined();
544 assert_eq!(tc.combined, actual);
545 }
546 }
547
548 #[test]
549 fn test_available_space() {
550 struct TestCase {
551 pointers: RingPointers,
552 buffer_size: u32,
553 expected: u32,
554 }
555 let test_cases = [
556 TestCase { pointers: RingPointers::new(0, 0), buffer_size: 16, expected: 16 },
557 TestCase { pointers: RingPointers::new(3, 3), buffer_size: 16, expected: 16 },
558 TestCase { pointers: RingPointers::new(3, 7), buffer_size: 16, expected: 12 },
559 TestCase { pointers: RingPointers::new(0xFFFFFFFC, 3), buffer_size: 16, expected: 9 },
560 TestCase { pointers: RingPointers::new(0, 16), buffer_size: 16, expected: 0 },
561 ];
562
563 for tc in &test_cases {
564 let buffer = Buffer::try_new(tc.buffer_size).unwrap();
565 let actual = buffer.available_space(tc.pointers);
566 assert_eq!(tc.expected, actual);
567 }
568 }
569
570 #[test]
571 fn test_ring_pointers_available_data() {
572 struct TestCase {
573 pointers: RingPointers,
574 expected: u32,
575 }
576 let test_cases = [
577 TestCase { pointers: RingPointers::new(0, 0), expected: 0 },
578 TestCase { pointers: RingPointers::new(3, 3), expected: 0 },
579 TestCase { pointers: RingPointers::new(3, 7), expected: 4 },
580 TestCase { pointers: RingPointers::new(0, 16), expected: 16 },
581 TestCase { pointers: RingPointers::new(0xFFFFFFFC, 3), expected: 7 },
582 ];
583
584 for tc in &test_cases {
585 let actual = tc.pointers.available_data();
586 assert_eq!(tc.expected, actual);
587 }
588 }
589
590 #[test]
591 fn test_advance_read_pointer() {
592 struct TestCase {
593 initial_pointers: RingPointers,
594 buffer_size: u32,
595 delta: u32,
596 expected: u64,
597 }
598 let test_cases = [
599 TestCase {
600 initial_pointers: RingPointers::new(1, 6),
601 buffer_size: 16,
602 delta: 4,
603 expected: 0x5_00000006,
604 },
605 TestCase {
606 initial_pointers: RingPointers::new(0, 16),
607 buffer_size: 16,
608 delta: 16,
609 expected: 0x10_00000010,
610 },
611 TestCase {
612 initial_pointers: RingPointers::new(0xFFFFFFFC, 12),
613 buffer_size: 16,
614 delta: 5,
615 expected: 0x1_0000000C,
616 },
617 ];
618
619 for tc in &test_cases {
620 let buffer = Buffer::try_new(tc.buffer_size).unwrap();
621
622 let initial = tc.initial_pointers.as_combined();
623 buffer.combined_pointers.store(initial, Ordering::Release);
624
625 buffer.advance_read_pointer(tc.initial_pointers, tc.delta).unwrap();
626 let actual = buffer.combined_pointers.load(Ordering::Acquire);
627 assert_eq!(tc.expected, actual);
628 }
629 }
630
631 #[test]
632 fn test_advance_write_pointer() {
633 struct TestCase {
634 initial_pointers: RingPointers,
635 buffer_size: u32,
636 delta: u32,
637 expected: u64,
638 }
639 let test_cases = [
640 TestCase {
641 initial_pointers: RingPointers::new(7, 9),
642 buffer_size: 16,
643 delta: 4,
644 expected: 0x7_0000000D,
645 },
646 TestCase {
647 initial_pointers: RingPointers::new(0, 0),
648 buffer_size: 16,
649 delta: 16,
650 expected: 0x10,
651 },
652 TestCase {
653 initial_pointers: RingPointers::new(0xFFFFFFF1, 0xFFFFFFFC),
654 buffer_size: 16,
655 delta: 5,
656 expected: 0xFFFFFFF100000001,
657 },
658 ];
659
660 for tc in &test_cases {
661 let buffer = Buffer::try_new(tc.buffer_size).unwrap();
662
663 let initial = tc.initial_pointers.as_combined();
664 buffer.combined_pointers.store(initial, Ordering::Release);
665
666 advance_write_pointer(
667 &buffer.combined_pointers,
668 buffer.size as u32,
669 tc.initial_pointers,
670 tc.delta,
671 )
672 .unwrap();
673 let actual = buffer.combined_pointers.load(Ordering::Acquire);
674 assert_eq!(tc.expected, actual);
675 }
676 }
677
678 #[test]
679 fn test_try_new() {
680 {
682 assert!(Buffer::try_new(256).is_ok());
683 }
684
685 {
687 assert_eq!(Buffer::try_new(u32::MAX).err().unwrap(), Status::INVALID_ARGS);
688 }
689
690 {
692 assert_eq!(Buffer::try_new(100).err().unwrap(), Status::INVALID_ARGS);
693 }
694
695 {
697 assert_eq!(Buffer::try_new_in(256, NoOpAllocator).err().unwrap(), Status::NO_MEMORY);
698 }
699 }
700
701 #[test]
702 fn test_read_write_single_threaded() {
703 const STORAGE_SIZE: usize = 256;
704 let mut src = [0u8; STORAGE_SIZE];
705 for i in 0..STORAGE_SIZE {
706 src[i] = (i * 17 + 5) as u8;
707 }
708
709 struct TestCase {
710 write_size: u32,
711 read_size: u32,
712 expected_read_size: u32,
713 expected_reserve_status: Result<(), Status>,
714 expected_read_status: Result<(), Status>,
715 initial_pointers: RingPointers,
716 use_copy_out_err_fn: bool,
717 }
718
719 let test_cases = [
720 TestCase {
721 write_size: (STORAGE_SIZE / 2) as u32,
722 read_size: (STORAGE_SIZE / 2) as u32,
723 expected_read_size: (STORAGE_SIZE / 2) as u32,
724 expected_reserve_status: Ok(()),
725 expected_read_status: Ok(()),
726 initial_pointers: RingPointers::new(0, 0),
727 use_copy_out_err_fn: false,
728 },
729 TestCase {
730 write_size: (STORAGE_SIZE / 2) as u32,
731 read_size: (STORAGE_SIZE / 4) as u32,
732 expected_read_size: (STORAGE_SIZE / 4) as u32,
733 expected_reserve_status: Ok(()),
734 expected_read_status: Ok(()),
735 initial_pointers: RingPointers::new(0, 0),
736 use_copy_out_err_fn: false,
737 },
738 TestCase {
739 write_size: STORAGE_SIZE as u32,
740 read_size: STORAGE_SIZE as u32,
741 expected_read_size: STORAGE_SIZE as u32,
742 expected_reserve_status: Ok(()),
743 expected_read_status: Ok(()),
744 initial_pointers: RingPointers::new(0, 0),
745 use_copy_out_err_fn: false,
746 },
747 TestCase {
748 write_size: (STORAGE_SIZE / 4) as u32,
749 read_size: (STORAGE_SIZE / 2) as u32,
750 expected_read_size: (STORAGE_SIZE / 4) as u32,
751 expected_reserve_status: Ok(()),
752 expected_read_status: Ok(()),
753 initial_pointers: RingPointers::new(0, 0),
754 use_copy_out_err_fn: false,
755 },
756 TestCase {
757 write_size: STORAGE_SIZE as u32,
758 read_size: STORAGE_SIZE as u32,
759 expected_read_size: STORAGE_SIZE as u32,
760 expected_reserve_status: Ok(()),
761 expected_read_status: Ok(()),
762 initial_pointers: RingPointers::new(
763 (STORAGE_SIZE / 2) as u32,
764 (STORAGE_SIZE / 2) as u32,
765 ),
766 use_copy_out_err_fn: false,
767 },
768 TestCase {
769 write_size: STORAGE_SIZE as u32,
770 read_size: STORAGE_SIZE as u32,
771 expected_read_size: STORAGE_SIZE as u32,
772 expected_reserve_status: Ok(()),
773 expected_read_status: Ok(()),
774 initial_pointers: RingPointers::new(0xFFFFFFFA, 0xFFFFFFFA),
775 use_copy_out_err_fn: false,
776 },
777 TestCase {
778 write_size: 64,
779 read_size: 0,
780 expected_read_size: 0,
781 expected_reserve_status: Err(Status::NO_SPACE),
782 expected_read_status: Ok(()),
783 initial_pointers: RingPointers::new(0, (STORAGE_SIZE - 48) as u32),
784 use_copy_out_err_fn: false,
785 },
786 TestCase {
787 write_size: STORAGE_SIZE as u32,
788 read_size: (STORAGE_SIZE / 2) as u32,
789 expected_read_size: 0,
790 expected_reserve_status: Ok(()),
791 expected_read_status: Err(Status::BAD_STATE),
792 initial_pointers: RingPointers::new(0, 0),
793 use_copy_out_err_fn: true,
794 },
795 ];
796
797 for tc in &test_cases {
798 let mut dst = [0u8; STORAGE_SIZE];
799
800 let mut spsc = Buffer::try_new(STORAGE_SIZE as u32).unwrap();
801
802 let starting_pointers = tc.initial_pointers.as_combined();
803 spsc.combined_pointers.store(starting_pointers, Ordering::Release);
804
805 let reservation = spsc.reserve(tc.write_size);
806 if let Err(e) = &tc.expected_reserve_status {
807 match reservation {
808 Err(actual_err) => assert_eq!(actual_err, *e),
809 Ok(_) => panic!("expected reserve to fail with {:?}, but it succeeded", e),
810 }
811 continue;
812 }
813 let mut reservation = reservation.unwrap();
814
815 reservation.write(&src[..tc.write_size as usize]).unwrap();
816 reservation.commit().unwrap();
817
818 let copy_out_fn = |offset: u32, src_slice: &[u8]| -> Result<(), Status> {
819 let offset = offset as usize;
820 assert!(offset + src_slice.len() <= dst.len());
821 dst[offset..offset + src_slice.len()].copy_from_slice(src_slice);
822 Ok(())
823 };
824
825 let copy_out_err_fn =
826 |_offset: u32, _src_slice: &[u8]| -> Result<(), Status> { Err(Status::BAD_STATE) };
827
828 let read_result = if tc.use_copy_out_err_fn {
829 spsc.read(copy_out_err_fn, tc.read_size)
830 } else {
831 spsc.read(copy_out_fn, tc.read_size)
832 };
833
834 if let Err(e) = &tc.expected_read_status {
835 assert_eq!(read_result.unwrap_err(), *e);
836 assert_eq!(spsc.load_pointers().available_data(), tc.write_size);
837 continue;
838 }
839
840 let read_bytes = read_result.unwrap();
841 assert_eq!(read_bytes, tc.expected_read_size);
842 assert_eq!(
843 &dst[..tc.expected_read_size as usize],
844 &src[..tc.expected_read_size as usize]
845 );
846 }
847 }
848
849 #[test]
850 fn test_drain() {
851 const STORAGE_SIZE: u32 = 256;
852 let mut spsc = Buffer::try_new(STORAGE_SIZE).unwrap();
853
854 let mut reservation = spsc.reserve(STORAGE_SIZE / 2).unwrap();
855 let write_data = [b'f'; (STORAGE_SIZE / 2) as usize];
856 reservation.write(&write_data).unwrap();
857 reservation.commit().unwrap();
858
859 assert_eq!(spsc.load_pointers().available_data(), STORAGE_SIZE / 2);
860
861 spsc.drain().unwrap();
862 assert_eq!(spsc.load_pointers().available_data(), 0);
863 }
864
865 #[test]
866 fn test_commit_error() {
867 const STORAGE_SIZE: u32 = 256;
868 let mut spsc = Buffer::try_new(STORAGE_SIZE).unwrap();
869
870 let mut reservation = spsc.reserve(STORAGE_SIZE / 2).unwrap();
871 let write_data = [b'f'; (STORAGE_SIZE / 2) as usize - 1];
873 reservation.write(&write_data).unwrap();
874 assert_eq!(reservation.commit(), Err(Status::BAD_STATE));
875 }
876
877 #[test]
878 fn test_write_error() {
879 const STORAGE_SIZE: u32 = 256;
880 let mut spsc = Buffer::try_new(STORAGE_SIZE).unwrap();
881
882 let mut reservation = spsc.reserve(STORAGE_SIZE / 2).unwrap();
883 let write_data = [b'f'; (STORAGE_SIZE / 2) as usize + 1];
885 assert_eq!(reservation.write(&write_data), Err(Status::BUFFER_TOO_SMALL));
886 reservation.committed = true;
887 }
888
889 #[test]
890 fn test_from_raw_parts() {
891 let mut mock_storage = [0u8; 256];
892
893 let mut spsc =
895 unsafe { Buffer::from_raw_parts(mock_storage.as_mut_ptr(), mock_storage.len()) };
896
897 let mut reservation = spsc.reserve(100).unwrap();
899 let write_data = [b'x'; 100];
900 reservation.write(&write_data).unwrap();
901 reservation.commit().unwrap();
902
903 assert_eq!(spsc.load_pointers().available_data(), 100);
904 assert_eq!(spsc.combined_pointers.load(Ordering::Relaxed) & 0xffffffff, 100); assert_eq!(&mock_storage[..100], &write_data[..]);
906 }
907
908 #[test]
909 fn test_reserve_zero() {
910 const STORAGE_SIZE: u32 = 256;
911 let mut spsc = Buffer::try_new(STORAGE_SIZE).unwrap();
912 match spsc.reserve(0) {
913 Err(e) => assert_eq!(e, Status::INVALID_ARGS),
914 Ok(_) => panic!("reserve(0) should fail with INVALID_ARGS"),
915 }
916 }
917
918 #[test]
919 fn test_reserve_too_large() {
920 const STORAGE_SIZE: u32 = 256;
921 let mut spsc = Buffer::try_new(STORAGE_SIZE).unwrap();
922 match spsc.reserve(u32::MAX) {
923 Err(e) => assert_eq!(e, Status::INVALID_ARGS),
924 Ok(_) => panic!("reserve(u32::MAX) should fail with INVALID_ARGS"),
925 }
926 }
927
928 #[test]
929 fn test_reserve_and_read_null_storage() {
930 let mut spsc = Buffer::<NoOpAllocator>::empty();
931
932 assert!(!spsc.is_valid());
933 assert_eq!(spsc.reserve(16).err(), Some(Status::BAD_STATE));
934 let read_result = spsc.read(|_, _| Ok(()), 16);
935 assert_eq!(read_result.err(), Some(Status::BAD_STATE));
936 }
937
938 #[test]
939 fn test_reserve_at_break() {
940 const STORAGE_SIZE: u32 = 16;
941 let mut mock_storage = [0u8; STORAGE_SIZE as usize];
946
947 let mut spsc =
949 unsafe { Buffer::from_raw_parts(mock_storage.as_mut_ptr(), mock_storage.len()) };
950 spsc.combined_pointers.store((3u64 << 32) | 15u64, Ordering::Release);
951
952 let mut reservation = spsc.reserve(4).unwrap();
953 assert_eq!(reservation.region1.len(), 1);
954 assert_eq!(reservation.region2.len(), 3);
955
956 let data = [1, 2, 3, 4];
957 reservation.write(&data).unwrap();
958 reservation.commit().unwrap();
959
960 assert_eq!(mock_storage[15], 1);
961 assert_eq!(mock_storage[0], 2);
962 assert_eq!(mock_storage[1], 3);
963 assert_eq!(mock_storage[2], 4);
964 }
965
966 #[test]
967 fn test_cpp_rust_integration() {
968 #[link(name = "c++")]
969 unsafe extern "C" {
970 fn cpp_spsc_allocate(size: u32) -> *mut Buffer<NoOpAllocator>;
971 fn cpp_spsc_free(spsc: *mut Buffer<NoOpAllocator>);
972 fn cpp_spsc_write(spsc: *mut Buffer<NoOpAllocator>, data: *const u8, len: u32) -> i32;
973 fn cpp_spsc_read(spsc: *mut Buffer<NoOpAllocator>, dst: *mut u8, len: u32) -> i32;
974 }
975
976 let spsc_ptr = unsafe { cpp_spsc_allocate(256) };
978 assert!(!spsc_ptr.is_null());
979
980 let spsc = unsafe { &mut *spsc_ptr };
982
983 let write_data = b"Hello from C++!";
985 let cpp_write_status =
986 unsafe { cpp_spsc_write(spsc_ptr, write_data.as_ptr(), write_data.len() as u32) };
987 assert_eq!(cpp_write_status, 0); let mut read_buf = [0u8; 100];
991 let bytes_read = spsc
992 .read(
993 |_, src| {
994 read_buf[..src.len()].copy_from_slice(src);
995 Ok(())
996 },
997 write_data.len() as u32,
998 )
999 .unwrap();
1000 assert_eq!(bytes_read, write_data.len() as u32);
1001 assert_eq!(&read_buf[..bytes_read as usize], write_data);
1002
1003 let rust_write_data = b"Hello from Rust!";
1005 let mut reservation = spsc.reserve(rust_write_data.len() as u32).unwrap();
1006 reservation.write(rust_write_data).unwrap();
1007 reservation.commit().unwrap();
1008
1009 let mut cpp_read_buf = [0u8; 100];
1011 let cpp_read_bytes = unsafe {
1012 cpp_spsc_read(spsc_ptr, cpp_read_buf.as_mut_ptr(), rust_write_data.len() as u32)
1013 };
1014 assert_eq!(cpp_read_bytes, rust_write_data.len() as i32);
1015 assert_eq!(&cpp_read_buf[..cpp_read_bytes as usize], rust_write_data);
1016
1017 unsafe {
1019 cpp_spsc_free(spsc_ptr);
1020 }
1021 }
1022}