From 62cc63c351cc4ab4c94c33d5ee9f8a59550de77a Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Mon, 6 Oct 2025 11:15:49 +0200 Subject: [PATCH] WIP: Reimpl WPrintfIter also in safe code. --- src/c_str.rs | 10 ++++ src/header/wchar/mod.rs | 3 +- src/header/wchar/wprintf.rs | 102 +++++++++++++++++------------------- 3 files changed, 60 insertions(+), 55 deletions(-) diff --git a/src/c_str.rs b/src/c_str.rs index b39f33a12f..a41f2506ef 100644 --- a/src/c_str.rs +++ b/src/c_str.rs @@ -186,6 +186,16 @@ impl<'a, T: Kind> NulStr<'a, T> { T::c2r(self.ptr.read()) } } + #[inline] + pub fn first_char(self) -> Option { + char::from_u32(self.first().into()) + } + /// Same as `split_first` except also requires that the first char be convertible into `char` + #[inline] + pub fn split_first_char(self) -> Option<(char, Self)> { + self.split_first() + .and_then(|(c, r)| Some((char::from_u32(c.into())?, r))) + } /// Split this string into `Some((first_byte, string_after_that))` or `None` if empty. #[inline] pub fn split_first(self) -> Option<(T::Char, Self)> { diff --git a/src/header/wchar/mod.rs b/src/header/wchar/mod.rs index 84ca2be8b1..0f231b1080 100644 --- a/src/header/wchar/mod.rs +++ b/src/header/wchar/mod.rs @@ -3,6 +3,7 @@ use core::{char, ffi::VaList as va_list, mem, ptr, slice, usize}; use crate::{ + c_str::WStr, header::{ ctype::isspace, errno::{EILSEQ, ENOMEM, ERANGE}, @@ -328,7 +329,7 @@ pub unsafe extern "C" fn vfwprintf( return -1; } - wprintf::wprintf(&mut *stream, format, arg) + wprintf::wprintf(&mut *stream, WStr::from_ptr(format), arg) } #[unsafe(no_mangle)] pub unsafe extern "C" fn fwprintf( diff --git a/src/header/wchar/wprintf.rs b/src/header/wchar/wprintf.rs index a4ffb9d03b..c89b7397e0 100644 --- a/src/header/wchar/wprintf.rs +++ b/src/header/wchar/wprintf.rs @@ -1,5 +1,8 @@ // TODO: generalize with printf impl -use crate::io::{self, Write}; +use crate::{ + c_str::WStr, + io::{self, Write}, +}; use alloc::{ collections::BTreeMap, string::{String, ToString}, @@ -284,10 +287,13 @@ static INF_STR_UPPER: &str = "INF"; static NAN_STR_LOWER: &str = "nan"; static NAN_STR_UPPER: &str = "NAN"; -unsafe fn pop_int_raw(format: &mut *const u32) -> Option { +fn pop_int_raw(format: &mut WStr) -> Option { let mut int = None; - while let Some(digit) = char::from_u32(**format).unwrap_or('\0').to_digit(10) { - *format = format.add(1); + while let Some((digit, rest)) = format + .split_first_char() + .and_then(|(c, r)| Some((c.to_digit(10)?, r))) + { + *format = rest; if int.is_none() { int = Some(0); } @@ -296,27 +302,27 @@ unsafe fn pop_int_raw(format: &mut *const u32) -> Option { } int } -unsafe fn pop_index(format: &mut *const u32) -> Option { +fn pop_index(format: &mut WStr) -> Option { // Peek ahead for a positional argument: let mut format2 = *format; if let Some(i) = pop_int_raw(&mut format2) { - if char::from_u32(*format2) == Some('$') { - *format = format2.add(1); + if let Some(('$', rest)) = format2.split_first_char() { + *format = rest; return Some(i); } } None } -unsafe fn pop_int(format: &mut *const u32) -> Option { - if char::from_u32(**format) == Some('*') { - *format = format.add(1); +fn pop_int(format: &mut WStr) -> Option { + if let Some(('*', rest)) = format.split_first_char() { + *format = rest; Some(pop_index(format).map(Number::Index).unwrap_or(Number::Next)) } else { pop_int_raw(format).map(Number::Static) } } -unsafe fn fmt_int(fmt: u32, i: I) -> String +fn fmt_int(fmt: u32, i: I) -> String where I: fmt::Display + fmt::Octal + fmt::LowerHex + fmt::UpperHex, { @@ -476,8 +482,8 @@ fn fmt_float_nonfinite(w: &mut W, float: c_double, case: FmtCase) -> i } #[derive(Clone, Copy)] -struct WPrintfIter { - format: *const u32, +struct WPrintfIter<'a> { + format: WStr<'a>, } #[derive(Clone, Copy, Debug)] struct WPrintfArg { @@ -494,31 +500,23 @@ struct WPrintfArg { fmtkind: FmtKind, } #[derive(Debug)] -enum WPrintfFmt { - Plain(&'static [u32]), +enum WPrintfFmt<'a> { + Plain(&'a [u32]), Arg(WPrintfArg), } -impl Iterator for WPrintfIter { - type Item = Result; +impl<'a> Iterator for WPrintfIter<'a> { + type Item = Result, ()>; fn next(&mut self) -> Option { unsafe { // Send WPrintfFmt::Plain until the next % - let mut len = 0; - while *self.format.add(len) != 0 && char::from_u32(*self.format.add(len)) != Some('%') { - len += 1; - } - if len > 0 { - let slice = slice::from_raw_parts(self.format as *const u32, len); - self.format = self.format.add(len); - return Some(Ok(WPrintfFmt::Plain(slice))); - } - self.format = self.format.add(len); - if *self.format == 0 { - return None; - } - - // *self.format is guaranteed to be '%' at this point - self.format = self.format.add(1); + let first_percent = match self.format.find_get_subslice_or_all(u32::from(b'%')) { + Err(([], _)) => return None, + Ok((chunk @ [_, ..], rest)) | Err((chunk @ [_, ..], rest)) => { + self.format = rest; + return Some(Ok(WPrintfFmt::Plain(chunk))); + } + Ok(([], rest)) => rest, + }; let mut peekahead = self.format; let index = pop_index(&mut peekahead).map(|i| { @@ -533,8 +531,8 @@ impl Iterator for WPrintfIter { let mut sign_reserve = false; let mut sign_always = false; - loop { - match char::from_u32(*self.format).unwrap_or('\0') { + while let Some((c, rest)) = self.format.split_first_char() { + match c { '#' => alternate = true, '0' => zero = true, '-' => left = true, @@ -542,13 +540,13 @@ impl Iterator for WPrintfIter { '+' => sign_always = true, _ => break, } - self.format = self.format.add(1); + self.format = rest; } // Width and precision: let min_width = pop_int(&mut self.format).unwrap_or(Number::Static(0)); - let precision = if char::from_u32(*self.format) == Some('.') { - self.format = self.format.add(1); + let precision = if let Some(('.', rest)) = self.format.split_first_char() { + self.format = rest; match pop_int(&mut self.format) { int @ Some(_) => int, None => return Some(Err(())), @@ -559,8 +557,8 @@ impl Iterator for WPrintfIter { // Integer size: let mut intkind = IntKind::Int; - loop { - intkind = match char::from_u32(*self.format).unwrap_or('\0') { + while let Some((char, rest)) = self.format.split_first_char() { + intkind = match char { 'h' => { if intkind == IntKind::Short || intkind == IntKind::Byte { IntKind::Byte @@ -582,10 +580,12 @@ impl Iterator for WPrintfIter { _ => break, }; - self.format = self.format.add(1); + self.format = rest; } - let fmt = *self.format; - let fmtkind = match char::from_u32(fmt).unwrap_or('\0') { + let Some((fmt, rest)) = self.format.split_first_char() else { + return Some(Err(())); + }; + let fmtkind = match fmt { '%' => FmtKind::Percent, 'd' | 'i' => FmtKind::Signed, 'o' | 'u' | 'x' | 'X' => FmtKind::Unsigned, @@ -598,7 +598,7 @@ impl Iterator for WPrintfIter { 'n' => FmtKind::GetWritten, _ => return Some(Err(())), }; - self.format = self.format.add(1); + self.format = rest; Some(Ok(WPrintfFmt::Arg(WPrintfArg { index, @@ -610,23 +610,17 @@ impl Iterator for WPrintfIter { min_width, precision, intkind, - fmt, + fmt: fmt as u32, fmtkind, }))) } } } -unsafe fn inner_wprintf( - w: W, - format: *const wchar_t, - mut ap: VaList, -) -> io::Result { +unsafe fn inner_wprintf(w: W, format: WStr, mut ap: VaList) -> io::Result { let w = &mut platform::CountingWriter::new(w); - let iterator = WPrintfIter { - format: format as *const u32, - }; + let iterator = WPrintfIter { format }; // Pre-fetch vararg types let mut varargs = VaListCache::default(); @@ -998,6 +992,6 @@ unsafe fn inner_wprintf( Ok(w.written as c_int) } -pub unsafe fn wprintf(w: W, format: *const wchar_t, ap: VaList) -> c_int { +pub unsafe fn wprintf(w: W, format: WStr, ap: VaList) -> c_int { inner_wprintf(w, format, ap).unwrap_or(-1) }