nstd_io/
lib.rs

1use nstd_core::slice::NSTDSlice;
2use std::{
3    ffi::{CStr, CString},
4    io::{self, prelude::*, BufReader, BufWriter},
5    os::raw::{c_char, c_int},
6    ptr,
7};
8#[cfg(feature = "deps")]
9pub mod deps {
10    pub use nstd_core;
11}
12
13/// Attempts to flush stdout.
14/// Returns: `int errc` - Nonzero on error.
15#[inline]
16#[cfg_attr(feature = "clib", no_mangle)]
17pub unsafe extern "C" fn nstd_io_flush() -> c_int {
18    match io::stdout().flush() {
19        Ok(_) => 0,
20        _ => 1,
21    }
22}
23
24/// Writes a single character to stdout.
25/// Parameters:
26///     `const char ch` - Character to write.
27/// Returns: `int errc` - Nonzero on error.
28#[inline]
29#[cfg_attr(feature = "clib", no_mangle)]
30pub unsafe extern "C" fn nstd_io_write_char(ch: c_char) -> c_int {
31    static_nstd_write(&[ch as u8], io::stdout())
32}
33
34/// Writes `str` to stdout.
35/// Parameters:
36///     `const char *const str` - String to write to stdout.
37/// Returns: `int errc` - Nonzero on error.
38#[inline]
39#[cfg_attr(feature = "clib", no_mangle)]
40pub unsafe extern "C" fn nstd_io_write(str: *const c_char) -> c_int {
41    static_nstd_write(CStr::from_ptr(str).to_bytes(), io::stdout())
42}
43
44/// Writes `str` to stdout with an additional newline.
45/// Parameters:
46///     `const char *const str` - String to write to stdout.
47/// Returns: `int errc` - Nonzero on error.
48#[cfg_attr(feature = "clib", no_mangle)]
49pub unsafe extern "C" fn nstd_io_write_line(str: *const c_char) -> c_int {
50    let mut str = CStr::from_ptr(str).to_string_lossy().to_string();
51    str.push('\n');
52    static_nstd_write(str.as_bytes(), io::stdout())
53}
54
55/// Writes a raw byte slice to stdout.
56/// Parameters:
57///     `const NSTDSlice *const bytes` - The byte slice to write to stdout.
58/// Returns: `int errc` - Nonzero on error.
59#[inline]
60#[cfg_attr(feature = "clib", no_mangle)]
61pub unsafe extern "C" fn nstd_io_write_raw(bytes: &NSTDSlice) -> c_int {
62    let bytes = std::slice::from_raw_parts(bytes.ptr.raw.cast(), bytes.byte_count());
63    static_nstd_write(bytes, io::stdout())
64}
65
66/// Attempts to flush stderr.
67/// Returns: `int errc` - Nonzero on error.
68#[inline]
69#[cfg_attr(feature = "clib", no_mangle)]
70pub unsafe extern "C" fn nstd_io_flush_err() -> c_int {
71    match io::stderr().flush() {
72        Ok(_) => 0,
73        _ => 1,
74    }
75}
76
77/// Writes a single character to stderr.
78/// Parameters:
79///     `const char ch` - Character to write.
80/// Returns: `int errc` - Nonzero on error.
81#[inline]
82#[cfg_attr(feature = "clib", no_mangle)]
83pub unsafe extern "C" fn nstd_io_write_char_err(ch: c_char) -> c_int {
84    static_nstd_write(&[ch as u8], io::stderr())
85}
86
87/// Writes `str` to stderr.
88/// Parameters:
89///     `const char *const str` - String to write to stderr.
90/// Returns: `int errc` - Nonzero on error.
91#[inline]
92#[cfg_attr(feature = "clib", no_mangle)]
93pub unsafe extern "C" fn nstd_io_write_err(str: *const c_char) -> c_int {
94    static_nstd_write(CStr::from_ptr(str).to_bytes(), io::stderr())
95}
96
97/// Writes `str` to stderr with an additional newline.
98/// Parameters:
99///     `const char *const str` - String to write to stderr.
100/// Returns: `int errc` - Nonzero on error.
101#[cfg_attr(feature = "clib", no_mangle)]
102pub unsafe extern "C" fn nstd_io_write_line_err(str: *const c_char) -> c_int {
103    let mut str = CStr::from_ptr(str).to_string_lossy().to_string();
104    str.push('\n');
105    static_nstd_write(str.as_bytes(), io::stderr())
106}
107
108/// Writes a raw byte slice to stderr.
109/// Parameters:
110///     `const NSTDSlice *const bytes` - The byte slice to write to stderr.
111/// Returns: `int errc` - Nonzero on error.
112#[inline]
113#[cfg_attr(feature = "clib", no_mangle)]
114pub unsafe extern "C" fn nstd_io_write_raw_err(bytes: &NSTDSlice) -> c_int {
115    let bytes = std::slice::from_raw_parts(bytes.ptr.raw.cast(), bytes.byte_count());
116    static_nstd_write(bytes, io::stderr())
117}
118
119/// Reads a single character from stdin.
120/// Parameters:
121///     `int *errc` - Error code, returns as nonzero on error.
122/// Returns: `char ch` - Character read from stdin.
123#[cfg_attr(feature = "clib", no_mangle)]
124pub unsafe extern "C" fn nstd_io_read_char(errc: *mut c_int) -> c_char {
125    let mut byte = [0];
126    match BufReader::new(io::stdin()).read_exact(&mut byte) {
127        Ok(_) => {
128            *errc = 0;
129            byte[0] as c_char
130        }
131        _ => {
132            *errc = 1;
133            0
134        }
135    }
136}
137
138/// Reads from stdin and returns the read string.
139/// Parameters:
140///     `int *errc` - Error code, returns as nonzero on error.
141/// Returns: `char *in` - String read from stdin.
142#[cfg_attr(feature = "clib", no_mangle)]
143pub unsafe extern "C" fn nstd_io_read(errc: *mut c_int) -> *mut c_char {
144    let mut bytes = static_nstd_read(errc);
145    match *errc {
146        0 => {
147            let mut len = bytes.len();
148            while len > 0 && bytes[len - 1].is_ascii_whitespace() {
149                bytes.pop();
150                len -= 1;
151            }
152            bytes.push(0);
153            CString::from_vec_unchecked(bytes).into_raw()
154        }
155        _ => ptr::null_mut(),
156    }
157}
158
159/// Reads from stdin and returns the read string appending a newline to the end.
160/// Parameters:
161///     `int *errc` - Error code, returns as nonzero on error.
162/// Returns: `char *in` - String read from stdin.
163#[cfg_attr(feature = "clib", no_mangle)]
164pub unsafe extern "C" fn nstd_io_read_line(errc: *mut c_int) -> *mut c_char {
165    let mut bytes = static_nstd_read(errc);
166    bytes.push(b'\0');
167    CString::from_vec_unchecked(bytes).into_raw()
168}
169
170/// Frees memory allocated by `nstd_io_read` and `nstd_io_readline`.
171/// Parameters:
172///     `const char **str` - Pointer to the string returned from the read functions.
173#[inline]
174#[cfg_attr(feature = "clib", no_mangle)]
175pub unsafe extern "C" fn nstd_io_free_read(str: *mut *mut c_char) {
176    drop(CString::from_raw(*str));
177    *str = ptr::null_mut();
178}
179
180/// Writes `bytes` to and flushes `buf`.
181fn static_nstd_write<Stream: Write>(bytes: &[u8], stream: Stream) -> c_int {
182    let mut buf = BufWriter::new(stream);
183    match buf.write_all(bytes) {
184        Ok(_) => match buf.flush() {
185            Ok(_) => 0,
186            _ => 1,
187        },
188        _ => 1,
189    }
190}
191
192/// Reads `Vec<u8>` from stdin.
193unsafe fn static_nstd_read(errc: *mut c_int) -> Vec<u8> {
194    let mut string = String::new();
195    *errc = match BufReader::new(io::stdin()).read_line(&mut string) {
196        Ok(_) => 0,
197        _ => 1,
198    };
199    string.into_bytes()
200}