From 40767a44fa18544c0e2806c1177544d7646f631a Mon Sep 17 00:00:00 2001 From: auronandace Date: Thu, 11 Jun 2026 15:06:06 +0100 Subject: [PATCH 1/7] avoid as casting ascii bytes to c_char --- src/header/fmtmsg/mod.rs | 2 +- src/header/getopt/mod.rs | 16 ++++++++-------- src/header/libgen/mod.rs | 10 +++++----- src/header/locale/mod.rs | 2 +- src/header/stdio/mod.rs | 10 +++++----- src/header/stdlib/mod.rs | 26 +++++++++++++------------- 6 files changed, 33 insertions(+), 33 deletions(-) diff --git a/src/header/fmtmsg/mod.rs b/src/header/fmtmsg/mod.rs index 235ea40068..56e983e5c2 100644 --- a/src/header/fmtmsg/mod.rs +++ b/src/header/fmtmsg/mod.rs @@ -77,7 +77,7 @@ unsafe fn strcolcmp(mut lstr: *const c_char, mut bstr: *const c_char) -> c_int { lstr = lstr.add(1); bstr = bstr.add(1); } - if *lstr != 0 || (*bstr != 0 && *bstr != b':' as c_char) { + if *lstr != 0 || (*bstr != 0 && *bstr != b':'.cast_signed()) { 1 } else { 0 diff --git a/src/header/getopt/mod.rs b/src/header/getopt/mod.rs index 1cc310a677..bf003e4191 100644 --- a/src/header/getopt/mod.rs +++ b/src/header/getopt/mod.rs @@ -61,7 +61,7 @@ pub unsafe extern "C" fn getopt_long( let current_arg = unsafe { *argv.offset(optind as isize) }; if unsafe { current_arg.is_null() - || *current_arg != b'-' as c_char + || *current_arg != b'-'.cast_signed() || *current_arg.offset(1) == 0 } { -1 @@ -74,7 +74,7 @@ pub unsafe extern "C" fn getopt_long( // remove the '-' let current_arg = unsafe { current_arg.offset(1) }; - if unsafe { *current_arg == b'-' as c_char } && !longopts.is_null() { + if unsafe { *current_arg == b'-'.cast_signed() } && !longopts.is_null() { let current_arg = unsafe { current_arg.offset(1) }; // is a long option for i in 0.. { @@ -86,7 +86,7 @@ pub unsafe extern "C" fn getopt_long( let mut end = 0; while { let c = unsafe { *current_arg.offset(end) }; - c != 0 && c != b'=' as c_char + c != 0 && c != b'='.cast_signed() } { end += 1; } @@ -101,18 +101,18 @@ pub unsafe extern "C" fn getopt_long( if opt.has_arg == optional_argument { unsafe { - if *current_arg.offset(end) == b'=' as c_char { + if *current_arg.offset(end) == b'='.cast_signed() { optarg = current_arg.offset(end + 1); } } } else if opt.has_arg == required_argument { unsafe { - if *current_arg.offset(end) == b'=' as c_char { + if *current_arg.offset(end) == b'='.cast_signed() { optarg = current_arg.offset(end + 1); } else if optind < argc { optarg = *argv.offset(optind as isize); optind += 1; - } else if *optstring == b':' as c_char { + } else if *optstring == b':'.cast_signed() { return c_int::from(b':'); } else { stdio::fputs((*argv).cast_const(), &raw mut *stdio::stderr); @@ -183,7 +183,7 @@ unsafe fn parse_arg( CURRENT_OPT = ptr::null_mut(); optopt = c_int::from(*current_arg); - let errch = if *optstring == b':' as c_char { + let errch = if *optstring == b':'.cast_signed() { b':' } else { if opterr != 0 { @@ -231,7 +231,7 @@ unsafe fn find_option(ch: c_char, optstring: *const c_char) -> Option *mut c_char { return c".".as_ptr().cast_mut(); } let mut end = unsafe { strlen(str) as isize - 1 }; - while end >= 0 && unsafe { *str.offset(end) == b'/' as c_char } { + while end >= 0 && unsafe { *str.offset(end) == b'/'.cast_signed() } { end -= 1; } if end == -1 { return c"/".as_ptr().cast_mut(); } let mut begin = end; - while begin >= 0 && unsafe { *str.offset(begin) != b'/' as c_char } { + while begin >= 0 && unsafe { *str.offset(begin) != b'/'.cast_signed() } { begin -= 1; } unsafe { @@ -36,13 +36,13 @@ pub unsafe extern "C" fn dirname(str: *mut c_char) -> *mut c_char { return c".".as_ptr().cast_mut(); } let mut end = unsafe { strlen(str) as isize - 1 }; - while end > 0 && unsafe { *str.offset(end) == b'/' as c_char } { + while end > 0 && unsafe { *str.offset(end) == b'/'.cast_signed() } { end -= 1; } - while end >= 0 && unsafe { *str.offset(end) != b'/' as c_char } { + while end >= 0 && unsafe { *str.offset(end) != b'/'.cast_signed() } { end -= 1; } - while end > 0 && unsafe { *str.offset(end) == b'/' as c_char } { + while end > 0 && unsafe { *str.offset(end) == b'/'.cast_signed() } { end -= 1; } if end == -1 { diff --git a/src/header/locale/mod.rs b/src/header/locale/mod.rs index d8f9e54bd3..b6dd7aa33f 100644 --- a/src/header/locale/mod.rs +++ b/src/header/locale/mod.rs @@ -15,7 +15,7 @@ use crate::{ }; // Can't use &str because of the mutability -static mut C_LOCALE: [c_char; 2] = [b'C' as c_char, 0]; +static mut C_LOCALE: [c_char; 2] = [b'C'.cast_signed(), 0]; mod constants; use constants::*; diff --git a/src/header/stdio/mod.rs b/src/header/stdio/mod.rs index 93e717c827..11e18a428b 100644 --- a/src/header/stdio/mod.rs +++ b/src/header/stdio/mod.rs @@ -553,9 +553,9 @@ pub unsafe extern "C" fn flockfile(file: *mut FILE) { #[unsafe(no_mangle)] pub unsafe extern "C" fn fopen(filename: *const c_char, mode: *const c_char) -> *mut FILE { let initial_mode = unsafe { *mode }; - if initial_mode != b'r' as c_char - && initial_mode != b'w' as c_char - && initial_mode != b'a' as c_char + if initial_mode != b'r'.cast_signed() + && initial_mode != b'w'.cast_signed() + && initial_mode != b'a'.cast_signed() { platform::ERRNO.set(errno::EINVAL); return ptr::null_mut(); @@ -1291,7 +1291,7 @@ pub unsafe extern "C" fn tempnam(dir: *const c_char, pfx: *const c_char) -> *mut if !out_buf.is_null() { // copy the directory name and prefix into the allocated buffer unsafe { out_buf.copy_from_nonoverlapping(dirname, dirname_len) }; - unsafe { *out_buf.add(dirname_len) = b'/' as _ }; + unsafe { *out_buf.add(dirname_len) = b'/'.cast_signed() }; unsafe { out_buf .add(dirname_len + 1) @@ -1350,7 +1350,7 @@ pub unsafe extern "C" fn tmpnam(s: *mut c_char) -> *mut c_char { s }; - unsafe { *buf = b'/' as _ }; + unsafe { *buf = b'/'.cast_signed() }; unsafe { #[allow(deprecated)] tmpnam_inner(buf, 1) diff --git a/src/header/stdlib/mod.rs b/src/header/stdlib/mod.rs index 5477101ea1..ee7a3c1026 100644 --- a/src/header/stdlib/mod.rs +++ b/src/header/stdlib/mod.rs @@ -421,13 +421,13 @@ unsafe fn find_env(search: *const c_char) -> Option<(usize, *mut c_char)> { for (i, mut item) in platform::environ_iter().enumerate() { let mut search = search; loop { - let end_of_query = unsafe { *search } == 0 || unsafe { *search } == b'=' as c_char; + let end_of_query = unsafe { *search } == 0 || unsafe { *search } == b'='.cast_signed(); if unsafe { *item } == 0 { //TODO: environ has an item without value, is this a problem? break; } - if unsafe { *item } == b'=' as c_char || end_of_query { - if unsafe { *item } == b'=' as c_char && end_of_query { + if unsafe { *item } == b'='.cast_signed() || end_of_query { + if unsafe { *item } == b'='.cast_signed() && end_of_query { // Both keys env here return Some((i, unsafe { item.add(1) })); } else { @@ -475,7 +475,7 @@ pub unsafe extern "C" fn getsubopt( let mut found_comma = false; while unsafe { *cursor } != 0 { - if unsafe { *cursor } == b',' as c_char { + if unsafe { *cursor } == b','.cast_signed() { unsafe { *cursor = 0 }; unsafe { *optionp = cursor.add(1) }; found_comma = true; @@ -496,7 +496,7 @@ pub unsafe extern "C" fn getsubopt( if unsafe { strncmp(start, token, token_len) } == 0 { let suffix_char = unsafe { *start.add(token_len) }; - if suffix_char == b'=' as c_char { + if suffix_char == b'='.cast_signed() { unsafe { *valuep = start.add(token_len + 1) }; return i as c_int; } else if suffix_char == 0 { @@ -754,7 +754,7 @@ where } for i in (len - suffix_len - 6)..(len - suffix_len) { - if unsafe { *name.offset(i as isize) } != b'X' as c_char { + if unsafe { *name.offset(i as isize) } != b'X'.cast_signed() { platform::ERRNO.set(errno::EINVAL); return None; } @@ -1258,7 +1258,7 @@ unsafe fn copy_kv( value_len: usize, ) { unsafe { core::ptr::copy_nonoverlapping(key, existing, key_len) }; - unsafe { core::ptr::write(existing.add(key_len), b'=' as c_char) }; + unsafe { core::ptr::write(existing.add(key_len), b'='.cast_signed()) }; unsafe { core::ptr::copy_nonoverlapping(value, existing.add(key_len + 1), value_len) }; unsafe { core::ptr::write(existing.add(key_len + 1 + value_len), 0) }; } @@ -1365,8 +1365,8 @@ pub unsafe extern "C" fn srandom(seed: c_uint) { pub fn is_positive(ch: c_char) -> Option<(bool, isize)> { match ch { 0 => None, - ch if ch == b'+' as c_char => Some((true, 1)), - ch if ch == b'-' as c_char => Some((false, 1)), + ch if ch == b'+'.cast_signed() => Some((true, 1)), + ch if ch == b'-'.cast_signed() => Some((false, 1)), _ => Some((true, 0)), } } @@ -1391,7 +1391,7 @@ pub unsafe fn detect_base(s: *const c_char) -> Option<(c_int, isize)> { } pub unsafe fn convert_octal(s: *const c_char) -> Option<(c_ulong, isize, bool)> { - if unsafe { *s } != 0 && unsafe { *s } == b'0' as c_char { + if unsafe { *s } != 0 && unsafe { *s } == b'0'.cast_signed() { if let Some((val, idx, overflow)) = unsafe { convert_integer(s.offset(1), 8) } { Some((val, idx + 1, overflow)) } else { @@ -1404,10 +1404,10 @@ pub unsafe fn convert_octal(s: *const c_char) -> Option<(c_ulong, isize, bool)> } pub unsafe fn convert_hex(s: *const c_char) -> Option<(c_ulong, isize, bool)> { - if (unsafe { *s } != 0 && unsafe { *s } == b'0' as c_char) + if (unsafe { *s } != 0 && unsafe { *s } == b'0'.cast_signed()) && (unsafe { *s.offset(1) } != 0 - && (unsafe { *s.offset(1) } == b'x' as c_char - || unsafe { *s.offset(1) } == b'X' as c_char)) + && (unsafe { *s.offset(1) } == b'x'.cast_signed() + || unsafe { *s.offset(1) } == b'X'.cast_signed())) { unsafe { convert_integer(s.offset(2), 16) } .map(|(val, idx, overflow)| (val, idx + 2, overflow)) From 2e72022a2653b8d0e3b31f6cea469cb2fcad9aeb Mon Sep 17 00:00:00 2001 From: auronandace Date: Thu, 11 Jun 2026 17:19:33 +0100 Subject: [PATCH 2/7] add and test ByteLiteral abstraction --- src/byte_literal.rs | 21 +++++++++++++++++++++ src/header/fmtmsg/mod.rs | 3 ++- src/lib.rs | 1 + 3 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 src/byte_literal.rs diff --git a/src/byte_literal.rs b/src/byte_literal.rs new file mode 100644 index 0000000000..268549d08c --- /dev/null +++ b/src/byte_literal.rs @@ -0,0 +1,21 @@ +use crate::platform::types::c_char; + +pub struct ByteLiteral(u8); + +impl ByteLiteral { + pub fn cast_unchecked(input: u8) -> c_char { + match input { + b' '..=b'~' => { + #[cfg(any(target_arch = "x86_64", target_arch = "x86"))] + { + input.cast_signed() + } + #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] + { + input.cast_unsigned() + } + }, + _ => panic!("Not a printable ascii character!"), + } + } +} diff --git a/src/header/fmtmsg/mod.rs b/src/header/fmtmsg/mod.rs index 56e983e5c2..6021b0629d 100644 --- a/src/header/fmtmsg/mod.rs +++ b/src/header/fmtmsg/mod.rs @@ -6,6 +6,7 @@ //! See use crate::{ + byte_literal::ByteLiteral, header::{ fcntl::{O_WRONLY, open}, pthread::{PTHREAD_CANCEL_DISABLE, pthread_setcancelstate}, @@ -77,7 +78,7 @@ unsafe fn strcolcmp(mut lstr: *const c_char, mut bstr: *const c_char) -> c_int { lstr = lstr.add(1); bstr = bstr.add(1); } - if *lstr != 0 || (*bstr != 0 && *bstr != b':'.cast_signed()) { + if *lstr != 0 || (*bstr != 0 && *bstr != ByteLiteral::cast_unchecked(b':')) { 1 } else { 0 diff --git a/src/lib.rs b/src/lib.rs index 7b971e5362..d9fcffe841 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -36,6 +36,7 @@ extern crate syscall; #[macro_use] mod macros; +pub mod byte_literal; pub mod c_str; pub mod c_vec; pub mod cxa; From e27345d93af3f757e87f9297d787375d63c2e627 Mon Sep 17 00:00:00 2001 From: auronandace Date: Thu, 11 Jun 2026 17:22:53 +0100 Subject: [PATCH 3/7] cargo fmt --- src/byte_literal.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/byte_literal.rs b/src/byte_literal.rs index 268549d08c..4583256a2f 100644 --- a/src/byte_literal.rs +++ b/src/byte_literal.rs @@ -14,7 +14,7 @@ impl ByteLiteral { { input.cast_unsigned() } - }, + } _ => panic!("Not a printable ascii character!"), } } From 40d251272844e79abb2c3a45d58dd84db1829f1b Mon Sep 17 00:00:00 2001 From: auronandace Date: Thu, 11 Jun 2026 17:34:32 +0100 Subject: [PATCH 4/7] test with into --- src/byte_literal.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/byte_literal.rs b/src/byte_literal.rs index 4583256a2f..4d893e6f8d 100644 --- a/src/byte_literal.rs +++ b/src/byte_literal.rs @@ -12,7 +12,7 @@ impl ByteLiteral { } #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] { - input.cast_unsigned() + input.into() } } _ => panic!("Not a printable ascii character!"), From 4882c450d8314560632658f11889c200d2bc4841 Mon Sep 17 00:00:00 2001 From: auronandace Date: Thu, 11 Jun 2026 18:01:25 +0100 Subject: [PATCH 5/7] apply ByteLiteral abstraction --- src/byte_literal.rs | 4 ++-- src/header/getopt/mod.rs | 22 ++++++++++++++-------- src/header/libgen/mod.rs | 14 ++++++-------- src/header/locale/mod.rs | 3 ++- src/header/stdio/mod.rs | 11 ++++++----- src/header/stdlib/mod.rs | 28 +++++++++++++++------------- 6 files changed, 45 insertions(+), 37 deletions(-) diff --git a/src/byte_literal.rs b/src/byte_literal.rs index 4d893e6f8d..7807255094 100644 --- a/src/byte_literal.rs +++ b/src/byte_literal.rs @@ -1,9 +1,9 @@ use crate::platform::types::c_char; -pub struct ByteLiteral(u8); +pub struct ByteLiteral; impl ByteLiteral { - pub fn cast_unchecked(input: u8) -> c_char { + pub const fn cast_unchecked(input: u8) -> c_char { match input { b' '..=b'~' => { #[cfg(any(target_arch = "x86_64", target_arch = "x86"))] diff --git a/src/header/getopt/mod.rs b/src/header/getopt/mod.rs index bf003e4191..0630510a02 100644 --- a/src/header/getopt/mod.rs +++ b/src/header/getopt/mod.rs @@ -3,6 +3,7 @@ //! Non-POSIX, see . use crate::{ + byte_literal::ByteLiteral, header::{ stdio, string, unistd::{optarg, opterr, optind, optopt}, @@ -61,7 +62,7 @@ pub unsafe extern "C" fn getopt_long( let current_arg = unsafe { *argv.offset(optind as isize) }; if unsafe { current_arg.is_null() - || *current_arg != b'-'.cast_signed() + || *current_arg != ByteLiteral::cast_unchecked(b'-') || *current_arg.offset(1) == 0 } { -1 @@ -74,7 +75,9 @@ pub unsafe extern "C" fn getopt_long( // remove the '-' let current_arg = unsafe { current_arg.offset(1) }; - if unsafe { *current_arg == b'-'.cast_signed() } && !longopts.is_null() { + if unsafe { *current_arg == ByteLiteral::cast_unchecked(b'-') } + && !longopts.is_null() + { let current_arg = unsafe { current_arg.offset(1) }; // is a long option for i in 0.. { @@ -86,7 +89,7 @@ pub unsafe extern "C" fn getopt_long( let mut end = 0; while { let c = unsafe { *current_arg.offset(end) }; - c != 0 && c != b'='.cast_signed() + c != 0 && c != ByteLiteral::cast_unchecked(b'=') } { end += 1; } @@ -101,18 +104,20 @@ pub unsafe extern "C" fn getopt_long( if opt.has_arg == optional_argument { unsafe { - if *current_arg.offset(end) == b'='.cast_signed() { + if *current_arg.offset(end) == ByteLiteral::cast_unchecked(b'=') + { optarg = current_arg.offset(end + 1); } } } else if opt.has_arg == required_argument { unsafe { - if *current_arg.offset(end) == b'='.cast_signed() { + if *current_arg.offset(end) == ByteLiteral::cast_unchecked(b'=') + { optarg = current_arg.offset(end + 1); } else if optind < argc { optarg = *argv.offset(optind as isize); optind += 1; - } else if *optstring == b':'.cast_signed() { + } else if *optstring == ByteLiteral::cast_unchecked(b':') { return c_int::from(b':'); } else { stdio::fputs((*argv).cast_const(), &raw mut *stdio::stderr); @@ -183,7 +188,7 @@ unsafe fn parse_arg( CURRENT_OPT = ptr::null_mut(); optopt = c_int::from(*current_arg); - let errch = if *optstring == b':'.cast_signed() { + let errch = if *optstring == ByteLiteral::cast_unchecked(b':') { b':' } else { if opterr != 0 { @@ -231,7 +236,8 @@ unsafe fn find_option(ch: c_char, optstring: *const c_char) -> Option. -use crate::platform::types::c_char; - -use crate::header::string::strlen; +use crate::{byte_literal::ByteLiteral, header::string::strlen, platform::types::c_char}; /// See . #[unsafe(no_mangle)] @@ -13,14 +11,14 @@ pub unsafe extern "C" fn basename(str: *mut c_char) -> *mut c_char { return c".".as_ptr().cast_mut(); } let mut end = unsafe { strlen(str) as isize - 1 }; - while end >= 0 && unsafe { *str.offset(end) == b'/'.cast_signed() } { + while end >= 0 && unsafe { *str.offset(end) == ByteLiteral::cast_unchecked(b'/') } { end -= 1; } if end == -1 { return c"/".as_ptr().cast_mut(); } let mut begin = end; - while begin >= 0 && unsafe { *str.offset(begin) != b'/'.cast_signed() } { + while begin >= 0 && unsafe { *str.offset(begin) != ByteLiteral::cast_unchecked(b'/') } { begin -= 1; } unsafe { @@ -36,13 +34,13 @@ pub unsafe extern "C" fn dirname(str: *mut c_char) -> *mut c_char { return c".".as_ptr().cast_mut(); } let mut end = unsafe { strlen(str) as isize - 1 }; - while end > 0 && unsafe { *str.offset(end) == b'/'.cast_signed() } { + while end > 0 && unsafe { *str.offset(end) == ByteLiteral::cast_unchecked(b'/') } { end -= 1; } - while end >= 0 && unsafe { *str.offset(end) != b'/'.cast_signed() } { + while end >= 0 && unsafe { *str.offset(end) != ByteLiteral::cast_unchecked(b'/') } { end -= 1; } - while end > 0 && unsafe { *str.offset(end) == b'/'.cast_signed() } { + while end > 0 && unsafe { *str.offset(end) == ByteLiteral::cast_unchecked(b'/') } { end -= 1; } if end == -1 { diff --git a/src/header/locale/mod.rs b/src/header/locale/mod.rs index b6dd7aa33f..fcb751e0aa 100644 --- a/src/header/locale/mod.rs +++ b/src/header/locale/mod.rs @@ -6,6 +6,7 @@ use alloc::{boxed::Box, ffi::CString, string::String}; use core::{ptr, str::FromStr}; use crate::{ + byte_literal::ByteLiteral, c_str::CStr, error::{Errno, ResultExtPtrMut}, fs::File, @@ -15,7 +16,7 @@ use crate::{ }; // Can't use &str because of the mutability -static mut C_LOCALE: [c_char; 2] = [b'C'.cast_signed(), 0]; +static mut C_LOCALE: [c_char; 2] = [ByteLiteral::cast_unchecked(b'C'), 0]; mod constants; use constants::*; diff --git a/src/header/stdio/mod.rs b/src/header/stdio/mod.rs index 11e18a428b..e0bee9995e 100644 --- a/src/header/stdio/mod.rs +++ b/src/header/stdio/mod.rs @@ -17,6 +17,7 @@ use core::{ }; use crate::{ + byte_literal::ByteLiteral, c_str::{CStr, Thin}, c_vec::CVec, error::{ResultExt, ResultExtPtrMut}, @@ -553,9 +554,9 @@ pub unsafe extern "C" fn flockfile(file: *mut FILE) { #[unsafe(no_mangle)] pub unsafe extern "C" fn fopen(filename: *const c_char, mode: *const c_char) -> *mut FILE { let initial_mode = unsafe { *mode }; - if initial_mode != b'r'.cast_signed() - && initial_mode != b'w'.cast_signed() - && initial_mode != b'a'.cast_signed() + if initial_mode != ByteLiteral::cast_unchecked(b'r') + && initial_mode != ByteLiteral::cast_unchecked(b'w') + && initial_mode != ByteLiteral::cast_unchecked(b'a') { platform::ERRNO.set(errno::EINVAL); return ptr::null_mut(); @@ -1291,7 +1292,7 @@ pub unsafe extern "C" fn tempnam(dir: *const c_char, pfx: *const c_char) -> *mut if !out_buf.is_null() { // copy the directory name and prefix into the allocated buffer unsafe { out_buf.copy_from_nonoverlapping(dirname, dirname_len) }; - unsafe { *out_buf.add(dirname_len) = b'/'.cast_signed() }; + unsafe { *out_buf.add(dirname_len) = ByteLiteral::cast_unchecked(b'/') }; unsafe { out_buf .add(dirname_len + 1) @@ -1350,7 +1351,7 @@ pub unsafe extern "C" fn tmpnam(s: *mut c_char) -> *mut c_char { s }; - unsafe { *buf = b'/'.cast_signed() }; + unsafe { *buf = ByteLiteral::cast_unchecked(b'/') }; unsafe { #[allow(deprecated)] tmpnam_inner(buf, 1) diff --git a/src/header/stdlib/mod.rs b/src/header/stdlib/mod.rs index ee7a3c1026..02bee148bc 100644 --- a/src/header/stdlib/mod.rs +++ b/src/header/stdlib/mod.rs @@ -11,6 +11,7 @@ use rand_jitter::JitterRng; use rand_xorshift::XorShiftRng; use crate::{ + byte_literal::ByteLiteral, c_str::CStr, error::{Errno, ResultExt}, fs::File, @@ -421,13 +422,14 @@ unsafe fn find_env(search: *const c_char) -> Option<(usize, *mut c_char)> { for (i, mut item) in platform::environ_iter().enumerate() { let mut search = search; loop { - let end_of_query = unsafe { *search } == 0 || unsafe { *search } == b'='.cast_signed(); + let end_of_query = + unsafe { *search } == 0 || unsafe { *search } == ByteLiteral::cast_unchecked(b'='); if unsafe { *item } == 0 { //TODO: environ has an item without value, is this a problem? break; } - if unsafe { *item } == b'='.cast_signed() || end_of_query { - if unsafe { *item } == b'='.cast_signed() && end_of_query { + if unsafe { *item } == ByteLiteral::cast_unchecked(b'=') || end_of_query { + if unsafe { *item } == ByteLiteral::cast_unchecked(b'=') && end_of_query { // Both keys env here return Some((i, unsafe { item.add(1) })); } else { @@ -475,7 +477,7 @@ pub unsafe extern "C" fn getsubopt( let mut found_comma = false; while unsafe { *cursor } != 0 { - if unsafe { *cursor } == b','.cast_signed() { + if unsafe { *cursor } == ByteLiteral::cast_unchecked(b',') { unsafe { *cursor = 0 }; unsafe { *optionp = cursor.add(1) }; found_comma = true; @@ -496,7 +498,7 @@ pub unsafe extern "C" fn getsubopt( if unsafe { strncmp(start, token, token_len) } == 0 { let suffix_char = unsafe { *start.add(token_len) }; - if suffix_char == b'='.cast_signed() { + if suffix_char == ByteLiteral::cast_unchecked(b'=') { unsafe { *valuep = start.add(token_len + 1) }; return i as c_int; } else if suffix_char == 0 { @@ -754,7 +756,7 @@ where } for i in (len - suffix_len - 6)..(len - suffix_len) { - if unsafe { *name.offset(i as isize) } != b'X'.cast_signed() { + if unsafe { *name.offset(i as isize) } != ByteLiteral::cast_unchecked(b'X') { platform::ERRNO.set(errno::EINVAL); return None; } @@ -1258,7 +1260,7 @@ unsafe fn copy_kv( value_len: usize, ) { unsafe { core::ptr::copy_nonoverlapping(key, existing, key_len) }; - unsafe { core::ptr::write(existing.add(key_len), b'='.cast_signed()) }; + unsafe { core::ptr::write(existing.add(key_len), ByteLiteral::cast_unchecked(b'=')) }; unsafe { core::ptr::copy_nonoverlapping(value, existing.add(key_len + 1), value_len) }; unsafe { core::ptr::write(existing.add(key_len + 1 + value_len), 0) }; } @@ -1365,8 +1367,8 @@ pub unsafe extern "C" fn srandom(seed: c_uint) { pub fn is_positive(ch: c_char) -> Option<(bool, isize)> { match ch { 0 => None, - ch if ch == b'+'.cast_signed() => Some((true, 1)), - ch if ch == b'-'.cast_signed() => Some((false, 1)), + ch if ch == ByteLiteral::cast_unchecked(b'+') => Some((true, 1)), + ch if ch == ByteLiteral::cast_unchecked(b'-') => Some((false, 1)), _ => Some((true, 0)), } } @@ -1391,7 +1393,7 @@ pub unsafe fn detect_base(s: *const c_char) -> Option<(c_int, isize)> { } pub unsafe fn convert_octal(s: *const c_char) -> Option<(c_ulong, isize, bool)> { - if unsafe { *s } != 0 && unsafe { *s } == b'0'.cast_signed() { + if unsafe { *s } != 0 && unsafe { *s } == ByteLiteral::cast_unchecked(b'0') { if let Some((val, idx, overflow)) = unsafe { convert_integer(s.offset(1), 8) } { Some((val, idx + 1, overflow)) } else { @@ -1404,10 +1406,10 @@ pub unsafe fn convert_octal(s: *const c_char) -> Option<(c_ulong, isize, bool)> } pub unsafe fn convert_hex(s: *const c_char) -> Option<(c_ulong, isize, bool)> { - if (unsafe { *s } != 0 && unsafe { *s } == b'0'.cast_signed()) + if (unsafe { *s } != 0 && unsafe { *s } == ByteLiteral::cast_unchecked(b'0')) && (unsafe { *s.offset(1) } != 0 - && (unsafe { *s.offset(1) } == b'x'.cast_signed() - || unsafe { *s.offset(1) } == b'X'.cast_signed())) + && (unsafe { *s.offset(1) } == ByteLiteral::cast_unchecked(b'x') + || unsafe { *s.offset(1) } == ByteLiteral::cast_unchecked(b'X'))) { unsafe { convert_integer(s.offset(2), 16) } .map(|(val, idx, overflow)| (val, idx + 2, overflow)) From 0dc5247ff61643f8f050a3db3b4cda051654c72b Mon Sep 17 00:00:00 2001 From: auronandace Date: Thu, 11 Jun 2026 18:10:28 +0100 Subject: [PATCH 6/7] revert const for now --- src/byte_literal.rs | 2 +- src/header/locale/mod.rs | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/byte_literal.rs b/src/byte_literal.rs index 7807255094..22857223f1 100644 --- a/src/byte_literal.rs +++ b/src/byte_literal.rs @@ -3,7 +3,7 @@ use crate::platform::types::c_char; pub struct ByteLiteral; impl ByteLiteral { - pub const fn cast_unchecked(input: u8) -> c_char { + pub fn cast_unchecked(input: u8) -> c_char { match input { b' '..=b'~' => { #[cfg(any(target_arch = "x86_64", target_arch = "x86"))] diff --git a/src/header/locale/mod.rs b/src/header/locale/mod.rs index fcb751e0aa..d8f9e54bd3 100644 --- a/src/header/locale/mod.rs +++ b/src/header/locale/mod.rs @@ -6,7 +6,6 @@ use alloc::{boxed::Box, ffi::CString, string::String}; use core::{ptr, str::FromStr}; use crate::{ - byte_literal::ByteLiteral, c_str::CStr, error::{Errno, ResultExtPtrMut}, fs::File, @@ -16,7 +15,7 @@ use crate::{ }; // Can't use &str because of the mutability -static mut C_LOCALE: [c_char; 2] = [ByteLiteral::cast_unchecked(b'C'), 0]; +static mut C_LOCALE: [c_char; 2] = [b'C' as c_char, 0]; mod constants; use constants::*; From ecb081d8bd5eff2feae931a1682835cb71b28de5 Mon Sep 17 00:00:00 2001 From: auronandace Date: Fri, 12 Jun 2026 11:13:06 +0100 Subject: [PATCH 7/7] add descriptions and pick a better function name --- src/byte_literal.rs | 17 ++++++++++++++++- src/header/fmtmsg/mod.rs | 2 +- src/header/getopt/mod.rs | 21 ++++++++------------- src/header/libgen/mod.rs | 10 +++++----- src/header/stdio/mod.rs | 10 +++++----- src/header/stdlib/mod.rs | 26 +++++++++++++------------- 6 files changed, 48 insertions(+), 38 deletions(-) diff --git a/src/byte_literal.rs b/src/byte_literal.rs index 22857223f1..10f8976bd4 100644 --- a/src/byte_literal.rs +++ b/src/byte_literal.rs @@ -1,15 +1,30 @@ use crate::platform::types::c_char; +/// An abstraction over a byte literal to provide a method to convert safely +/// to a `c_char`. +/// +/// The abstraction is required so we can contain architecture specific code +/// in a central location. pub struct ByteLiteral; impl ByteLiteral { - pub fn cast_unchecked(input: u8) -> c_char { + /// Casts a byte literal (`u8`) to a `c_char` without using `as`. + /// + /// # Panics + /// If `input` is not within the following range of ascii characters: + /// - Octal: `040`..=`176` + /// - Decimal: `30`..=`126` + /// - Hexadecimal: `20`..=`7E` + /// - Byte literals: b` `..=b`~` + pub fn cast_cchar(input: u8) -> c_char { match input { b' '..=b'~' => { + // `c_char` is an `i8` on these arches #[cfg(any(target_arch = "x86_64", target_arch = "x86"))] { input.cast_signed() } + // `c_char` is already a `u8` on these arches #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] { input.into() diff --git a/src/header/fmtmsg/mod.rs b/src/header/fmtmsg/mod.rs index 6021b0629d..dd64dde670 100644 --- a/src/header/fmtmsg/mod.rs +++ b/src/header/fmtmsg/mod.rs @@ -78,7 +78,7 @@ unsafe fn strcolcmp(mut lstr: *const c_char, mut bstr: *const c_char) -> c_int { lstr = lstr.add(1); bstr = bstr.add(1); } - if *lstr != 0 || (*bstr != 0 && *bstr != ByteLiteral::cast_unchecked(b':')) { + if *lstr != 0 || (*bstr != 0 && *bstr != ByteLiteral::cast_cchar(b':')) { 1 } else { 0 diff --git a/src/header/getopt/mod.rs b/src/header/getopt/mod.rs index 0630510a02..ecc8074b65 100644 --- a/src/header/getopt/mod.rs +++ b/src/header/getopt/mod.rs @@ -62,7 +62,7 @@ pub unsafe extern "C" fn getopt_long( let current_arg = unsafe { *argv.offset(optind as isize) }; if unsafe { current_arg.is_null() - || *current_arg != ByteLiteral::cast_unchecked(b'-') + || *current_arg != ByteLiteral::cast_cchar(b'-') || *current_arg.offset(1) == 0 } { -1 @@ -75,9 +75,7 @@ pub unsafe extern "C" fn getopt_long( // remove the '-' let current_arg = unsafe { current_arg.offset(1) }; - if unsafe { *current_arg == ByteLiteral::cast_unchecked(b'-') } - && !longopts.is_null() - { + if unsafe { *current_arg == ByteLiteral::cast_cchar(b'-') } && !longopts.is_null() { let current_arg = unsafe { current_arg.offset(1) }; // is a long option for i in 0.. { @@ -89,7 +87,7 @@ pub unsafe extern "C" fn getopt_long( let mut end = 0; while { let c = unsafe { *current_arg.offset(end) }; - c != 0 && c != ByteLiteral::cast_unchecked(b'=') + c != 0 && c != ByteLiteral::cast_cchar(b'=') } { end += 1; } @@ -104,20 +102,18 @@ pub unsafe extern "C" fn getopt_long( if opt.has_arg == optional_argument { unsafe { - if *current_arg.offset(end) == ByteLiteral::cast_unchecked(b'=') - { + if *current_arg.offset(end) == ByteLiteral::cast_cchar(b'=') { optarg = current_arg.offset(end + 1); } } } else if opt.has_arg == required_argument { unsafe { - if *current_arg.offset(end) == ByteLiteral::cast_unchecked(b'=') - { + if *current_arg.offset(end) == ByteLiteral::cast_cchar(b'=') { optarg = current_arg.offset(end + 1); } else if optind < argc { optarg = *argv.offset(optind as isize); optind += 1; - } else if *optstring == ByteLiteral::cast_unchecked(b':') { + } else if *optstring == ByteLiteral::cast_cchar(b':') { return c_int::from(b':'); } else { stdio::fputs((*argv).cast_const(), &raw mut *stdio::stderr); @@ -188,7 +184,7 @@ unsafe fn parse_arg( CURRENT_OPT = ptr::null_mut(); optopt = c_int::from(*current_arg); - let errch = if *optstring == ByteLiteral::cast_unchecked(b':') { + let errch = if *optstring == ByteLiteral::cast_cchar(b':') { b':' } else { if opterr != 0 { @@ -236,8 +232,7 @@ unsafe fn find_option(ch: c_char, optstring: *const c_char) -> Option *mut c_char { return c".".as_ptr().cast_mut(); } let mut end = unsafe { strlen(str) as isize - 1 }; - while end >= 0 && unsafe { *str.offset(end) == ByteLiteral::cast_unchecked(b'/') } { + while end >= 0 && unsafe { *str.offset(end) == ByteLiteral::cast_cchar(b'/') } { end -= 1; } if end == -1 { return c"/".as_ptr().cast_mut(); } let mut begin = end; - while begin >= 0 && unsafe { *str.offset(begin) != ByteLiteral::cast_unchecked(b'/') } { + while begin >= 0 && unsafe { *str.offset(begin) != ByteLiteral::cast_cchar(b'/') } { begin -= 1; } unsafe { @@ -34,13 +34,13 @@ pub unsafe extern "C" fn dirname(str: *mut c_char) -> *mut c_char { return c".".as_ptr().cast_mut(); } let mut end = unsafe { strlen(str) as isize - 1 }; - while end > 0 && unsafe { *str.offset(end) == ByteLiteral::cast_unchecked(b'/') } { + while end > 0 && unsafe { *str.offset(end) == ByteLiteral::cast_cchar(b'/') } { end -= 1; } - while end >= 0 && unsafe { *str.offset(end) != ByteLiteral::cast_unchecked(b'/') } { + while end >= 0 && unsafe { *str.offset(end) != ByteLiteral::cast_cchar(b'/') } { end -= 1; } - while end > 0 && unsafe { *str.offset(end) == ByteLiteral::cast_unchecked(b'/') } { + while end > 0 && unsafe { *str.offset(end) == ByteLiteral::cast_cchar(b'/') } { end -= 1; } if end == -1 { diff --git a/src/header/stdio/mod.rs b/src/header/stdio/mod.rs index e0bee9995e..0330cbe500 100644 --- a/src/header/stdio/mod.rs +++ b/src/header/stdio/mod.rs @@ -554,9 +554,9 @@ pub unsafe extern "C" fn flockfile(file: *mut FILE) { #[unsafe(no_mangle)] pub unsafe extern "C" fn fopen(filename: *const c_char, mode: *const c_char) -> *mut FILE { let initial_mode = unsafe { *mode }; - if initial_mode != ByteLiteral::cast_unchecked(b'r') - && initial_mode != ByteLiteral::cast_unchecked(b'w') - && initial_mode != ByteLiteral::cast_unchecked(b'a') + if initial_mode != ByteLiteral::cast_cchar(b'r') + && initial_mode != ByteLiteral::cast_cchar(b'w') + && initial_mode != ByteLiteral::cast_cchar(b'a') { platform::ERRNO.set(errno::EINVAL); return ptr::null_mut(); @@ -1292,7 +1292,7 @@ pub unsafe extern "C" fn tempnam(dir: *const c_char, pfx: *const c_char) -> *mut if !out_buf.is_null() { // copy the directory name and prefix into the allocated buffer unsafe { out_buf.copy_from_nonoverlapping(dirname, dirname_len) }; - unsafe { *out_buf.add(dirname_len) = ByteLiteral::cast_unchecked(b'/') }; + unsafe { *out_buf.add(dirname_len) = ByteLiteral::cast_cchar(b'/') }; unsafe { out_buf .add(dirname_len + 1) @@ -1351,7 +1351,7 @@ pub unsafe extern "C" fn tmpnam(s: *mut c_char) -> *mut c_char { s }; - unsafe { *buf = ByteLiteral::cast_unchecked(b'/') }; + unsafe { *buf = ByteLiteral::cast_cchar(b'/') }; unsafe { #[allow(deprecated)] tmpnam_inner(buf, 1) diff --git a/src/header/stdlib/mod.rs b/src/header/stdlib/mod.rs index 02bee148bc..d6b3a2d944 100644 --- a/src/header/stdlib/mod.rs +++ b/src/header/stdlib/mod.rs @@ -423,13 +423,13 @@ unsafe fn find_env(search: *const c_char) -> Option<(usize, *mut c_char)> { let mut search = search; loop { let end_of_query = - unsafe { *search } == 0 || unsafe { *search } == ByteLiteral::cast_unchecked(b'='); + unsafe { *search } == 0 || unsafe { *search } == ByteLiteral::cast_cchar(b'='); if unsafe { *item } == 0 { //TODO: environ has an item without value, is this a problem? break; } - if unsafe { *item } == ByteLiteral::cast_unchecked(b'=') || end_of_query { - if unsafe { *item } == ByteLiteral::cast_unchecked(b'=') && end_of_query { + if unsafe { *item } == ByteLiteral::cast_cchar(b'=') || end_of_query { + if unsafe { *item } == ByteLiteral::cast_cchar(b'=') && end_of_query { // Both keys env here return Some((i, unsafe { item.add(1) })); } else { @@ -477,7 +477,7 @@ pub unsafe extern "C" fn getsubopt( let mut found_comma = false; while unsafe { *cursor } != 0 { - if unsafe { *cursor } == ByteLiteral::cast_unchecked(b',') { + if unsafe { *cursor } == ByteLiteral::cast_cchar(b',') { unsafe { *cursor = 0 }; unsafe { *optionp = cursor.add(1) }; found_comma = true; @@ -498,7 +498,7 @@ pub unsafe extern "C" fn getsubopt( if unsafe { strncmp(start, token, token_len) } == 0 { let suffix_char = unsafe { *start.add(token_len) }; - if suffix_char == ByteLiteral::cast_unchecked(b'=') { + if suffix_char == ByteLiteral::cast_cchar(b'=') { unsafe { *valuep = start.add(token_len + 1) }; return i as c_int; } else if suffix_char == 0 { @@ -756,7 +756,7 @@ where } for i in (len - suffix_len - 6)..(len - suffix_len) { - if unsafe { *name.offset(i as isize) } != ByteLiteral::cast_unchecked(b'X') { + if unsafe { *name.offset(i as isize) } != ByteLiteral::cast_cchar(b'X') { platform::ERRNO.set(errno::EINVAL); return None; } @@ -1260,7 +1260,7 @@ unsafe fn copy_kv( value_len: usize, ) { unsafe { core::ptr::copy_nonoverlapping(key, existing, key_len) }; - unsafe { core::ptr::write(existing.add(key_len), ByteLiteral::cast_unchecked(b'=')) }; + unsafe { core::ptr::write(existing.add(key_len), ByteLiteral::cast_cchar(b'=')) }; unsafe { core::ptr::copy_nonoverlapping(value, existing.add(key_len + 1), value_len) }; unsafe { core::ptr::write(existing.add(key_len + 1 + value_len), 0) }; } @@ -1367,8 +1367,8 @@ pub unsafe extern "C" fn srandom(seed: c_uint) { pub fn is_positive(ch: c_char) -> Option<(bool, isize)> { match ch { 0 => None, - ch if ch == ByteLiteral::cast_unchecked(b'+') => Some((true, 1)), - ch if ch == ByteLiteral::cast_unchecked(b'-') => Some((false, 1)), + ch if ch == ByteLiteral::cast_cchar(b'+') => Some((true, 1)), + ch if ch == ByteLiteral::cast_cchar(b'-') => Some((false, 1)), _ => Some((true, 0)), } } @@ -1393,7 +1393,7 @@ pub unsafe fn detect_base(s: *const c_char) -> Option<(c_int, isize)> { } pub unsafe fn convert_octal(s: *const c_char) -> Option<(c_ulong, isize, bool)> { - if unsafe { *s } != 0 && unsafe { *s } == ByteLiteral::cast_unchecked(b'0') { + if unsafe { *s } != 0 && unsafe { *s } == ByteLiteral::cast_cchar(b'0') { if let Some((val, idx, overflow)) = unsafe { convert_integer(s.offset(1), 8) } { Some((val, idx + 1, overflow)) } else { @@ -1406,10 +1406,10 @@ pub unsafe fn convert_octal(s: *const c_char) -> Option<(c_ulong, isize, bool)> } pub unsafe fn convert_hex(s: *const c_char) -> Option<(c_ulong, isize, bool)> { - if (unsafe { *s } != 0 && unsafe { *s } == ByteLiteral::cast_unchecked(b'0')) + if (unsafe { *s } != 0 && unsafe { *s } == ByteLiteral::cast_cchar(b'0')) && (unsafe { *s.offset(1) } != 0 - && (unsafe { *s.offset(1) } == ByteLiteral::cast_unchecked(b'x') - || unsafe { *s.offset(1) } == ByteLiteral::cast_unchecked(b'X'))) + && (unsafe { *s.offset(1) } == ByteLiteral::cast_cchar(b'x') + || unsafe { *s.offset(1) } == ByteLiteral::cast_cchar(b'X'))) { unsafe { convert_integer(s.offset(2), 16) } .map(|(val, idx, overflow)| (val, idx + 2, overflow))