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
138#[repr(C, align(8))]
148pub struct Buffer<A: Allocator + Default = DefaultAllocator> {
149 combined_pointers: AtomicU64,
152 storage: *mut u8,
154 size: usize,
157 _phantom: core::marker::PhantomData<A>,
158}
159
160impl<A: Allocator + Default> Drop for Buffer<A> {
161 fn drop(&mut self) {
162 if !self.storage.is_null() {
163 let slice_ptr = ptr::slice_from_raw_parts_mut(self.storage, self.size);
164 unsafe {
165 let _ = Box::from_raw_in(slice_ptr, A::default());
166 }
167 }
168 }
169}
170
171impl<A: Allocator + Default> Buffer<A> {
172 const MAX_STORAGE_SIZE: u32 = 1 << 31;
174
175 pub fn try_new_in(size: u32, allocator: A) -> Result<Self, Status> {
178 if size > Self::MAX_STORAGE_SIZE {
179 return Err(Status::INVALID_ARGS);
180 }
181 if !size.is_power_of_two() {
182 return Err(Status::INVALID_ARGS);
183 }
184
185 let storage_box = Box::<[u8], A>::try_new_zeroed_slice_in(size as usize, allocator)
186 .map_err(|_| Status::NO_MEMORY)?;
187 let (storage_ptr, _) = Box::into_raw_with_allocator(storage_box);
188
189 Ok(Self {
190 combined_pointers: AtomicU64::new(0),
191 storage: storage_ptr as *mut u8,
192 size: size as usize,
193 _phantom: core::marker::PhantomData,
194 })
195 }
196
197 pub fn size(&self) -> u32 {
199 self.size as u32
200 }
201
202 pub fn reserve(&mut self, size: u32) -> Result<Reservation<'_>, Status> {
207 if size == 0 || size > Self::MAX_STORAGE_SIZE {
208 return Err(Status::INVALID_ARGS);
209 }
210
211 let storage_len = self.size as u32;
212 if size > storage_len {
213 return Err(Status::NO_SPACE);
214 }
215
216 let initial_state = self.load_pointers();
217 let available_space = self.available_space(initial_state);
218 if available_space < size {
219 return Err(Status::NO_SPACE);
220 }
221
222 let write_offset = self.pointer_to_offset(initial_state.write);
223 let ring_break_distance = storage_len - write_offset;
224 let bytes_before_break = cmp::min(size, ring_break_distance);
225
226 let storage_slice = unsafe { slice::from_raw_parts_mut(self.storage, self.size) };
229 let (left, right) = storage_slice.split_at_mut(write_offset as usize);
230 let region1 = &mut right[..bytes_before_break as usize];
231
232 let region2 = if bytes_before_break < size {
233 let region2_len = size - bytes_before_break;
234 &mut left[..region2_len as usize]
235 } else {
236 &mut []
237 };
238
239 Ok(Reservation {
240 combined_pointers: &self.combined_pointers,
241 storage_len,
242 initial_ring_pointers: initial_state,
243 region1,
244 region2,
245 write_offset: 0,
246 committed: false,
247 })
248 }
249
250 pub fn read<F>(&self, mut copy_fn: F, len: u32) -> Result<u32, Status>
264 where
265 F: FnMut(u32, &[u8]) -> Result<(), Status>,
266 {
267 let initial_state = self.load_pointers();
268 let available_data = initial_state.available_data();
269 if available_data == 0 {
270 return Ok(0);
271 }
272
273 let amount_to_copy = cmp::min(available_data, len) as usize;
274 let read_offset = self.pointer_to_offset(initial_state.read) as usize;
275 let ring_break_distance = self.size - read_offset;
276 let bytes_before_break = cmp::min(amount_to_copy, ring_break_distance);
277
278 let storage_slice = unsafe { slice::from_raw_parts(self.storage, self.size) };
281 let slice1 = &storage_slice[read_offset..read_offset + bytes_before_break];
282 copy_fn(0, slice1)?;
283
284 if bytes_before_break < amount_to_copy {
285 let bytes_after_break = amount_to_copy - bytes_before_break;
286 let slice2 = &storage_slice[..bytes_after_break];
287 copy_fn(bytes_before_break as u32, slice2)?;
288 }
289
290 self.advance_read_pointer(initial_state, amount_to_copy as u32)?;
291 Ok(amount_to_copy as u32)
292 }
293
294 pub fn drain(&self) -> Result<(), Status> {
299 let initial_state = self.load_pointers();
300 let available_data = initial_state.available_data();
301 if available_data == 0 {
302 return Ok(());
303 }
304 self.advance_read_pointer(initial_state, available_data)
305 }
306
307 fn pointer_to_offset(&self, pointer: u32) -> u32 {
312 let storage_len = self.size as u32;
313 pointer & (storage_len - 1)
314 }
315
316 fn available_space(&self, pointers: RingPointers) -> u32 {
318 let storage_len = self.size as u32;
319 storage_len.wrapping_sub(pointers.available_data())
320 }
321
322 fn load_pointers(&self) -> RingPointers {
324 let combined = self.combined_pointers.load(Ordering::Acquire);
325 RingPointers::from_combined(combined)
326 }
327
328 fn advance_read_pointer(&self, initial: RingPointers, delta: u32) -> Result<(), Status> {
339 if delta > initial.available_data() {
340 return Err(Status::INVALID_ARGS);
341 }
342
343 let target_read = initial.read.wrapping_add(delta);
344 let mut starting_pointers = initial.as_combined();
345 let mut target_pointers = RingPointers::new(target_read, initial.write).as_combined();
346
347 loop {
348 match self.combined_pointers.compare_exchange_weak(
349 starting_pointers,
350 target_pointers,
351 Ordering::Release,
352 Ordering::Relaxed,
353 ) {
354 Ok(_) => break,
355 Err(observed_combined) => {
356 starting_pointers = observed_combined;
357 let observed = RingPointers::from_combined(observed_combined);
358 debug_assert_eq!(
359 observed.read, initial.read,
360 "potential concurrent read detected; expected read pointer {}, got {}",
361 initial.read, observed.read
362 );
363 target_pointers = RingPointers::new(target_read, observed.write).as_combined();
364 }
365 }
366 }
367 Ok(())
368 }
369}
370
371fn advance_write_pointer(
382 combined_pointers: &AtomicU64,
383 storage_len: u32,
384 initial: RingPointers,
385 delta: u32,
386) -> Result<(), Status> {
387 let available_data = initial.available_data();
388 if delta > storage_len.checked_sub(available_data).ok_or(Status::INVALID_ARGS)? {
389 return Err(Status::INVALID_ARGS);
390 }
391
392 let target_write = initial.write.wrapping_add(delta);
393 let mut starting_pointers = initial.as_combined();
394 let mut target_pointers = RingPointers::new(initial.read, target_write).as_combined();
395
396 loop {
397 match combined_pointers.compare_exchange_weak(
398 starting_pointers,
399 target_pointers,
400 Ordering::Release,
401 Ordering::Relaxed,
402 ) {
403 Ok(_) => break,
404 Err(observed_combined) => {
405 starting_pointers = observed_combined;
406 let observed = RingPointers::from_combined(observed_combined);
407 debug_assert_eq!(
408 observed.write, initial.write,
409 "potential concurrent write detected; expected write pointer {}, got {}",
410 initial.write, observed.write
411 );
412 target_pointers = RingPointers::new(observed.read, target_write).as_combined();
413 }
414 }
415 }
416 Ok(())
417}
418
419impl Buffer<DefaultAllocator> {
420 pub fn try_new(size: u32) -> Result<Self, Status> {
423 Self::try_new_in(size, DefaultAllocator)
424 }
425}
426
427impl Buffer<NoOpAllocator> {
428 pub unsafe fn from_raw_parts(storage: *mut u8, size: usize) -> Self {
437 Self {
438 combined_pointers: AtomicU64::new(0),
439 storage,
440 size,
441 _phantom: core::marker::PhantomData,
442 }
443 }
444}
445
446#[cfg(test)]
447mod tests {
448 use super::*;
449
450 #[test]
451 fn test_ring_pointers_from_combined() {
452 struct TestCase {
453 combined_pointers: u64,
454 expected: RingPointers,
455 }
456 let test_cases = [
457 TestCase {
458 combined_pointers: 0x01320087_01005382,
459 expected: RingPointers::new(0x1320087, 0x1005382),
460 },
461 TestCase { combined_pointers: 0, expected: RingPointers::new(0, 0) },
462 TestCase {
463 combined_pointers: 0xFFFFFFFF_00004587,
464 expected: RingPointers::new(0xFFFFFFFF, 0x4587),
465 },
466 TestCase {
467 combined_pointers: 0x00004587_FFFFFFFF,
468 expected: RingPointers::new(0x4587, 0xFFFFFFFF),
469 },
470 ];
471
472 for tc in &test_cases {
473 let actual = RingPointers::from_combined(tc.combined_pointers);
474 assert_eq!(tc.expected, actual);
475 }
476 }
477
478 #[test]
479 fn test_ring_pointers_as_combined() {
480 struct TestCase {
481 pointers: RingPointers,
482 combined: u64,
483 }
484 let test_cases = [
485 TestCase {
486 pointers: RingPointers::new(0x1320087, 0x1005382),
487 combined: 0x01320087_01005382,
488 },
489 TestCase { pointers: RingPointers::new(0, 0), combined: 0 },
490 TestCase {
491 pointers: RingPointers::new(0xFFFFFFFF, 0x123),
492 combined: 0xFFFFFFFF_00000123,
493 },
494 TestCase {
495 pointers: RingPointers::new(0x123, 0xFFFFFFFF),
496 combined: 0x00000123_FFFFFFFF,
497 },
498 ];
499
500 for tc in &test_cases {
501 let actual = tc.pointers.as_combined();
502 assert_eq!(tc.combined, actual);
503 }
504 }
505
506 #[test]
507 fn test_available_space() {
508 struct TestCase {
509 pointers: RingPointers,
510 buffer_size: u32,
511 expected: u32,
512 }
513 let test_cases = [
514 TestCase { pointers: RingPointers::new(0, 0), buffer_size: 16, expected: 16 },
515 TestCase { pointers: RingPointers::new(3, 3), buffer_size: 16, expected: 16 },
516 TestCase { pointers: RingPointers::new(3, 7), buffer_size: 16, expected: 12 },
517 TestCase { pointers: RingPointers::new(0xFFFFFFFC, 3), buffer_size: 16, expected: 9 },
518 TestCase { pointers: RingPointers::new(0, 16), buffer_size: 16, expected: 0 },
519 ];
520
521 for tc in &test_cases {
522 let buffer = Buffer::try_new(tc.buffer_size).unwrap();
523 let actual = buffer.available_space(tc.pointers);
524 assert_eq!(tc.expected, actual);
525 }
526 }
527
528 #[test]
529 fn test_ring_pointers_available_data() {
530 struct TestCase {
531 pointers: RingPointers,
532 expected: u32,
533 }
534 let test_cases = [
535 TestCase { pointers: RingPointers::new(0, 0), expected: 0 },
536 TestCase { pointers: RingPointers::new(3, 3), expected: 0 },
537 TestCase { pointers: RingPointers::new(3, 7), expected: 4 },
538 TestCase { pointers: RingPointers::new(0, 16), expected: 16 },
539 TestCase { pointers: RingPointers::new(0xFFFFFFFC, 3), expected: 7 },
540 ];
541
542 for tc in &test_cases {
543 let actual = tc.pointers.available_data();
544 assert_eq!(tc.expected, actual);
545 }
546 }
547
548 #[test]
549 fn test_advance_read_pointer() {
550 struct TestCase {
551 initial_pointers: RingPointers,
552 buffer_size: u32,
553 delta: u32,
554 expected: u64,
555 }
556 let test_cases = [
557 TestCase {
558 initial_pointers: RingPointers::new(1, 6),
559 buffer_size: 16,
560 delta: 4,
561 expected: 0x5_00000006,
562 },
563 TestCase {
564 initial_pointers: RingPointers::new(0, 16),
565 buffer_size: 16,
566 delta: 16,
567 expected: 0x10_00000010,
568 },
569 TestCase {
570 initial_pointers: RingPointers::new(0xFFFFFFFC, 12),
571 buffer_size: 16,
572 delta: 5,
573 expected: 0x1_0000000C,
574 },
575 ];
576
577 for tc in &test_cases {
578 let buffer = Buffer::try_new(tc.buffer_size).unwrap();
579
580 let initial = tc.initial_pointers.as_combined();
581 buffer.combined_pointers.store(initial, Ordering::Release);
582
583 buffer.advance_read_pointer(tc.initial_pointers, tc.delta).unwrap();
584 let actual = buffer.combined_pointers.load(Ordering::Acquire);
585 assert_eq!(tc.expected, actual);
586 }
587 }
588
589 #[test]
590 fn test_advance_write_pointer() {
591 struct TestCase {
592 initial_pointers: RingPointers,
593 buffer_size: u32,
594 delta: u32,
595 expected: u64,
596 }
597 let test_cases = [
598 TestCase {
599 initial_pointers: RingPointers::new(7, 9),
600 buffer_size: 16,
601 delta: 4,
602 expected: 0x7_0000000D,
603 },
604 TestCase {
605 initial_pointers: RingPointers::new(0, 0),
606 buffer_size: 16,
607 delta: 16,
608 expected: 0x10,
609 },
610 TestCase {
611 initial_pointers: RingPointers::new(0xFFFFFFF1, 0xFFFFFFFC),
612 buffer_size: 16,
613 delta: 5,
614 expected: 0xFFFFFFF100000001,
615 },
616 ];
617
618 for tc in &test_cases {
619 let buffer = Buffer::try_new(tc.buffer_size).unwrap();
620
621 let initial = tc.initial_pointers.as_combined();
622 buffer.combined_pointers.store(initial, Ordering::Release);
623
624 advance_write_pointer(
625 &buffer.combined_pointers,
626 buffer.size as u32,
627 tc.initial_pointers,
628 tc.delta,
629 )
630 .unwrap();
631 let actual = buffer.combined_pointers.load(Ordering::Acquire);
632 assert_eq!(tc.expected, actual);
633 }
634 }
635
636 #[test]
637 fn test_try_new() {
638 {
640 assert!(Buffer::try_new(256).is_ok());
641 }
642
643 {
645 assert_eq!(Buffer::try_new(u32::MAX).err().unwrap(), Status::INVALID_ARGS);
646 }
647
648 {
650 assert_eq!(Buffer::try_new(100).err().unwrap(), Status::INVALID_ARGS);
651 }
652
653 {
655 assert_eq!(Buffer::try_new_in(256, NoOpAllocator).err().unwrap(), Status::NO_MEMORY);
656 }
657 }
658
659 #[test]
660 fn test_read_write_single_threaded() {
661 const STORAGE_SIZE: usize = 256;
662 let mut src = [0u8; STORAGE_SIZE];
663 for i in 0..STORAGE_SIZE {
664 src[i] = (i * 17 + 5) as u8;
665 }
666
667 struct TestCase {
668 write_size: u32,
669 read_size: u32,
670 expected_read_size: u32,
671 expected_reserve_status: Result<(), Status>,
672 expected_read_status: Result<(), Status>,
673 initial_pointers: RingPointers,
674 use_copy_out_err_fn: bool,
675 }
676
677 let test_cases = [
678 TestCase {
679 write_size: (STORAGE_SIZE / 2) as u32,
680 read_size: (STORAGE_SIZE / 2) as u32,
681 expected_read_size: (STORAGE_SIZE / 2) as u32,
682 expected_reserve_status: Ok(()),
683 expected_read_status: Ok(()),
684 initial_pointers: RingPointers::new(0, 0),
685 use_copy_out_err_fn: false,
686 },
687 TestCase {
688 write_size: (STORAGE_SIZE / 2) as u32,
689 read_size: (STORAGE_SIZE / 4) as u32,
690 expected_read_size: (STORAGE_SIZE / 4) as u32,
691 expected_reserve_status: Ok(()),
692 expected_read_status: Ok(()),
693 initial_pointers: RingPointers::new(0, 0),
694 use_copy_out_err_fn: false,
695 },
696 TestCase {
697 write_size: STORAGE_SIZE as u32,
698 read_size: STORAGE_SIZE as u32,
699 expected_read_size: STORAGE_SIZE as u32,
700 expected_reserve_status: Ok(()),
701 expected_read_status: Ok(()),
702 initial_pointers: RingPointers::new(0, 0),
703 use_copy_out_err_fn: false,
704 },
705 TestCase {
706 write_size: (STORAGE_SIZE / 4) as u32,
707 read_size: (STORAGE_SIZE / 2) as u32,
708 expected_read_size: (STORAGE_SIZE / 4) as u32,
709 expected_reserve_status: Ok(()),
710 expected_read_status: Ok(()),
711 initial_pointers: RingPointers::new(0, 0),
712 use_copy_out_err_fn: false,
713 },
714 TestCase {
715 write_size: STORAGE_SIZE as u32,
716 read_size: STORAGE_SIZE as u32,
717 expected_read_size: STORAGE_SIZE as u32,
718 expected_reserve_status: Ok(()),
719 expected_read_status: Ok(()),
720 initial_pointers: RingPointers::new(
721 (STORAGE_SIZE / 2) as u32,
722 (STORAGE_SIZE / 2) as u32,
723 ),
724 use_copy_out_err_fn: false,
725 },
726 TestCase {
727 write_size: STORAGE_SIZE as u32,
728 read_size: STORAGE_SIZE as u32,
729 expected_read_size: STORAGE_SIZE as u32,
730 expected_reserve_status: Ok(()),
731 expected_read_status: Ok(()),
732 initial_pointers: RingPointers::new(0xFFFFFFFA, 0xFFFFFFFA),
733 use_copy_out_err_fn: false,
734 },
735 TestCase {
736 write_size: 64,
737 read_size: 0,
738 expected_read_size: 0,
739 expected_reserve_status: Err(Status::NO_SPACE),
740 expected_read_status: Ok(()),
741 initial_pointers: RingPointers::new(0, (STORAGE_SIZE - 48) as u32),
742 use_copy_out_err_fn: false,
743 },
744 TestCase {
745 write_size: STORAGE_SIZE as u32,
746 read_size: (STORAGE_SIZE / 2) as u32,
747 expected_read_size: 0,
748 expected_reserve_status: Ok(()),
749 expected_read_status: Err(Status::BAD_STATE),
750 initial_pointers: RingPointers::new(0, 0),
751 use_copy_out_err_fn: true,
752 },
753 ];
754
755 for tc in &test_cases {
756 let mut dst = [0u8; STORAGE_SIZE];
757
758 let mut spsc = Buffer::try_new(STORAGE_SIZE as u32).unwrap();
759
760 let starting_pointers = tc.initial_pointers.as_combined();
761 spsc.combined_pointers.store(starting_pointers, Ordering::Release);
762
763 let reservation = spsc.reserve(tc.write_size);
764 if let Err(e) = &tc.expected_reserve_status {
765 match reservation {
766 Err(actual_err) => assert_eq!(actual_err, *e),
767 Ok(_) => panic!("expected reserve to fail with {:?}, but it succeeded", e),
768 }
769 continue;
770 }
771 let mut reservation = reservation.unwrap();
772
773 reservation.write(&src[..tc.write_size as usize]).unwrap();
774 reservation.commit().unwrap();
775
776 let copy_out_fn = |offset: u32, src_slice: &[u8]| -> Result<(), Status> {
777 let offset = offset as usize;
778 assert!(offset + src_slice.len() <= dst.len());
779 dst[offset..offset + src_slice.len()].copy_from_slice(src_slice);
780 Ok(())
781 };
782
783 let copy_out_err_fn =
784 |_offset: u32, _src_slice: &[u8]| -> Result<(), Status> { Err(Status::BAD_STATE) };
785
786 let read_result = if tc.use_copy_out_err_fn {
787 spsc.read(copy_out_err_fn, tc.read_size)
788 } else {
789 spsc.read(copy_out_fn, tc.read_size)
790 };
791
792 if let Err(e) = &tc.expected_read_status {
793 assert_eq!(read_result.unwrap_err(), *e);
794 assert_eq!(spsc.load_pointers().available_data(), tc.write_size);
795 continue;
796 }
797
798 let read_bytes = read_result.unwrap();
799 assert_eq!(read_bytes, tc.expected_read_size);
800 assert_eq!(
801 &dst[..tc.expected_read_size as usize],
802 &src[..tc.expected_read_size as usize]
803 );
804 }
805 }
806
807 #[test]
808 fn test_drain() {
809 const STORAGE_SIZE: u32 = 256;
810 let mut spsc = Buffer::try_new(STORAGE_SIZE).unwrap();
811
812 let mut reservation = spsc.reserve(STORAGE_SIZE / 2).unwrap();
813 let write_data = [b'f'; (STORAGE_SIZE / 2) as usize];
814 reservation.write(&write_data).unwrap();
815 reservation.commit().unwrap();
816
817 assert_eq!(spsc.load_pointers().available_data(), STORAGE_SIZE / 2);
818
819 spsc.drain().unwrap();
820 assert_eq!(spsc.load_pointers().available_data(), 0);
821 }
822
823 #[test]
824 fn test_commit_error() {
825 const STORAGE_SIZE: u32 = 256;
826 let mut spsc = Buffer::try_new(STORAGE_SIZE).unwrap();
827
828 let mut reservation = spsc.reserve(STORAGE_SIZE / 2).unwrap();
829 let write_data = [b'f'; (STORAGE_SIZE / 2) as usize - 1];
831 reservation.write(&write_data).unwrap();
832 assert_eq!(reservation.commit(), Err(Status::BAD_STATE));
833 }
834
835 #[test]
836 fn test_write_error() {
837 const STORAGE_SIZE: u32 = 256;
838 let mut spsc = Buffer::try_new(STORAGE_SIZE).unwrap();
839
840 let mut reservation = spsc.reserve(STORAGE_SIZE / 2).unwrap();
841 let write_data = [b'f'; (STORAGE_SIZE / 2) as usize + 1];
843 assert_eq!(reservation.write(&write_data), Err(Status::BUFFER_TOO_SMALL));
844 reservation.committed = true;
845 }
846
847 #[test]
848 fn test_from_raw_parts() {
849 let mut mock_storage = [0u8; 256];
850
851 let mut spsc =
853 unsafe { Buffer::from_raw_parts(mock_storage.as_mut_ptr(), mock_storage.len()) };
854
855 let mut reservation = spsc.reserve(100).unwrap();
857 let write_data = [b'x'; 100];
858 reservation.write(&write_data).unwrap();
859 reservation.commit().unwrap();
860
861 assert_eq!(spsc.load_pointers().available_data(), 100);
862 assert_eq!(spsc.combined_pointers.load(Ordering::Relaxed) & 0xffffffff, 100); assert_eq!(&mock_storage[..100], &write_data[..]);
864 }
865
866 #[test]
867 fn test_reserve_zero() {
868 const STORAGE_SIZE: u32 = 256;
869 let mut spsc = Buffer::try_new(STORAGE_SIZE).unwrap();
870 match spsc.reserve(0) {
871 Err(e) => assert_eq!(e, Status::INVALID_ARGS),
872 Ok(_) => panic!("reserve(0) should fail with INVALID_ARGS"),
873 }
874 }
875
876 #[test]
877 fn test_reserve_too_large() {
878 const STORAGE_SIZE: u32 = 256;
879 let mut spsc = Buffer::try_new(STORAGE_SIZE).unwrap();
880 match spsc.reserve(u32::MAX) {
881 Err(e) => assert_eq!(e, Status::INVALID_ARGS),
882 Ok(_) => panic!("reserve(u32::MAX) should fail with INVALID_ARGS"),
883 }
884 }
885
886 #[test]
887 fn test_reserve_at_break() {
888 const STORAGE_SIZE: u32 = 16;
889 let mut mock_storage = [0u8; STORAGE_SIZE as usize];
894
895 let mut spsc =
897 unsafe { Buffer::from_raw_parts(mock_storage.as_mut_ptr(), mock_storage.len()) };
898 spsc.combined_pointers.store((3u64 << 32) | 15u64, Ordering::Release);
899
900 let mut reservation = spsc.reserve(4).unwrap();
901 assert_eq!(reservation.region1.len(), 1);
902 assert_eq!(reservation.region2.len(), 3);
903
904 let data = [1, 2, 3, 4];
905 reservation.write(&data).unwrap();
906 reservation.commit().unwrap();
907
908 assert_eq!(mock_storage[15], 1);
909 assert_eq!(mock_storage[0], 2);
910 assert_eq!(mock_storage[1], 3);
911 assert_eq!(mock_storage[2], 4);
912 }
913
914 #[test]
915 fn test_cpp_rust_integration() {
916 #[link(name = "c++")]
917 unsafe extern "C" {
918 fn cpp_spsc_allocate(size: u32) -> *mut Buffer<NoOpAllocator>;
919 fn cpp_spsc_free(spsc: *mut Buffer<NoOpAllocator>);
920 fn cpp_spsc_write(spsc: *mut Buffer<NoOpAllocator>, data: *const u8, len: u32) -> i32;
921 fn cpp_spsc_read(spsc: *mut Buffer<NoOpAllocator>, dst: *mut u8, len: u32) -> i32;
922 }
923
924 let spsc_ptr = unsafe { cpp_spsc_allocate(256) };
926 assert!(!spsc_ptr.is_null());
927
928 let spsc = unsafe { &mut *spsc_ptr };
930
931 let write_data = b"Hello from C++!";
933 let cpp_write_status =
934 unsafe { cpp_spsc_write(spsc_ptr, write_data.as_ptr(), write_data.len() as u32) };
935 assert_eq!(cpp_write_status, 0); let mut read_buf = [0u8; 100];
939 let bytes_read = spsc
940 .read(
941 |_, src| {
942 read_buf[..src.len()].copy_from_slice(src);
943 Ok(())
944 },
945 write_data.len() as u32,
946 )
947 .unwrap();
948 assert_eq!(bytes_read, write_data.len() as u32);
949 assert_eq!(&read_buf[..bytes_read as usize], write_data);
950
951 let rust_write_data = b"Hello from Rust!";
953 let mut reservation = spsc.reserve(rust_write_data.len() as u32).unwrap();
954 reservation.write(rust_write_data).unwrap();
955 reservation.commit().unwrap();
956
957 let mut cpp_read_buf = [0u8; 100];
959 let cpp_read_bytes = unsafe {
960 cpp_spsc_read(spsc_ptr, cpp_read_buf.as_mut_ptr(), rust_write_data.len() as u32)
961 };
962 assert_eq!(cpp_read_bytes, rust_write_data.len() as i32);
963 assert_eq!(&cpp_read_buf[..cpp_read_bytes as usize], rust_write_data);
964
965 unsafe {
967 cpp_spsc_free(spsc_ptr);
968 }
969 }
970}