vexide_core/io/
stdio.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
use no_std_io::io::{self, Write};
use vex_sdk::{vexSerialReadChar, vexSerialWriteBuffer};

use crate::sync::{Mutex, MutexGuard};

pub(crate) const STDIO_CHANNEL: u32 = 1;

static STDOUT: Mutex<StdoutRaw> = Mutex::new(StdoutRaw);
static STDIN: Mutex<StdinRaw> = Mutex::new(StdinRaw);

/// A handle to a raw instance of the serial output stream of this program.
///
/// This handle is not synchronized or buffered in any fashion. Constructed via
/// the `stdout_raw` function.
struct StdoutRaw;

impl io::Write for StdoutRaw {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        let written =
            unsafe { vexSerialWriteBuffer(STDIO_CHANNEL, buf.as_ptr(), buf.len() as u32) };

        if written == -1 {
            return Err(io::Error::new(
                io::ErrorKind::Other,
                "Internal write error occurred.",
            ));
        }

        self.flush()?;

        Ok(written as usize)
    }

    fn flush(&mut self) -> io::Result<()> {
        // Serial buffers are automatically flushed every 2mS by vexTasksRun
        // in our background processing task.
        Ok(())
    }
}

/// A locked serial output stream.
/// Only one of these can exist at a time and writes occur without waiting.
pub struct StdoutLock<'a> {
    inner: MutexGuard<'a, StdoutRaw>,
}

impl Write for StdoutLock<'_> {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        self.inner.write(buf)
    }

    fn flush(&mut self) -> io::Result<()> {
        self.inner.flush()
    }
}

/// A handle to the serial output stream of this program.
///
/// An instance of this can be obtained using the [`stdout`] function.
pub struct Stdout(());

/// Constructs a handle to the serial output stream
#[must_use]
pub const fn stdout() -> Stdout {
    Stdout(())
}

impl Stdout {
    /// The size of the internal VEXOs FIFO serial out buffer.
    pub const INTERNAL_BUFFER_SIZE: usize = 2048;

    /// Locks the stdout for writing.
    /// This function will wait until the lock is acquired.
    pub async fn lock(&self) -> StdoutLock<'static> {
        StdoutLock {
            inner: STDOUT.lock().await,
        }
    }
    /// Attempts to lock the stdout for writing.
    ///
    /// This function will return `None` if the lock could not be acquired.
    pub fn try_lock(&self) -> Option<StdoutLock<'static>> {
        Some(StdoutLock {
            inner: STDOUT.try_lock()?,
        })
    }
}

struct StdinRaw;

impl io::Read for StdinRaw {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        let mut iterator = buf.iter_mut();

        let mut byte: i32;
        let mut written: usize = 0;

        // Little but cursed, but hey it gets the job done...
        while {
            byte = unsafe { vexSerialReadChar(STDIO_CHANNEL) };
            byte != -1
        } {
            if let Some(next) = iterator.next() {
                *next = byte as u8;
                written += 1;
            } else {
                return Ok(written);
            }
        }

        Ok(written)
    }
}

/// A locked serial input stream.
/// Only one of these can exist at a time and reads occur without waiting.
pub struct StdinLock<'a> {
    inner: MutexGuard<'a, StdinRaw>,
}

impl io::Read for StdinLock<'_> {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        self.inner.read(buf)
    }
}

/// A handle to the serial input stream of this program.
///
/// An instance of this can be obtained using the [`stdin`] function.
pub struct Stdin(());

impl Stdin {
    /// The size of the internal VEXos serial in buffer.
    pub const STDIN_BUFFER_SIZE: usize = 4096;

    /// Locks the stdin for reading.
    /// This function is blocking and will wait until the lock is acquired.
    pub async fn lock(&self) -> StdinLock<'static> {
        StdinLock {
            inner: STDIN.lock().await,
        }
    }
    /// Attempts to lock the stdin for writing.
    ///
    /// This function will return `None` if the lock could not be acquired.
    pub fn try_lock(&self) -> Option<StdinLock<'static>> {
        Some(StdinLock {
            inner: STDIN.try_lock()?,
        })
    }
}

/// Constructs a handle to the serial input stream.
#[must_use]
pub const fn stdin() -> Stdin {
    Stdin(())
}

#[macro_export]
/// Prints a message to the standard output and appends a newline.
macro_rules! println {
    () => {
        $crate::print!("\n")
    };
    ($($arg:tt)*) => {
		$crate::print!("{}\n", format_args!($($arg)*))
	};
}
pub use println;

#[macro_export]
/// Prints a message to the standard output.
macro_rules! print {
    ($($arg:tt)*) => {{
		{
			use $crate::io::Write;
            // Panic on print if stdout is not available.
            // While this is less than ideal,
            // the alternative is either ingoring the print, a complete deadlock, or writing unsafely without locking.
            let mut stdout =  $crate::io::stdout().try_lock().expect("Attempted to print while stdout was already locked.");
            if let Err(e) = stdout.write_fmt(format_args!($($arg)*)) {
                panic!("failed printing to stdout: {e}");
            }
		}
    }};
}
pub use print;

#[macro_export]
#[expect(
    edition_2024_expr_fragment_specifier,
    reason = "OK for this macro to accept `const {}` expressions"
)]
/// Prints and returns the value of a given expression for quick and dirty debugging.
macro_rules! dbg {
    () => {
        $crate::println!("[{}:{}]", file!(), line!())
    };
    ($val:expr $(,)?) => {
        match $val {
            tmp => {
                $crate::println!("[{}:{}] {} = {:#?}", file!(), line!(), stringify!($val), &tmp);
                tmp
            }
        }
    };
    ($($val:expr),+ $(,)?) => {
        ($($crate::dbg!($val)),+,)
    };
}
pub use dbg;