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 #[inline]
213 pub const fn size(&self) -> u32 {
214 self.size as u32
215 }
216
217 #[inline]
222 pub fn reserve(&mut self, size: u32) -> Result<Reservation<'_>, Status> {
223 unsafe { self.reserve_shared(size) }
225 }
226
227 pub unsafe fn reserve_shared(&self, size: u32) -> Result<Reservation<'_>, Status> {
238 if size == 0 || size > Self::MAX_STORAGE_SIZE {
239 return Err(Status::INVALID_ARGS);
240 }
241
242 if self.storage.is_null() {
243 return Err(Status::BAD_STATE);
244 }
245
246 let storage_len = self.size as u32;
247 if size > storage_len {
248 return Err(Status::NO_SPACE);
249 }
250
251 let initial_state = self.load_pointers();
252 let available_space = self.available_space(initial_state);
253 if available_space < size {
254 return Err(Status::NO_SPACE);
255 }
256
257 let write_offset = self.pointer_to_offset(initial_state.write);
258 let ring_break_distance = storage_len - write_offset;
259 let bytes_before_break = cmp::min(size, ring_break_distance);
260
261 let region1 = unsafe {
267 slice::from_raw_parts_mut(
268 self.storage.add(write_offset as usize),
269 bytes_before_break as usize,
270 )
271 };
272
273 let region2 = if bytes_before_break < size {
274 let region2_len = size - bytes_before_break;
275 unsafe { slice::from_raw_parts_mut(self.storage, region2_len as usize) }
278 } else {
279 &mut []
280 };
281
282 Ok(Reservation {
283 combined_pointers: &self.combined_pointers,
284 storage_len,
285 initial_ring_pointers: initial_state,
286 region1,
287 region2,
288 write_offset: 0,
289 committed: false,
290 })
291 }
292
293 pub fn read<F>(&self, mut copy_fn: F, len: u32) -> Result<u32, Status>
307 where
308 F: FnMut(u32, &[u8]) -> Result<(), Status>,
309 {
310 if len > Self::MAX_STORAGE_SIZE {
311 return Err(Status::INVALID_ARGS);
312 }
313 if len == 0 {
314 return Ok(0);
315 }
316
317 if self.storage.is_null() {
318 return Err(Status::BAD_STATE);
319 }
320
321 let initial_state = self.load_pointers();
322 let available_data = initial_state.available_data();
323 if available_data == 0 {
324 return Ok(0);
325 }
326
327 let amount_to_copy = cmp::min(available_data, len) as usize;
328 let read_offset = self.pointer_to_offset(initial_state.read) as usize;
329 let ring_break_distance = self.size - read_offset;
330 let bytes_before_break = cmp::min(amount_to_copy, ring_break_distance);
331
332 let slice1 =
338 unsafe { slice::from_raw_parts(self.storage.add(read_offset), bytes_before_break) };
339 copy_fn(0, slice1)?;
340
341 if bytes_before_break < amount_to_copy {
342 let bytes_after_break = amount_to_copy - bytes_before_break;
343 let slice2 = unsafe { slice::from_raw_parts(self.storage, bytes_after_break) };
346 copy_fn(bytes_before_break as u32, slice2)?;
347 }
348
349 self.advance_read_pointer(initial_state, amount_to_copy as u32)?;
350 Ok(amount_to_copy as u32)
351 }
352
353 pub fn drain(&self) -> Result<(), Status> {
358 let initial_state = self.load_pointers();
359 let available_data = initial_state.available_data();
360 if available_data == 0 {
361 return Ok(());
362 }
363 self.advance_read_pointer(initial_state, available_data)
364 }
365
366 fn pointer_to_offset(&self, pointer: u32) -> u32 {
371 let storage_len = self.size as u32;
372 pointer & (storage_len - 1)
373 }
374
375 fn available_space(&self, pointers: RingPointers) -> u32 {
377 let storage_len = self.size as u32;
378 storage_len.wrapping_sub(pointers.available_data())
379 }
380
381 fn load_pointers(&self) -> RingPointers {
383 let combined = self.combined_pointers.load(Ordering::Acquire);
384 RingPointers::from_combined(combined)
385 }
386
387 fn advance_read_pointer(&self, initial: RingPointers, delta: u32) -> Result<(), Status> {
398 if delta > initial.available_data() {
399 return Err(Status::INVALID_ARGS);
400 }
401
402 let target_read = initial.read.wrapping_add(delta);
403 let mut starting_pointers = initial.as_combined();
404 let mut target_pointers = RingPointers::new(target_read, initial.write).as_combined();
405
406 loop {
407 match self.combined_pointers.compare_exchange_weak(
408 starting_pointers,
409 target_pointers,
410 Ordering::Release,
411 Ordering::Relaxed,
412 ) {
413 Ok(_) => break,
414 Err(observed_combined) => {
415 starting_pointers = observed_combined;
416 let observed = RingPointers::from_combined(observed_combined);
417 debug_assert_eq!(
418 observed.read, initial.read,
419 "potential concurrent read detected; expected read pointer {}, got {}",
420 initial.read, observed.read
421 );
422 target_pointers = RingPointers::new(target_read, observed.write).as_combined();
423 }
424 }
425 }
426 Ok(())
427 }
428}
429
430fn advance_write_pointer(
441 combined_pointers: &AtomicU64,
442 storage_len: u32,
443 initial: RingPointers,
444 delta: u32,
445) -> Result<(), Status> {
446 let available_data = initial.available_data();
447 if delta > storage_len.checked_sub(available_data).ok_or(Status::INVALID_ARGS)? {
448 return Err(Status::INVALID_ARGS);
449 }
450
451 let target_write = initial.write.wrapping_add(delta);
452 let mut starting_pointers = initial.as_combined();
453 let mut target_pointers = RingPointers::new(initial.read, target_write).as_combined();
454
455 loop {
456 match combined_pointers.compare_exchange_weak(
457 starting_pointers,
458 target_pointers,
459 Ordering::Release,
460 Ordering::Relaxed,
461 ) {
462 Ok(_) => break,
463 Err(observed_combined) => {
464 starting_pointers = observed_combined;
465 let observed = RingPointers::from_combined(observed_combined);
466 debug_assert_eq!(
467 observed.write, initial.write,
468 "potential concurrent write detected; expected write pointer {}, got {}",
469 initial.write, observed.write
470 );
471 target_pointers = RingPointers::new(observed.read, target_write).as_combined();
472 }
473 }
474 }
475 Ok(())
476}
477
478impl Buffer<DefaultAllocator> {
479 pub fn try_new(size: u32) -> Result<Self, Status> {
482 Self::try_new_in(size, DefaultAllocator)
483 }
484}
485
486impl Buffer<NoOpAllocator> {
487 pub unsafe fn from_raw_parts(storage: *mut u8, size: usize) -> Self {
496 assert!(!storage.is_null());
497 assert!(size <= Self::MAX_STORAGE_SIZE as usize);
498 assert!(size.is_power_of_two());
499 Self {
500 combined_pointers: AtomicU64::new(0),
501 storage,
502 size,
503 _phantom: core::marker::PhantomData,
504 }
505 }
506
507 pub const fn empty() -> Self {
509 Self {
510 combined_pointers: AtomicU64::new(0),
511 storage: core::ptr::null_mut(),
512 size: 0,
513 _phantom: core::marker::PhantomData,
514 }
515 }
516
517 #[inline]
519 pub fn take_raw_parts(&mut self) -> (*mut u8, usize) {
520 let storage = self.storage;
521 let size = self.size;
522 self.storage = core::ptr::null_mut();
523 self.size = 0;
524 (storage, size)
525 }
526}
527
528#[cfg(test)]
529mod tests {
530 use super::*;
531
532 #[test]
533 fn test_ring_pointers_from_combined() {
534 struct TestCase {
535 combined_pointers: u64,
536 expected: RingPointers,
537 }
538 let test_cases = [
539 TestCase {
540 combined_pointers: 0x01320087_01005382,
541 expected: RingPointers::new(0x1320087, 0x1005382),
542 },
543 TestCase { combined_pointers: 0, expected: RingPointers::new(0, 0) },
544 TestCase {
545 combined_pointers: 0xFFFFFFFF_00004587,
546 expected: RingPointers::new(0xFFFFFFFF, 0x4587),
547 },
548 TestCase {
549 combined_pointers: 0x00004587_FFFFFFFF,
550 expected: RingPointers::new(0x4587, 0xFFFFFFFF),
551 },
552 ];
553
554 for tc in &test_cases {
555 let actual = RingPointers::from_combined(tc.combined_pointers);
556 assert_eq!(tc.expected, actual);
557 }
558 }
559
560 #[test]
561 fn test_ring_pointers_as_combined() {
562 struct TestCase {
563 pointers: RingPointers,
564 combined: u64,
565 }
566 let test_cases = [
567 TestCase {
568 pointers: RingPointers::new(0x1320087, 0x1005382),
569 combined: 0x01320087_01005382,
570 },
571 TestCase { pointers: RingPointers::new(0, 0), combined: 0 },
572 TestCase {
573 pointers: RingPointers::new(0xFFFFFFFF, 0x123),
574 combined: 0xFFFFFFFF_00000123,
575 },
576 TestCase {
577 pointers: RingPointers::new(0x123, 0xFFFFFFFF),
578 combined: 0x00000123_FFFFFFFF,
579 },
580 ];
581
582 for tc in &test_cases {
583 let actual = tc.pointers.as_combined();
584 assert_eq!(tc.combined, actual);
585 }
586 }
587
588 #[test]
589 fn test_available_space() {
590 struct TestCase {
591 pointers: RingPointers,
592 buffer_size: u32,
593 expected: u32,
594 }
595 let test_cases = [
596 TestCase { pointers: RingPointers::new(0, 0), buffer_size: 16, expected: 16 },
597 TestCase { pointers: RingPointers::new(3, 3), buffer_size: 16, expected: 16 },
598 TestCase { pointers: RingPointers::new(3, 7), buffer_size: 16, expected: 12 },
599 TestCase { pointers: RingPointers::new(0xFFFFFFFC, 3), buffer_size: 16, expected: 9 },
600 TestCase { pointers: RingPointers::new(0, 16), buffer_size: 16, expected: 0 },
601 ];
602
603 for tc in &test_cases {
604 let buffer = Buffer::try_new(tc.buffer_size).unwrap();
605 let actual = buffer.available_space(tc.pointers);
606 assert_eq!(tc.expected, actual);
607 }
608 }
609
610 #[test]
611 fn test_ring_pointers_available_data() {
612 struct TestCase {
613 pointers: RingPointers,
614 expected: u32,
615 }
616 let test_cases = [
617 TestCase { pointers: RingPointers::new(0, 0), expected: 0 },
618 TestCase { pointers: RingPointers::new(3, 3), expected: 0 },
619 TestCase { pointers: RingPointers::new(3, 7), expected: 4 },
620 TestCase { pointers: RingPointers::new(0, 16), expected: 16 },
621 TestCase { pointers: RingPointers::new(0xFFFFFFFC, 3), expected: 7 },
622 ];
623
624 for tc in &test_cases {
625 let actual = tc.pointers.available_data();
626 assert_eq!(tc.expected, actual);
627 }
628 }
629
630 #[test]
631 fn test_advance_read_pointer() {
632 struct TestCase {
633 initial_pointers: RingPointers,
634 buffer_size: u32,
635 delta: u32,
636 expected: u64,
637 }
638 let test_cases = [
639 TestCase {
640 initial_pointers: RingPointers::new(1, 6),
641 buffer_size: 16,
642 delta: 4,
643 expected: 0x5_00000006,
644 },
645 TestCase {
646 initial_pointers: RingPointers::new(0, 16),
647 buffer_size: 16,
648 delta: 16,
649 expected: 0x10_00000010,
650 },
651 TestCase {
652 initial_pointers: RingPointers::new(0xFFFFFFFC, 12),
653 buffer_size: 16,
654 delta: 5,
655 expected: 0x1_0000000C,
656 },
657 ];
658
659 for tc in &test_cases {
660 let buffer = Buffer::try_new(tc.buffer_size).unwrap();
661
662 let initial = tc.initial_pointers.as_combined();
663 buffer.combined_pointers.store(initial, Ordering::Release);
664
665 buffer.advance_read_pointer(tc.initial_pointers, tc.delta).unwrap();
666 let actual = buffer.combined_pointers.load(Ordering::Acquire);
667 assert_eq!(tc.expected, actual);
668 }
669 }
670
671 #[test]
672 fn test_advance_write_pointer() {
673 struct TestCase {
674 initial_pointers: RingPointers,
675 buffer_size: u32,
676 delta: u32,
677 expected: u64,
678 }
679 let test_cases = [
680 TestCase {
681 initial_pointers: RingPointers::new(7, 9),
682 buffer_size: 16,
683 delta: 4,
684 expected: 0x7_0000000D,
685 },
686 TestCase {
687 initial_pointers: RingPointers::new(0, 0),
688 buffer_size: 16,
689 delta: 16,
690 expected: 0x10,
691 },
692 TestCase {
693 initial_pointers: RingPointers::new(0xFFFFFFF1, 0xFFFFFFFC),
694 buffer_size: 16,
695 delta: 5,
696 expected: 0xFFFFFFF100000001,
697 },
698 ];
699
700 for tc in &test_cases {
701 let buffer = Buffer::try_new(tc.buffer_size).unwrap();
702
703 let initial = tc.initial_pointers.as_combined();
704 buffer.combined_pointers.store(initial, Ordering::Release);
705
706 advance_write_pointer(
707 &buffer.combined_pointers,
708 buffer.size as u32,
709 tc.initial_pointers,
710 tc.delta,
711 )
712 .unwrap();
713 let actual = buffer.combined_pointers.load(Ordering::Acquire);
714 assert_eq!(tc.expected, actual);
715 }
716 }
717
718 #[test]
719 fn test_try_new() {
720 {
722 assert!(Buffer::try_new(256).is_ok());
723 }
724
725 {
727 assert_eq!(Buffer::try_new(u32::MAX).err().unwrap(), Status::INVALID_ARGS);
728 }
729
730 {
732 assert_eq!(Buffer::try_new(100).err().unwrap(), Status::INVALID_ARGS);
733 }
734
735 {
737 assert_eq!(Buffer::try_new_in(256, NoOpAllocator).err().unwrap(), Status::NO_MEMORY);
738 }
739 }
740
741 #[test]
742 fn test_read_write_single_threaded() {
743 const STORAGE_SIZE: usize = 256;
744 let mut src = [0u8; STORAGE_SIZE];
745 for i in 0..STORAGE_SIZE {
746 src[i] = (i * 17 + 5) as u8;
747 }
748
749 struct TestCase {
750 write_size: u32,
751 read_size: u32,
752 expected_read_size: u32,
753 expected_reserve_status: Result<(), Status>,
754 expected_read_status: Result<(), Status>,
755 initial_pointers: RingPointers,
756 use_copy_out_err_fn: bool,
757 }
758
759 let test_cases = [
760 TestCase {
761 write_size: (STORAGE_SIZE / 2) as u32,
762 read_size: (STORAGE_SIZE / 2) as u32,
763 expected_read_size: (STORAGE_SIZE / 2) as u32,
764 expected_reserve_status: Ok(()),
765 expected_read_status: Ok(()),
766 initial_pointers: RingPointers::new(0, 0),
767 use_copy_out_err_fn: false,
768 },
769 TestCase {
770 write_size: (STORAGE_SIZE / 2) as u32,
771 read_size: (STORAGE_SIZE / 4) as u32,
772 expected_read_size: (STORAGE_SIZE / 4) as u32,
773 expected_reserve_status: Ok(()),
774 expected_read_status: Ok(()),
775 initial_pointers: RingPointers::new(0, 0),
776 use_copy_out_err_fn: false,
777 },
778 TestCase {
779 write_size: STORAGE_SIZE as u32,
780 read_size: STORAGE_SIZE as u32,
781 expected_read_size: STORAGE_SIZE as u32,
782 expected_reserve_status: Ok(()),
783 expected_read_status: Ok(()),
784 initial_pointers: RingPointers::new(0, 0),
785 use_copy_out_err_fn: false,
786 },
787 TestCase {
788 write_size: (STORAGE_SIZE / 4) as u32,
789 read_size: (STORAGE_SIZE / 2) as u32,
790 expected_read_size: (STORAGE_SIZE / 4) as u32,
791 expected_reserve_status: Ok(()),
792 expected_read_status: Ok(()),
793 initial_pointers: RingPointers::new(0, 0),
794 use_copy_out_err_fn: false,
795 },
796 TestCase {
797 write_size: STORAGE_SIZE as u32,
798 read_size: STORAGE_SIZE as u32,
799 expected_read_size: STORAGE_SIZE as u32,
800 expected_reserve_status: Ok(()),
801 expected_read_status: Ok(()),
802 initial_pointers: RingPointers::new(
803 (STORAGE_SIZE / 2) as u32,
804 (STORAGE_SIZE / 2) as u32,
805 ),
806 use_copy_out_err_fn: false,
807 },
808 TestCase {
809 write_size: STORAGE_SIZE as u32,
810 read_size: STORAGE_SIZE as u32,
811 expected_read_size: STORAGE_SIZE as u32,
812 expected_reserve_status: Ok(()),
813 expected_read_status: Ok(()),
814 initial_pointers: RingPointers::new(0xFFFFFFFA, 0xFFFFFFFA),
815 use_copy_out_err_fn: false,
816 },
817 TestCase {
818 write_size: 64,
819 read_size: 0,
820 expected_read_size: 0,
821 expected_reserve_status: Err(Status::NO_SPACE),
822 expected_read_status: Ok(()),
823 initial_pointers: RingPointers::new(0, (STORAGE_SIZE - 48) as u32),
824 use_copy_out_err_fn: false,
825 },
826 TestCase {
827 write_size: STORAGE_SIZE as u32,
828 read_size: (STORAGE_SIZE / 2) as u32,
829 expected_read_size: 0,
830 expected_reserve_status: Ok(()),
831 expected_read_status: Err(Status::BAD_STATE),
832 initial_pointers: RingPointers::new(0, 0),
833 use_copy_out_err_fn: true,
834 },
835 ];
836
837 for tc in &test_cases {
838 let mut dst = [0u8; STORAGE_SIZE];
839
840 let mut spsc = Buffer::try_new(STORAGE_SIZE as u32).unwrap();
841
842 let starting_pointers = tc.initial_pointers.as_combined();
843 spsc.combined_pointers.store(starting_pointers, Ordering::Release);
844
845 let reservation = spsc.reserve(tc.write_size);
846 if let Err(e) = &tc.expected_reserve_status {
847 match reservation {
848 Err(actual_err) => assert_eq!(actual_err, *e),
849 Ok(_) => panic!("expected reserve to fail with {:?}, but it succeeded", e),
850 }
851 continue;
852 }
853 let mut reservation = reservation.unwrap();
854
855 reservation.write(&src[..tc.write_size as usize]).unwrap();
856 reservation.commit().unwrap();
857
858 let copy_out_fn = |offset: u32, src_slice: &[u8]| -> Result<(), Status> {
859 let offset = offset as usize;
860 assert!(offset + src_slice.len() <= dst.len());
861 dst[offset..offset + src_slice.len()].copy_from_slice(src_slice);
862 Ok(())
863 };
864
865 let copy_out_err_fn =
866 |_offset: u32, _src_slice: &[u8]| -> Result<(), Status> { Err(Status::BAD_STATE) };
867
868 let read_result = if tc.use_copy_out_err_fn {
869 spsc.read(copy_out_err_fn, tc.read_size)
870 } else {
871 spsc.read(copy_out_fn, tc.read_size)
872 };
873
874 if let Err(e) = &tc.expected_read_status {
875 assert_eq!(read_result.unwrap_err(), *e);
876 assert_eq!(spsc.load_pointers().available_data(), tc.write_size);
877 continue;
878 }
879
880 let read_bytes = read_result.unwrap();
881 assert_eq!(read_bytes, tc.expected_read_size);
882 assert_eq!(
883 &dst[..tc.expected_read_size as usize],
884 &src[..tc.expected_read_size as usize]
885 );
886 }
887 }
888
889 #[test]
890 fn test_drain() {
891 const STORAGE_SIZE: u32 = 256;
892 let mut spsc = Buffer::try_new(STORAGE_SIZE).unwrap();
893
894 let mut reservation = spsc.reserve(STORAGE_SIZE / 2).unwrap();
895 let write_data = [b'f'; (STORAGE_SIZE / 2) as usize];
896 reservation.write(&write_data).unwrap();
897 reservation.commit().unwrap();
898
899 assert_eq!(spsc.load_pointers().available_data(), STORAGE_SIZE / 2);
900
901 spsc.drain().unwrap();
902 assert_eq!(spsc.load_pointers().available_data(), 0);
903 }
904
905 #[test]
906 fn test_commit_error() {
907 const STORAGE_SIZE: u32 = 256;
908 let mut spsc = Buffer::try_new(STORAGE_SIZE).unwrap();
909
910 let mut reservation = spsc.reserve(STORAGE_SIZE / 2).unwrap();
911 let write_data = [b'f'; (STORAGE_SIZE / 2) as usize - 1];
913 reservation.write(&write_data).unwrap();
914 assert_eq!(reservation.commit(), Err(Status::BAD_STATE));
915 }
916
917 #[test]
918 fn test_write_error() {
919 const STORAGE_SIZE: u32 = 256;
920 let mut spsc = Buffer::try_new(STORAGE_SIZE).unwrap();
921
922 let mut reservation = spsc.reserve(STORAGE_SIZE / 2).unwrap();
923 let write_data = [b'f'; (STORAGE_SIZE / 2) as usize + 1];
925 assert_eq!(reservation.write(&write_data), Err(Status::BUFFER_TOO_SMALL));
926 reservation.committed = true;
927 }
928
929 #[test]
930 fn test_from_raw_parts() {
931 let mut mock_storage = [0u8; 256];
932
933 let mut spsc =
935 unsafe { Buffer::from_raw_parts(mock_storage.as_mut_ptr(), mock_storage.len()) };
936
937 let mut reservation = spsc.reserve(100).unwrap();
939 let write_data = [b'x'; 100];
940 reservation.write(&write_data).unwrap();
941 reservation.commit().unwrap();
942
943 assert_eq!(spsc.load_pointers().available_data(), 100);
944 assert_eq!(spsc.combined_pointers.load(Ordering::Relaxed) & 0xffffffff, 100); assert_eq!(&mock_storage[..100], &write_data[..]);
946 }
947
948 #[test]
949 fn test_reserve_zero() {
950 const STORAGE_SIZE: u32 = 256;
951 let mut spsc = Buffer::try_new(STORAGE_SIZE).unwrap();
952 match spsc.reserve(0) {
953 Err(e) => assert_eq!(e, Status::INVALID_ARGS),
954 Ok(_) => panic!("reserve(0) should fail with INVALID_ARGS"),
955 }
956 }
957
958 #[test]
959 fn test_reserve_too_large() {
960 const STORAGE_SIZE: u32 = 256;
961 let mut spsc = Buffer::try_new(STORAGE_SIZE).unwrap();
962 match spsc.reserve(u32::MAX) {
963 Err(e) => assert_eq!(e, Status::INVALID_ARGS),
964 Ok(_) => panic!("reserve(u32::MAX) should fail with INVALID_ARGS"),
965 }
966 }
967
968 #[test]
969 fn test_reserve_and_read_null_storage() {
970 let mut spsc = Buffer::<NoOpAllocator>::empty();
971
972 assert!(!spsc.is_valid());
973 assert_eq!(spsc.reserve(16).err(), Some(Status::BAD_STATE));
974 let read_result = spsc.read(|_, _| Ok(()), 16);
975 assert_eq!(read_result.err(), Some(Status::BAD_STATE));
976 }
977
978 #[test]
979 fn test_reserve_at_break() {
980 const STORAGE_SIZE: u32 = 16;
981 let mut mock_storage = [0u8; STORAGE_SIZE as usize];
986
987 let mut spsc =
989 unsafe { Buffer::from_raw_parts(mock_storage.as_mut_ptr(), mock_storage.len()) };
990 spsc.combined_pointers.store((3u64 << 32) | 15u64, Ordering::Release);
991
992 let mut reservation = spsc.reserve(4).unwrap();
993 assert_eq!(reservation.region1.len(), 1);
994 assert_eq!(reservation.region2.len(), 3);
995
996 let data = [1, 2, 3, 4];
997 reservation.write(&data).unwrap();
998 reservation.commit().unwrap();
999
1000 assert_eq!(mock_storage[15], 1);
1001 assert_eq!(mock_storage[0], 2);
1002 assert_eq!(mock_storage[1], 3);
1003 assert_eq!(mock_storage[2], 4);
1004 }
1005
1006 #[test]
1007 fn test_cpp_rust_integration() {
1008 #[link(name = "c++")]
1009 unsafe extern "C" {
1010 fn cpp_spsc_allocate(size: u32) -> *mut Buffer<NoOpAllocator>;
1011 fn cpp_spsc_free(spsc: *mut Buffer<NoOpAllocator>);
1012 fn cpp_spsc_write(spsc: *mut Buffer<NoOpAllocator>, data: *const u8, len: u32) -> i32;
1013 fn cpp_spsc_read(spsc: *mut Buffer<NoOpAllocator>, dst: *mut u8, len: u32) -> i32;
1014 }
1015
1016 let spsc_ptr = unsafe { cpp_spsc_allocate(256) };
1018 assert!(!spsc_ptr.is_null());
1019
1020 let spsc = unsafe { &mut *spsc_ptr };
1022
1023 let write_data = b"Hello from C++!";
1025 let cpp_write_status =
1026 unsafe { cpp_spsc_write(spsc_ptr, write_data.as_ptr(), write_data.len() as u32) };
1027 assert_eq!(cpp_write_status, 0); let mut read_buf = [0u8; 100];
1031 let bytes_read = spsc
1032 .read(
1033 |_, src| {
1034 read_buf[..src.len()].copy_from_slice(src);
1035 Ok(())
1036 },
1037 write_data.len() as u32,
1038 )
1039 .unwrap();
1040 assert_eq!(bytes_read, write_data.len() as u32);
1041 assert_eq!(&read_buf[..bytes_read as usize], write_data);
1042
1043 let rust_write_data = b"Hello from Rust!";
1045 let mut reservation = spsc.reserve(rust_write_data.len() as u32).unwrap();
1046 reservation.write(rust_write_data).unwrap();
1047 reservation.commit().unwrap();
1048
1049 let mut cpp_read_buf = [0u8; 100];
1051 let cpp_read_bytes = unsafe {
1052 cpp_spsc_read(spsc_ptr, cpp_read_buf.as_mut_ptr(), rust_write_data.len() as u32)
1053 };
1054 assert_eq!(cpp_read_bytes, rust_write_data.len() as i32);
1055 assert_eq!(&cpp_read_buf[..cpp_read_bytes as usize], rust_write_data);
1056
1057 unsafe {
1059 cpp_spsc_free(spsc_ptr);
1060 }
1061 }
1062}