bytebuf/
bytebuf.rs

1#![warn(unsafe_op_in_unsafe_fn)]
2#![allow(non_camel_case_types)]
3#![allow(clippy::missing_safety_doc)]
4#![allow(unused_unsafe)]
5
6use ffizz_passby::Unboxed;
7
8/// ByteBuffer defines a buffer full of bytes.
9struct ByteBuffer(Vec<u8>);
10
11/// byte_buffer_t contains a string, for sharing with Rust code.  Its contents are
12/// opaque and should not be manipulated.
13///
14/// ```c
15/// strurct byte_buffer_t {
16///     _reserved size_t[N];
17/// };
18/// ```
19#[derive(Clone, Copy)]
20#[repr(C)]
21pub struct byte_buffer_t([u64; 4]); // must be larger than ByteBuffer
22
23type UnboxedByteBuffer = Unboxed<ByteBuffer, byte_buffer_t>;
24
25/// Return a new empty byte_buffer_t.
26#[no_mangle]
27pub unsafe extern "C" fn byte_buffer_new() -> byte_buffer_t {
28    unsafe { UnboxedByteBuffer::return_val(ByteBuffer(Vec::new())) }
29}
30
31/// Initialize the given byte_buffer_t to an empty value.
32#[no_mangle]
33pub unsafe extern "C" fn byte_buffer_init(bb: *mut byte_buffer_t) {
34    unsafe { UnboxedByteBuffer::to_out_param_nonnull(ByteBuffer(Vec::new()), bb) }
35}
36
37/// Free a byte_buffer_t.
38#[no_mangle]
39pub unsafe extern "C" fn byte_buffer_free(bb: *mut byte_buffer_t) {
40    let bb = unsafe { UnboxedByteBuffer::take_ptr_nonnull(bb) };
41    drop(bb); // just to be explicit
42}
43
44/// Checksum a byte_buffer_t's contents by XOR'ing all bytes together.
45#[no_mangle]
46pub unsafe extern "C" fn byte_buffer_checksum(bb: *const byte_buffer_t) -> u8 {
47    unsafe {
48        UnboxedByteBuffer::with_ref_nonnull(bb, |bb| {
49            // ok, not the most exciting "checksum"!
50            bb.0.iter().copied().reduce(|a, b| a ^ b).unwrap_or(0)
51        })
52    }
53}
54
55/// Add a byte to the byte buffer.
56#[no_mangle]
57pub unsafe extern "C" fn byte_buffer_push(bb: *mut byte_buffer_t, b: u8) {
58    unsafe { UnboxedByteBuffer::with_ref_mut_nonnull(bb, |bb| bb.0.push(b)) }
59}
60
61/// Combine two byte buffers, returning a new byte buffer containing the bytes
62/// from both inputs.  This function consumes its inputs and they must not be
63/// used after it returns.
64#[no_mangle]
65pub unsafe extern "C" fn byte_buffer_combine(
66    bb1: *mut byte_buffer_t,
67    bb2: *mut byte_buffer_t,
68) -> byte_buffer_t {
69    let mut bb1 = unsafe { UnboxedByteBuffer::take_ptr_nonnull(bb1) };
70    let bb2 = unsafe { UnboxedByteBuffer::take_ptr_nonnull(bb2) };
71
72    // modify bb1 in place (but it's not in the caller's location anymore)
73    bb1.0.extend(&bb2.0[..]);
74    unsafe { UnboxedByteBuffer::return_val(bb1) }
75}
76
77fn main() {
78    let mut bb1 = unsafe { byte_buffer_new() };
79    assert_eq!(
80        unsafe { byte_buffer_checksum(&bb1 as *const byte_buffer_t) },
81        0
82    );
83
84    unsafe {
85        byte_buffer_push(&mut bb1 as *mut byte_buffer_t, 0xf0);
86        byte_buffer_push(&mut bb1 as *mut byte_buffer_t, 0x0f);
87    }
88
89    assert_eq!(
90        unsafe { byte_buffer_checksum(&bb1 as *const byte_buffer_t) },
91        0xff
92    );
93
94    let mut bb2: byte_buffer_t = unsafe { std::mem::zeroed() }; // this is easier in C!
95    unsafe {
96        byte_buffer_init(&mut bb2 as *mut byte_buffer_t);
97    }
98    unsafe {
99        byte_buffer_push(&mut bb2 as *mut byte_buffer_t, 0xa5);
100        byte_buffer_push(&mut bb2 as *mut byte_buffer_t, 0x5b);
101    }
102    assert_eq!(
103        unsafe { byte_buffer_checksum(&bb2 as *const byte_buffer_t) },
104        0xfe
105    );
106
107    let mut bb3 = unsafe {
108        byte_buffer_combine(
109            &mut bb1 as *mut byte_buffer_t,
110            &mut bb2 as *mut byte_buffer_t,
111        )
112    };
113
114    // -- note that bb1 and bb2 are invalid now.  Sorry, Rust!
115
116    assert_eq!(
117        unsafe { byte_buffer_checksum(&bb3 as *const byte_buffer_t) },
118        0xff ^ 0xfe
119    );
120
121    unsafe { byte_buffer_free(&mut bb3 as *mut byte_buffer_t) };
122}