From 40d01d9d57c52bbef54bf68b97f63de044f9fd0a Mon Sep 17 00:00:00 2001 From: jD91mZM2 Date: Mon, 22 Apr 2019 16:25:29 +0200 Subject: [PATCH 1/3] Convert printf internals to iterator --- include.sh | 18 +- src/header/stdio/printf.rs | 609 ++++++++++++++++++++++--------------- src/platform/redox/mod.rs | 2 - 3 files changed, 380 insertions(+), 249 deletions(-) diff --git a/include.sh b/include.sh index 16d265a287..c6821291c4 100755 --- a/include.sh +++ b/include.sh @@ -1,5 +1,7 @@ #!/usr/bin/env bash +SUPRESS_ALL_THE_ERRORS=yes + set -e include="$(realpath "$1")" @@ -7,6 +9,10 @@ include="$(realpath "$1")" cargo build --release --manifest-path cbindgen/Cargo.toml cbindgen="$(realpath target/release/cbindgen)" +if [ "$SUPRESS_ALL_THE_ERRORS" = "yes" ]; then + echo -e "\e[91mNote: Warnings by cbindgen are suppressed in include.sh.\e[0m" +fi + jobs=() for config in src/header/*/cbindgen.toml do @@ -15,10 +21,16 @@ do if [ "${name:0:1}" != "_" ] then header="$include/${name/_//}.h" - pushd "$dir" - "$cbindgen" -c cbindgen.toml -o "$header" mod.rs & + pushd "$dir" > /dev/null + echo "$dir" + cbindgen_cmd='"$cbindgen" -c cbindgen.toml -o "$header" mod.rs' + if [ "$SUPRESS_ALL_THE_ERRORS" = "yes" ]; then + eval "$cbindgen_cmd" 2>&1 | (grep "^ERROR" -A 3 || true) & + else + eval "$cbindgen_cmd" & + fi jobs+=($!) - popd + popd > /dev/null fi done diff --git a/src/header/stdio/printf.rs b/src/header/stdio/printf.rs index e41f24b30e..1c9dcb01a9 100644 --- a/src/header/stdio/printf.rs +++ b/src/header/stdio/printf.rs @@ -20,8 +20,37 @@ enum IntKind { PtrDiff, Size, } +enum FmtKind { + Percent, -enum ArgType { + Signed, + Unsigned, + + Scientific, + Decimal, + AnyNotation, + + String, + Char, + Pointer, + GetWritten +} +#[derive(Clone, Copy, Debug)] +enum Number { + Static(usize), + Index(usize), + Next +} +impl Number { + unsafe fn resolve(&self, ap: &mut BufferedVaList) -> usize { + match *self { + Number::Static(num) => num, + Number::Index(i) => ap.index(ArgKind::Int, i).int as usize, + Number::Next => ap.next(ArgKind::Int).int as usize, + } + } +} +enum ArgKind { Byte, Short, Int, @@ -69,58 +98,58 @@ impl<'a> BufferedVaList<'a> { } } - unsafe fn get_arg(&mut self, ty: ArgType) -> VaArg { + unsafe fn get_arg(&mut self, ty: ArgKind) -> VaArg { match ty { - ArgType::Byte => VaArg { + ArgKind::Byte => VaArg { byte: self.list.arg::(), }, - ArgType::Short => VaArg { + ArgKind::Short => VaArg { short: self.list.arg::(), }, - ArgType::Int => VaArg { + ArgKind::Int => VaArg { int: self.list.arg::(), }, - ArgType::Long => VaArg { + ArgKind::Long => VaArg { long: self.list.arg::(), }, - ArgType::LongLong => VaArg { + ArgKind::LongLong => VaArg { longlong: self.list.arg::(), }, - ArgType::PtrDiff => VaArg { + ArgKind::PtrDiff => VaArg { ptrdiff: self.list.arg::(), }, - ArgType::Size => VaArg { + ArgKind::Size => VaArg { size: self.list.arg::(), }, - ArgType::IntMax => VaArg { + ArgKind::IntMax => VaArg { intmax: self.list.arg::(), }, - ArgType::Double => VaArg { + ArgKind::Double => VaArg { double: self.list.arg::(), }, - ArgType::CharPtr => VaArg { + ArgKind::CharPtr => VaArg { char_ptr: self.list.arg::<*const c_char>(), }, - ArgType::VoidPtr => VaArg { + ArgKind::VoidPtr => VaArg { void_ptr: self.list.arg::<*const c_void>(), }, - ArgType::IntPtr => VaArg { + ArgKind::IntPtr => VaArg { int_ptr: self.list.arg::<*mut c_int>(), }, - ArgType::ArgDefault => VaArg { + ArgKind::ArgDefault => VaArg { arg_default: self.list.arg::(), }, } } - unsafe fn get(&mut self, ty: ArgType, i: Option) -> VaArg { + unsafe fn get(&mut self, ty: ArgKind, i: Option) -> VaArg { match i { None => self.next(ty), Some(i) => self.index(ty, i), } } - unsafe fn next(&mut self, ty: ArgType) -> VaArg { + unsafe fn next(&mut self, ty: ArgKind) -> VaArg { if self.i >= self.buf.len() { let arg = self.get_arg(ty); self.buf.push(arg); @@ -130,7 +159,7 @@ impl<'a> BufferedVaList<'a> { arg } - unsafe fn index(&mut self, ty: ArgType, i: usize) -> VaArg { + unsafe fn index(&mut self, ty: ArgKind, i: usize) -> VaArg { if self.i >= self.buf.len() { while self.buf.len() < (i - 1) { // Getting a usize here most definitely isn't sane, however, @@ -142,7 +171,7 @@ impl<'a> BufferedVaList<'a> { // This chooses the width 10. How does it know the type of 0 and "hello"? // It clearly can't. - let arg = self.get_arg(ArgType::ArgDefault); + let arg = self.get_arg(ArgKind::ArgDefault); self.buf.push(arg); } let arg = self.get_arg(ty); @@ -155,7 +184,7 @@ impl<'a> BufferedVaList<'a> { unsafe fn pop_int_raw(format: &mut *const u8) -> Option { let mut int = None; while let Some(digit) = (**format as char).to_digit(10) { - *format = format.offset(1); + *format = format.add(1); if int.is_none() { int = Some(0); } @@ -164,23 +193,27 @@ unsafe fn pop_int_raw(format: &mut *const u8) -> Option { } int } - -unsafe fn pop_int(format: &mut *const u8, ap: &mut BufferedVaList) -> Option { - if **format == b'*' { - *format = format.offset(1); - - // Peek ahead for a positional argument: - let mut format2 = *format; - if let Some(i) = pop_int_raw(&mut format2) { - if *format2 == b'$' { - *format = format2.offset(1); - return Some(ap.index(ArgType::ArgDefault, i).arg_default); - } +unsafe fn pop_index(format: &mut *const u8) -> Option { + // Peek ahead for a positional argument: + let mut format2 = *format; + if let Some(i) = pop_int_raw(&mut format2) { + if *format2 == b'$' { + *format = format2.add(1); + return Some(i); } - - Some(ap.next(ArgType::ArgDefault).arg_default) + } + None +} +unsafe fn pop_int(format: &mut *const u8) -> Option { + if **format == b'*' { + *format = format.add(1); + Some( + pop_index(format) + .map(Number::Index) + .unwrap_or(Number::Next) + ) } else { - pop_int_raw(format) + pop_int_raw(format).map(Number::Static) } } @@ -193,7 +226,7 @@ where b'u' => i.to_string(), b'x' => format!("{:x}", i), b'X' => format!("{:X}", i), - _ => panic!("fmt_int should never be called with the fmt {}", fmt), + _ => panic!("fmt_int should never be called with the fmt {:?}", fmt as char), } } @@ -305,26 +338,57 @@ fn fmt_float_normal( Ok(string.len()) } -unsafe fn inner_printf(w: W, format: *const c_char, ap: va_list) -> io::Result { - let w = &mut platform::CountingWriter::new(w); - let mut ap = BufferedVaList::new(ap); - let mut format = format as *const u8; - - while *format != 0 { - if *format != b'%' { - w.write_all(&[*format])?; - } else { - format = format.offset(1); - - // Peek ahead to maybe specify argument to fetch from - let mut index = None; - let mut format2 = format; - if let Some(i) = pop_int_raw(&mut format2) { - if *format2 == b'$' { - format = format2.offset(1); - index = Some(i); - } +#[derive(Clone, Copy)] +struct PrintfIter { + format: *const u8 +} +struct PrintfArg { + index: Option, + alternate: bool, + zero: bool, + left: bool, + sign_reserve: bool, + sign_always: bool, + min_width: Number, + precision: Option, + pad_space: Number, + pad_zero: Number, + intkind: IntKind, + fmt: u8, + fmtkind: FmtKind +} +enum PrintfFmt { + Plain(&'static [u8]), + Arg(PrintfArg) +} +impl Iterator for PrintfIter { + type Item = Result; + fn next(&mut self) -> Option { + unsafe { + // Send PrintfFmt::Plain until the next % + let mut len = 0; + while *self.format.add(len) != 0 && *self.format.add(len) != b'%' { + len += 1; } + if len > 0 { + let slice = slice::from_raw_parts(self.format as *const u8, len); + self.format = self.format.add(len); + return Some(Ok(PrintfFmt::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 mut peekahead = self.format; + let index = pop_index(&mut peekahead) + .map(|i| { + self.format = peekahead; + i + }); // Flags: let mut alternate = false; @@ -334,7 +398,7 @@ unsafe fn inner_printf(w: W, format: *const c_char, ap: va_list) -> io let mut sign_always = false; loop { - match *format { + match *self.format { b'#' => alternate = true, b'0' => zero = true, b'-' => left = true, @@ -342,30 +406,30 @@ unsafe fn inner_printf(w: W, format: *const c_char, ap: va_list) -> io b'+' => sign_always = true, _ => break, } - format = format.offset(1); + self.format = self.format.add(1); } // Width and precision: - let min_width = pop_int(&mut format, &mut ap).unwrap_or(0); - let precision = if *format == b'.' { - format = format.offset(1); - match pop_int(&mut format, &mut ap) { + let min_width = pop_int(&mut self.format).unwrap_or(Number::Static(0)); + let precision = if *self.format == b'.' { + self.format = self.format.add(1); + match pop_int(&mut self.format) { int @ Some(_) => int, - None => return Ok(-1), + None => return Some(Err(())), } } else { None }; - let pad_space = if zero { 0 } else { min_width }; - let pad_zero = if zero { min_width } else { 0 }; + let pad_space = if zero { Number::Static(0) } else { min_width }; + let pad_zero = if zero { min_width } else { Number::Static(0) }; // Integer size: - let mut kind = IntKind::Int; + let mut intkind = IntKind::Int; loop { - kind = match *format { + intkind = match *self.format { b'h' => { - if kind == IntKind::Short || kind == IntKind::Byte { + if intkind == IntKind::Short || intkind == IntKind::Byte { IntKind::Byte } else { IntKind::Short @@ -373,7 +437,7 @@ unsafe fn inner_printf(w: W, format: *const c_char, ap: va_list) -> io } b'j' => IntKind::IntMax, b'l' => { - if kind == IntKind::Long || kind == IntKind::LongLong { + if intkind == IntKind::Long || intkind == IntKind::LongLong { IntKind::LongLong } else { IntKind::Long @@ -385,213 +449,270 @@ unsafe fn inner_printf(w: W, format: *const c_char, ap: va_list) -> io _ => break, }; - format = format.offset(1); + self.format = self.format.add(1); } + let fmt = *self.format; + let fmtkind = match fmt { + b'%' => FmtKind::Percent, + b'd' | b'i' => FmtKind::Signed, + b'o' | b'u' | b'x' | b'X' => FmtKind::Unsigned, + b'e' | b'E' => FmtKind::Scientific, + b'f' | b'F' => FmtKind::Decimal, + b'g' | b'G' => FmtKind::AnyNotation, + b's' => FmtKind::String, + b'c' => FmtKind::Char, + b'p' => FmtKind::Pointer, + b'n' => FmtKind::GetWritten, + _ => return Some(Err(())), + }; + self.format = self.format.add(1); - // Finally, type: - match *format { - b'%' => w.write_all(&[b'%'])?, - b'd' | b'i' => { - let string = match kind { - // Per the C standard using va_arg with a type with a size - // less than that of an int for integers and double for floats - // is invalid. As a result any arguments smaller than an int or - // double passed to a function will be promoted to the smallest - // possible size. The va_list::arg function will handle this - // automagically. - IntKind::Byte => ap.get(ArgType::Byte, index).byte.to_string(), - IntKind::Short => ap.get(ArgType::Short, index).short.to_string(), - // Types that will not be promoted - IntKind::Int => ap.get(ArgType::Int, index).int.to_string(), - IntKind::Long => ap.get(ArgType::Long, index).long.to_string(), - IntKind::LongLong => ap.get(ArgType::LongLong, index).longlong.to_string(), - IntKind::PtrDiff => ap.get(ArgType::PtrDiff, index).ptrdiff.to_string(), - IntKind::Size => ap.get(ArgType::Size, index).size.to_string(), - IntKind::IntMax => ap.get(ArgType::IntMax, index).intmax.to_string(), - }; - let positive = !string.starts_with('-'); - let zero = precision == Some(0) && string == "0"; + Some(Ok(PrintfFmt::Arg(PrintfArg { + index, + alternate, + zero, + left, + sign_reserve, + sign_always, + min_width, + precision, + pad_space, + pad_zero, + intkind, + fmt, + fmtkind + }))) + } + } +} - let mut len = string.len(); - let mut final_len = string.len().max(precision.unwrap_or(0)); - if positive && (sign_reserve || sign_always) { - final_len += 1; - } - if zero { - len = 0; - final_len = 0; - } +unsafe fn inner_printf(w: W, format: *const c_char, ap: va_list) -> io::Result { + let w = &mut platform::CountingWriter::new(w); + let mut ap = BufferedVaList::new(ap); - pad(w, !left, b' ', final_len..pad_space)?; + let iterator = PrintfIter { + format: format as *const u8 + }; - let bytes = if positive { - if sign_reserve { - w.write_all(&[b' '])?; - } else if sign_always { - w.write_all(&[b'+'])?; - } - string.as_bytes() - } else { - w.write_all(&[b'-'])?; - &string.as_bytes()[1..] - }; - pad(w, true, b'0', len..precision.unwrap_or(pad_zero))?; + for section in iterator { + let arg = match section { + Ok(PrintfFmt::Plain(text)) => { + w.write_all(text)?; + continue; + }, + Ok(PrintfFmt::Arg(arg)) => arg, + Err(()) => return Ok(-1) + }; + let index = arg.index; + let alternate = arg.alternate; + let zero = arg.zero; + let left = arg.left; + let sign_reserve = arg.sign_reserve; + let sign_always = arg.sign_always; + let min_width = arg.min_width.resolve(&mut ap); + let precision = arg.precision.map(|n| n.resolve(&mut ap)); + let pad_space = arg.pad_space.resolve(&mut ap); + let pad_zero = arg.pad_zero.resolve(&mut ap); + let intkind = arg.intkind; + let fmt = arg.fmt; + let fmtkind = arg.fmtkind; - if !zero { - w.write_all(bytes)?; - } + // Finally, type: + match fmtkind { + FmtKind::Percent => w.write_all(&[b'%'])?, + FmtKind::Signed => { + let string = match intkind { + // Per the C standard using va_arg with a type with a size + // less than that of an int for integers and double for floats + // is invalid. As a result any arguments smaller than an int or + // double passed to a function will be promoted to the smallest + // possible size. The va_list::arg function will handle this + // automagically. + IntKind::Byte => ap.get(ArgKind::Byte, index).byte.to_string(), + IntKind::Short => ap.get(ArgKind::Short, index).short.to_string(), + // Types that will not be promoted + IntKind::Int => ap.get(ArgKind::Int, index).int.to_string(), + IntKind::Long => ap.get(ArgKind::Long, index).long.to_string(), + IntKind::LongLong => ap.get(ArgKind::LongLong, index).longlong.to_string(), + IntKind::PtrDiff => ap.get(ArgKind::PtrDiff, index).ptrdiff.to_string(), + IntKind::Size => ap.get(ArgKind::Size, index).size.to_string(), + IntKind::IntMax => ap.get(ArgKind::IntMax, index).intmax.to_string(), + }; + let positive = !string.starts_with('-'); + let zero = precision == Some(0) && string == "0"; - pad(w, left, b' ', final_len..pad_space)?; + let mut len = string.len(); + let mut final_len = string.len().max(precision.unwrap_or(0)); + if positive && (sign_reserve || sign_always) { + final_len += 1; + } + if zero { + len = 0; + final_len = 0; } - b'o' | b'u' | b'x' | b'X' => { - let fmt = *format; - let string = match kind { - // va_list will promote the following two to a c_int - IntKind::Byte => fmt_int(fmt, ap.get(ArgType::Byte, index).byte), - IntKind::Short => fmt_int(fmt, ap.get(ArgType::Short, index).short), - IntKind::Int => fmt_int(fmt, ap.get(ArgType::Int, index).int), - IntKind::Long => fmt_int(fmt, ap.get(ArgType::Long, index).long), - IntKind::LongLong => { - fmt_int(fmt, ap.get(ArgType::LongLong, index).longlong) - } - IntKind::PtrDiff => fmt_int(fmt, ap.get(ArgType::PtrDiff, index).ptrdiff), - IntKind::Size => fmt_int(fmt, ap.get(ArgType::Size, index).size), - IntKind::IntMax => fmt_int(fmt, ap.get(ArgType::IntMax, index).intmax), - }; - let zero = precision == Some(0) && string == "0"; - // If this int is padded out to be larger than it is, don't - // add an extra zero if octal. - let no_precision = precision.map(|pad| pad < string.len()).unwrap_or(true); + pad(w, !left, b' ', final_len..pad_space)?; - let mut len = string.len(); - let mut final_len = string.len().max(precision.unwrap_or(0)) - + if alternate && string != "0" { - match fmt { - b'o' if no_precision => 1, - b'x' | b'X' => 2, - _ => 0, - } - } else { - 0 - }; - - if zero { - len = 0; - final_len = 0; + let bytes = if positive { + if sign_reserve { + w.write_all(&[b' '])?; + } else if sign_always { + w.write_all(&[b'+'])?; } + string.as_bytes() + } else { + w.write_all(&[b'-'])?; + &string.as_bytes()[1..] + }; + pad(w, true, b'0', len..precision.unwrap_or(pad_zero))?; - pad(w, !left, b' ', final_len..pad_space)?; + if !zero { + w.write_all(bytes)?; + } - if alternate && string != "0" { + pad(w, left, b' ', final_len..pad_space)?; + }, + FmtKind::Unsigned => { + let string = match intkind { + // va_list will promote the following two to a c_int + IntKind::Byte => fmt_int(fmt, ap.get(ArgKind::Byte, index).byte), + IntKind::Short => fmt_int(fmt, ap.get(ArgKind::Short, index).short), + IntKind::Int => fmt_int(fmt, ap.get(ArgKind::Int, index).int), + IntKind::Long => fmt_int(fmt, ap.get(ArgKind::Long, index).long), + IntKind::LongLong => { + fmt_int(fmt, ap.get(ArgKind::LongLong, index).longlong) + } + IntKind::PtrDiff => fmt_int(fmt, ap.get(ArgKind::PtrDiff, index).ptrdiff), + IntKind::Size => fmt_int(fmt, ap.get(ArgKind::Size, index).size), + IntKind::IntMax => fmt_int(fmt, ap.get(ArgKind::IntMax, index).intmax), + }; + let zero = precision == Some(0) && string == "0"; + + // If this int is padded out to be larger than it is, don't + // add an extra zero if octal. + let no_precision = precision.map(|pad| pad < string.len()).unwrap_or(true); + + let mut len = string.len(); + let mut final_len = string.len().max(precision.unwrap_or(0)) + + if alternate && string != "0" { match fmt { - b'o' if no_precision => w.write_all(&[b'0'])?, - b'x' => w.write_all(&[b'0', b'x'])?, - b'X' => w.write_all(&[b'0', b'X'])?, - _ => (), + b'o' if no_precision => 1, + b'x' | b'X' => 2, + _ => 0, } - } - pad(w, true, b'0', len..precision.unwrap_or(pad_zero))?; - - if !zero { - w.write_all(string.as_bytes())?; - } - - pad(w, left, b' ', final_len..pad_space)?; - } - b'e' | b'E' => { - let exp_fmt = *format; - let mut float = ap.get(ArgType::Double, index).double; - let precision = precision.unwrap_or(6); - - fmt_float_exp( - w, exp_fmt, None, false, precision, float, left, pad_space, pad_zero, - )?; - } - b'f' | b'F' => { - let mut float = ap.get(ArgType::Double, index).double; - let precision = precision.unwrap_or(6); - - fmt_float_normal(w, false, precision, float, left, pad_space, pad_zero)?; - } - b'g' | b'G' => { - let exp_fmt = b'E' | (*format & 32); - let mut float = ap.get(ArgType::Double, index).double; - let precision = precision.unwrap_or(6); - - if !fmt_float_exp( - w, - exp_fmt, - Some((-4, precision as isize)), - true, - precision, - float, - left, - pad_space, - pad_zero, - )? { - fmt_float_normal(w, true, precision, float, left, pad_space, pad_zero)?; - } - } - b's' => { - // if kind == IntKind::Long || kind == IntKind::LongLong, handle *const wchar_t - - let ptr = ap.get(ArgType::CharPtr, index).char_ptr; - - if ptr.is_null() { - w.write_all(b"(null)")?; } else { - let mut len = 0; - while *ptr.offset(len) != 0 { - len += 1; - } + 0 + }; - let len = (len as usize).min(precision.unwrap_or(::core::usize::MAX)); + if zero { + len = 0; + final_len = 0; + } - pad(w, !left, b' ', len..pad_space)?; - w.write_all(slice::from_raw_parts(ptr as *const u8, len))?; - pad(w, left, b' ', len..pad_space)?; + pad(w, !left, b' ', final_len..pad_space)?; + + if alternate && string != "0" { + match fmt { + b'o' if no_precision => w.write_all(&[b'0'])?, + b'x' => w.write_all(&[b'0', b'x'])?, + b'X' => w.write_all(&[b'0', b'X'])?, + _ => (), } } - b'c' => { - // if kind == IntKind::Long || kind == IntKind::LongLong, handle wint_t + pad(w, true, b'0', len..precision.unwrap_or(pad_zero))?; - let c = ap.get(ArgType::Byte, index).byte; - - pad(w, !left, b' ', 1..pad_space)?; - w.write_all(&[c as u8])?; - pad(w, left, b' ', 1..pad_space)?; + if !zero { + w.write_all(string.as_bytes())?; } - b'p' => { - let ptr = ap.get(ArgType::VoidPtr, index).int_ptr; - let mut len = 1; - if ptr.is_null() { - len = "(nil)".len(); - } else { - let mut ptr = ptr as usize; - while ptr >= 10 { - ptr /= 10; - len += 1; - } + pad(w, left, b' ', final_len..pad_space)?; + }, + FmtKind::Scientific => { + let mut float = ap.get(ArgKind::Double, index).double; + let precision = precision.unwrap_or(6); + + fmt_float_exp(w, fmt, None, false, precision, float, left, pad_space, pad_zero)?; + }, + FmtKind::Decimal => { + let mut float = ap.get(ArgKind::Double, index).double; + let precision = precision.unwrap_or(6); + + fmt_float_normal(w, false, precision, float, left, pad_space, pad_zero)?; + }, + FmtKind::AnyNotation => { + let exp_fmt = b'E' | (fmt & 32); + let mut float = ap.get(ArgKind::Double, index).double; + let precision = precision.unwrap_or(6); + + if !fmt_float_exp( + w, + exp_fmt, + Some((-4, precision as isize)), + true, + precision, + float, + left, + pad_space, + pad_zero, + )? { + fmt_float_normal(w, true, precision, float, left, pad_space, pad_zero)?; + } + }, + FmtKind::String => { + // if intkind == IntKind::Long || intkind == IntKind::LongLong, handle *const wchar_t + + let ptr = ap.get(ArgKind::CharPtr, index).char_ptr; + + if ptr.is_null() { + w.write_all(b"(null)")?; + } else { + let max = precision.unwrap_or(::core::usize::MAX); + let mut len = 0; + while *ptr.add(len) != 0 && len < max { + len += 1; } pad(w, !left, b' ', len..pad_space)?; - if ptr.is_null() { - write!(w, "(nil)")?; - } else { - write!(w, "0x{:x}", ptr as usize)?; - } + w.write_all(slice::from_raw_parts(ptr as *const u8, len))?; pad(w, left, b' ', len..pad_space)?; } - b'n' => { - let ptr = ap.get(ArgType::IntPtr, index).int_ptr; - *ptr = w.written as c_int; + }, + FmtKind::Char => { + // if intkind == IntKind::Long || intkind == IntKind::LongLong, handle wint_t + + let c = ap.get(ArgKind::Byte, index).byte; + + pad(w, !left, b' ', 1..pad_space)?; + w.write_all(&[c as u8])?; + pad(w, left, b' ', 1..pad_space)?; + }, + FmtKind::Pointer => { + let ptr = ap.get(ArgKind::VoidPtr, index).int_ptr; + + let mut len = 1; + if ptr.is_null() { + len = "(nil)".len(); + } else { + let mut ptr = ptr as usize; + while ptr >= 10 { + ptr /= 10; + len += 1; + } } - _ => return Ok(-1), + + pad(w, !left, b' ', len..pad_space)?; + if ptr.is_null() { + write!(w, "(nil)")?; + } else { + write!(w, "0x{:x}", ptr as usize)?; + } + pad(w, left, b' ', len..pad_space)?; + }, + FmtKind::GetWritten => { + let ptr = ap.get(ArgKind::IntPtr, index).int_ptr; + *ptr = w.written as c_int; } } - format = format.offset(1); } Ok(w.written as c_int) } diff --git a/src/platform/redox/mod.rs b/src/platform/redox/mod.rs index a6092cc7a9..eb45d814b3 100644 --- a/src/platform/redox/mod.rs +++ b/src/platform/redox/mod.rs @@ -151,8 +151,6 @@ impl Pal for Sys { mut argv: *const *mut c_char, mut envp: *const *mut c_char, ) -> c_int { - use alloc::vec::Vec; - let mut file = match File::open(path, fcntl::O_RDONLY | fcntl::O_CLOEXEC) { Ok(file) => file, Err(_) => return -1, From 0a0aec502b701a2b62f66d4e312c550149ccb426 Mon Sep 17 00:00:00 2001 From: jD91mZM2 Date: Tue, 23 Apr 2019 07:34:06 +0200 Subject: [PATCH 2/3] Fix #140 --- src/header/stdio/printf.rs | 401 ++++++++++++++++++++----------------- tests/stdio/printf.c | 2 - 2 files changed, 220 insertions(+), 183 deletions(-) diff --git a/src/header/stdio/printf.rs b/src/header/stdio/printf.rs index 1c9dcb01a9..b825868b85 100644 --- a/src/header/stdio/printf.rs +++ b/src/header/stdio/printf.rs @@ -1,7 +1,8 @@ use alloc::string::String; use alloc::string::ToString; +use alloc::collections::BTreeMap; use alloc::vec::Vec; -use core::ffi::VaList as va_list; +use core::ffi::VaList; use core::ops::Range; use core::{fmt, slice}; use io::{self, Write}; @@ -9,7 +10,15 @@ use io::{self, Write}; use platform; use platform::types::*; -#[derive(PartialEq, Eq)] +// ____ _ _ _ _ +// | __ ) ___ (_) | ___ _ __ _ __ | | __ _| |_ ___ _ +// | _ \ / _ \| | |/ _ \ '__| '_ \| |/ _` | __/ _ (_) +// | |_) | (_) | | | __/ | | |_) | | (_| | || __/_ +// |____/ \___/|_|_|\___|_| | .__/|_|\__,_|\__\___(_) +// |_| + + +#[derive(Clone, Copy, PartialEq, Eq)] enum IntKind { Byte, Short, @@ -20,6 +29,7 @@ enum IntKind { PtrDiff, Size, } +#[derive(Clone, Copy, PartialEq, Eq)] enum FmtKind { Percent, @@ -42,144 +52,113 @@ enum Number { Next } impl Number { - unsafe fn resolve(&self, ap: &mut BufferedVaList) -> usize { - match *self { - Number::Static(num) => num, - Number::Index(i) => ap.index(ArgKind::Int, i).int as usize, - Number::Next => ap.next(ArgKind::Int).int as usize, - } - } -} -enum ArgKind { - Byte, - Short, - Int, - Long, - LongLong, - PtrDiff, - Size, - IntMax, - Double, - CharPtr, - VoidPtr, - IntPtr, - ArgDefault, -} - -#[derive(Clone, Copy)] -union VaArg { - byte: c_char, - short: c_short, - int: c_int, - long: c_long, - longlong: c_longlong, - ptrdiff: ptrdiff_t, - size: ssize_t, - intmax: intmax_t, - double: c_double, - char_ptr: *const c_char, - void_ptr: *const c_void, - int_ptr: *mut c_int, - arg_default: usize, -} - -struct BufferedVaList<'a> { - list: va_list<'a>, - buf: Vec, - i: usize, -} - -impl<'a> BufferedVaList<'a> { - fn new(list: va_list<'a>) -> Self { - Self { - list, - buf: Vec::new(), - i: 0, - } - } - - unsafe fn get_arg(&mut self, ty: ArgKind) -> VaArg { - match ty { - ArgKind::Byte => VaArg { - byte: self.list.arg::(), - }, - ArgKind::Short => VaArg { - short: self.list.arg::(), - }, - ArgKind::Int => VaArg { - int: self.list.arg::(), - }, - ArgKind::Long => VaArg { - long: self.list.arg::(), - }, - ArgKind::LongLong => VaArg { - longlong: self.list.arg::(), - }, - ArgKind::PtrDiff => VaArg { - ptrdiff: self.list.arg::(), - }, - ArgKind::Size => VaArg { - size: self.list.arg::(), - }, - ArgKind::IntMax => VaArg { - intmax: self.list.arg::(), - }, - ArgKind::Double => VaArg { - double: self.list.arg::(), - }, - ArgKind::CharPtr => VaArg { - char_ptr: self.list.arg::<*const c_char>(), - }, - ArgKind::VoidPtr => VaArg { - void_ptr: self.list.arg::<*const c_void>(), - }, - ArgKind::IntPtr => VaArg { - int_ptr: self.list.arg::<*mut c_int>(), - }, - ArgKind::ArgDefault => VaArg { - arg_default: self.list.arg::(), - }, - } - } - - unsafe fn get(&mut self, ty: ArgKind, i: Option) -> VaArg { - match i { - None => self.next(ty), - Some(i) => self.index(ty, i), - } - } - - unsafe fn next(&mut self, ty: ArgKind) -> VaArg { - if self.i >= self.buf.len() { - let arg = self.get_arg(ty); - self.buf.push(arg); - } - let arg = self.buf[self.i]; - self.i += 1; - arg - } - - unsafe fn index(&mut self, ty: ArgKind, i: usize) -> VaArg { - if self.i >= self.buf.len() { - while self.buf.len() < (i - 1) { - // Getting a usize here most definitely isn't sane, however, - // there's no way to know the type! - // Just take this for example: - // - // printf("%*4$d\n", "hi", 0, "hello", 10); - // - // This chooses the width 10. How does it know the type of 0 and "hello"? - // It clearly can't. - - let arg = self.get_arg(ArgKind::ArgDefault); - self.buf.push(arg); + unsafe fn resolve(&self, varargs: &mut VaListCache, ap: &mut VaList) -> usize { + let arg = match *self { + Number::Static(num) => return num, + Number::Index(i) => varargs.get(i-1, ap, None), + Number::Next => { + let i = varargs.i; + varargs.i += 1; + varargs.get(i, ap, None) } - let arg = self.get_arg(ty); - self.buf.push(arg); + }; + match arg { + VaArg::c_char(i) => i as usize, + VaArg::c_double(i) => i as usize, + VaArg::c_int(i) => i as usize, + VaArg::c_long(i) => i as usize, + VaArg::c_longlong(i) => i as usize, + VaArg::c_short(i) => i as usize, + VaArg::intmax_t(i) => i as usize, + VaArg::pointer(i) => i as usize, + VaArg::ptrdiff_t(i) => i as usize, + VaArg::ssize_t(i) => i as usize } - self.buf[i - 1] } } +#[derive(Clone, Copy)] +enum VaArg { + c_char(c_char), + c_double(c_double), + c_int(c_int), + c_long(c_long), + c_longlong(c_longlong), + c_short(c_short), + intmax_t(intmax_t), + pointer(*const c_void), + ptrdiff_t(ptrdiff_t), + ssize_t(ssize_t) +} +impl VaArg { + unsafe fn arg_from(arg: &PrintfArg, ap: &mut VaList) -> VaArg { + // Per the C standard using va_arg with a type with a size + // less than that of an int for integers and double for floats + // is invalid. As a result any arguments smaller than an int or + // double passed to a function will be promoted to the smallest + // possible size. The VaList::arg function will handle this + // automagically. + + match (arg.fmtkind, arg.intkind) { + (FmtKind::Percent, _) => panic!("Can't call arg_from on %"), + + (FmtKind::Char, _) | + (FmtKind::Unsigned, IntKind::Byte) | + (FmtKind::Signed, IntKind::Byte) => VaArg::c_char(ap.arg::()), + (FmtKind::Unsigned, IntKind::Short) | + (FmtKind::Signed, IntKind::Short) => VaArg::c_short(ap.arg::()), + (FmtKind::Unsigned, IntKind::Int) | + (FmtKind::Signed, IntKind::Int) => VaArg::c_int(ap.arg::()), + (FmtKind::Unsigned, IntKind::Long) | + (FmtKind::Signed, IntKind::Long) => VaArg::c_long(ap.arg::()), + (FmtKind::Unsigned, IntKind::LongLong) | + (FmtKind::Signed, IntKind::LongLong) => VaArg::c_longlong(ap.arg::()), + (FmtKind::Unsigned, IntKind::IntMax) | + (FmtKind::Signed, IntKind::IntMax) => VaArg::intmax_t(ap.arg::()), + (FmtKind::Unsigned, IntKind::PtrDiff) | + (FmtKind::Signed, IntKind::PtrDiff) => VaArg::ptrdiff_t(ap.arg::()), + (FmtKind::Unsigned, IntKind::Size) | + (FmtKind::Signed, IntKind::Size) => VaArg::ssize_t(ap.arg::()), + + (FmtKind::AnyNotation, _) | (FmtKind::Decimal, _) | (FmtKind::Scientific, _) + => VaArg::c_double(ap.arg::()), + + (FmtKind::GetWritten, _) | (FmtKind::Pointer, _) | (FmtKind::String, _) + => VaArg::pointer(ap.arg::<*const c_void>()), + } + } +} +#[derive(Default)] +struct VaListCache { + args: Vec, + i: usize +} +impl VaListCache { + unsafe fn get(&mut self, i: usize, ap: &mut VaList, arg: Option<&PrintfArg>) -> VaArg { + if let Some(&arg) = self.args.get(i) { + return arg; + } + while self.args.len() < i { + // We can't POSSIBLY know the type if we reach this + // point. Reaching here means there are unused gaps in the + // arguments. Ultimately we'll have to settle down with + // defaulting to c_int. + self.args.push(VaArg::c_int(ap.arg::())) + } + self.args.push(match arg { + Some(arg) => VaArg::arg_from(arg, ap), + None => VaArg::c_int(ap.arg::()) + }); + self.args[i] + } +} + +// ___ _ _ _ _ +// |_ _|_ __ ___ _ __ | | ___ _ __ ___ ___ _ __ | |_ __ _| |_(_) ___ _ __ _ +// | || '_ ` _ \| '_ \| |/ _ \ '_ ` _ \ / _ \ '_ \| __/ _` | __| |/ _ \| '_ \(_) +// | || | | | | | |_) | | __/ | | | | | __/ | | | || (_| | |_| | (_) | | | |_ +// |___|_| |_| |_| .__/|_|\___|_| |_| |_|\___|_| |_|\__\__,_|\__|_|\___/|_| |_(_) +// |_| + unsafe fn pop_int_raw(format: &mut *const u8) -> Option { let mut int = None; @@ -342,6 +321,7 @@ fn fmt_float_normal( struct PrintfIter { format: *const u8 } +#[derive(Clone, Copy)] struct PrintfArg { index: Option, alternate: bool, @@ -486,14 +466,40 @@ impl Iterator for PrintfIter { } } -unsafe fn inner_printf(w: W, format: *const c_char, ap: va_list) -> io::Result { + +unsafe fn inner_printf(w: W, format: *const c_char, mut ap: VaList) -> io::Result { let w = &mut platform::CountingWriter::new(w); - let mut ap = BufferedVaList::new(ap); let iterator = PrintfIter { format: format as *const u8 }; + // Pre-fetch vararg types + let mut varargs = VaListCache::default(); + let mut positional = BTreeMap::new(); + // ^ NOTE: This depends on the sorted order, do not change to HashMap or whatever + + for section in iterator { + let arg = match section { + Ok(PrintfFmt::Plain(text)) => continue, + Ok(PrintfFmt::Arg(arg)) => arg, + Err(()) => return Ok(-1) + }; + if arg.fmtkind == FmtKind::Percent { + continue; + } + if let Some(i) = arg.index { + positional.insert(i-1, arg); + } else { + varargs.args.push(VaArg::arg_from(&arg, &mut ap)); + } + } + // Make sure, in order, the positional arguments exist with the specified type + for (i, arg) in positional { + varargs.get(i, &mut ap, Some(&arg)); + } + + // Main loop for section in iterator { let arg = match section { Ok(PrintfFmt::Plain(text)) => { @@ -503,40 +509,43 @@ unsafe fn inner_printf(w: W, format: *const c_char, ap: va_list) -> io Ok(PrintfFmt::Arg(arg)) => arg, Err(()) => return Ok(-1) }; - let index = arg.index; let alternate = arg.alternate; let zero = arg.zero; let left = arg.left; let sign_reserve = arg.sign_reserve; let sign_always = arg.sign_always; - let min_width = arg.min_width.resolve(&mut ap); - let precision = arg.precision.map(|n| n.resolve(&mut ap)); - let pad_space = arg.pad_space.resolve(&mut ap); - let pad_zero = arg.pad_zero.resolve(&mut ap); + let min_width = arg.min_width.resolve(&mut varargs, &mut ap); + let precision = arg.precision.map(|n| n.resolve(&mut varargs, &mut ap)); + let pad_space = arg.pad_space.resolve(&mut varargs, &mut ap); + let pad_zero = arg.pad_zero.resolve(&mut varargs, &mut ap); let intkind = arg.intkind; let fmt = arg.fmt; let fmtkind = arg.fmtkind; - // Finally, type: + let index = arg.index + .map(|i| i-1) + .unwrap_or_else(|| if fmtkind == FmtKind::Percent { + 0 + } else { + let i = varargs.i; + varargs.i += 1; + i + }); + match fmtkind { FmtKind::Percent => w.write_all(&[b'%'])?, FmtKind::Signed => { - let string = match intkind { - // Per the C standard using va_arg with a type with a size - // less than that of an int for integers and double for floats - // is invalid. As a result any arguments smaller than an int or - // double passed to a function will be promoted to the smallest - // possible size. The va_list::arg function will handle this - // automagically. - IntKind::Byte => ap.get(ArgKind::Byte, index).byte.to_string(), - IntKind::Short => ap.get(ArgKind::Short, index).short.to_string(), - // Types that will not be promoted - IntKind::Int => ap.get(ArgKind::Int, index).int.to_string(), - IntKind::Long => ap.get(ArgKind::Long, index).long.to_string(), - IntKind::LongLong => ap.get(ArgKind::LongLong, index).longlong.to_string(), - IntKind::PtrDiff => ap.get(ArgKind::PtrDiff, index).ptrdiff.to_string(), - IntKind::Size => ap.get(ArgKind::Size, index).size.to_string(), - IntKind::IntMax => ap.get(ArgKind::IntMax, index).intmax.to_string(), + let string = match varargs.get(index, &mut ap, Some(&arg)) { + VaArg::c_char(i) => i.to_string(), + VaArg::c_double(i) => panic!("this should not be possible"), + VaArg::c_int(i) => i.to_string(), + VaArg::c_long(i) => i.to_string(), + VaArg::c_longlong(i) => i.to_string(), + VaArg::c_short(i) => i.to_string(), + VaArg::intmax_t(i) => i.to_string(), + VaArg::pointer(i) => (i as usize).to_string(), + VaArg::ptrdiff_t(i) => i.to_string(), + VaArg::ssize_t(i) => i.to_string() }; let positive = !string.starts_with('-'); let zero = precision == Some(0) && string == "0"; @@ -573,18 +582,17 @@ unsafe fn inner_printf(w: W, format: *const c_char, ap: va_list) -> io pad(w, left, b' ', final_len..pad_space)?; }, FmtKind::Unsigned => { - let string = match intkind { - // va_list will promote the following two to a c_int - IntKind::Byte => fmt_int(fmt, ap.get(ArgKind::Byte, index).byte), - IntKind::Short => fmt_int(fmt, ap.get(ArgKind::Short, index).short), - IntKind::Int => fmt_int(fmt, ap.get(ArgKind::Int, index).int), - IntKind::Long => fmt_int(fmt, ap.get(ArgKind::Long, index).long), - IntKind::LongLong => { - fmt_int(fmt, ap.get(ArgKind::LongLong, index).longlong) - } - IntKind::PtrDiff => fmt_int(fmt, ap.get(ArgKind::PtrDiff, index).ptrdiff), - IntKind::Size => fmt_int(fmt, ap.get(ArgKind::Size, index).size), - IntKind::IntMax => fmt_int(fmt, ap.get(ArgKind::IntMax, index).intmax), + let string = match varargs.get(index, &mut ap, Some(&arg)) { + VaArg::c_char(i) => fmt_int(fmt, i as c_uchar), + VaArg::c_double(i) => panic!("this should not be possible"), + VaArg::c_int(i) => fmt_int(fmt, i as c_uint), + VaArg::c_long(i) => fmt_int(fmt, i as c_ulong), + VaArg::c_longlong(i) => fmt_int(fmt, i as c_ulonglong), + VaArg::c_short(i) => fmt_int(fmt, i as c_ushort), + VaArg::intmax_t(i) => fmt_int(fmt, i as uintmax_t), + VaArg::pointer(i) => fmt_int(fmt, i as usize), + VaArg::ptrdiff_t(i) => fmt_int(fmt, i as size_t), + VaArg::ssize_t(i) => fmt_int(fmt, i as size_t) }; let zero = precision == Some(0) && string == "0"; @@ -628,20 +636,29 @@ unsafe fn inner_printf(w: W, format: *const c_char, ap: va_list) -> io pad(w, left, b' ', final_len..pad_space)?; }, FmtKind::Scientific => { - let mut float = ap.get(ArgKind::Double, index).double; + let mut float = match varargs.get(index, &mut ap, Some(&arg)) { + VaArg::c_double(i) => i, + _ => panic!("this should not be possible") + }; let precision = precision.unwrap_or(6); fmt_float_exp(w, fmt, None, false, precision, float, left, pad_space, pad_zero)?; }, FmtKind::Decimal => { - let mut float = ap.get(ArgKind::Double, index).double; + let mut float = match varargs.get(index, &mut ap, Some(&arg)) { + VaArg::c_double(i) => i, + _ => panic!("this should not be possible") + }; let precision = precision.unwrap_or(6); fmt_float_normal(w, false, precision, float, left, pad_space, pad_zero)?; }, FmtKind::AnyNotation => { + let mut float = match varargs.get(index, &mut ap, Some(&arg)) { + VaArg::c_double(i) => i, + _ => panic!("this should not be possible") + }; let exp_fmt = b'E' | (fmt & 32); - let mut float = ap.get(ArgKind::Double, index).double; let precision = precision.unwrap_or(6); if !fmt_float_exp( @@ -661,7 +678,10 @@ unsafe fn inner_printf(w: W, format: *const c_char, ap: va_list) -> io FmtKind::String => { // if intkind == IntKind::Long || intkind == IntKind::LongLong, handle *const wchar_t - let ptr = ap.get(ArgKind::CharPtr, index).char_ptr; + let mut ptr = match varargs.get(index, &mut ap, Some(&arg)) { + VaArg::pointer(p) => p, + _ => panic!("this should not be possible") + } as *const c_char; if ptr.is_null() { w.write_all(b"(null)")?; @@ -680,14 +700,20 @@ unsafe fn inner_printf(w: W, format: *const c_char, ap: va_list) -> io FmtKind::Char => { // if intkind == IntKind::Long || intkind == IntKind::LongLong, handle wint_t - let c = ap.get(ArgKind::Byte, index).byte; + let c = match varargs.get(index, &mut ap, Some(&arg)) { + VaArg::c_char(c) => c, + _ => panic!("this should not be possible") + }; pad(w, !left, b' ', 1..pad_space)?; w.write_all(&[c as u8])?; pad(w, left, b' ', 1..pad_space)?; }, FmtKind::Pointer => { - let ptr = ap.get(ArgKind::VoidPtr, index).int_ptr; + let mut ptr = match varargs.get(index, &mut ap, Some(&arg)) { + VaArg::pointer(p) => p, + _ => panic!("this should not be possible") + }; let mut len = 1; if ptr.is_null() { @@ -709,14 +735,27 @@ unsafe fn inner_printf(w: W, format: *const c_char, ap: va_list) -> io pad(w, left, b' ', len..pad_space)?; }, FmtKind::GetWritten => { - let ptr = ap.get(ArgKind::IntPtr, index).int_ptr; - *ptr = w.written as c_int; + let mut ptr = match varargs.get(index, &mut ap, Some(&arg)) { + VaArg::pointer(p) => p, + _ => panic!("this should not be possible") + }; + + match intkind { + IntKind::Byte => *(ptr as *mut c_char) = w.written as c_char, + IntKind::Short => *(ptr as *mut c_short) = w.written as c_short, + IntKind::Int => *(ptr as *mut c_int) = w.written as c_int, + IntKind::Long => *(ptr as *mut c_long) = w.written as c_long, + IntKind::LongLong => *(ptr as *mut c_longlong) = w.written as c_longlong, + IntKind::IntMax => *(ptr as *mut intmax_t) = w.written as intmax_t, + IntKind::PtrDiff => *(ptr as *mut ptrdiff_t) = w.written as ptrdiff_t, + IntKind::Size => *(ptr as *mut size_t) = w.written as size_t + } } } } Ok(w.written as c_int) } -pub unsafe fn printf(w: W, format: *const c_char, ap: va_list) -> c_int { +pub unsafe fn printf(w: W, format: *const c_char, ap: VaList) -> c_int { inner_printf(w, format, ap).unwrap_or(-1) } diff --git a/tests/stdio/printf.c b/tests/stdio/printf.c index f36cc970c7..ea7b8dde02 100644 --- a/tests/stdio/printf.c +++ b/tests/stdio/printf.c @@ -1,7 +1,5 @@ #include -#include "test_helpers.h" - int main(void) { int sofar = 0; int len = printf( From 8fca7bcbc1c9e8ad8ad10929324ca725074c0406 Mon Sep 17 00:00:00 2001 From: jD91mZM2 Date: Tue, 23 Apr 2019 08:15:58 +0200 Subject: [PATCH 3/3] More printf tests and corner cases --- src/header/stdio/printf.rs | 71 ++++++++++++++++++++++++++++-- tests/expected/stdio/printf.stdout | 3 ++ tests/stdio/printf.c | 4 ++ 3 files changed, 75 insertions(+), 3 deletions(-) diff --git a/src/header/stdio/printf.rs b/src/header/stdio/printf.rs index b825868b85..658695ec17 100644 --- a/src/header/stdio/printf.rs +++ b/src/header/stdio/printf.rs @@ -126,6 +126,67 @@ impl VaArg { => VaArg::pointer(ap.arg::<*const c_void>()), } } + unsafe fn transmute(&self, arg: &PrintfArg) -> VaArg { + // At this point, there are conflicting printf arguments. An + // example of this is: + // ```c + // printf("%1$d %1$lf\n", 5, 0.1); + // ``` + // We handle it just like glibc: We read it from the VaList + // using the *last* argument type, but we transmute it when we + // try to access the other ones. + union Untyped { + c_char: c_char, + c_double: c_double, + c_int: c_int, + c_long: c_long, + c_longlong: c_longlong, + c_short: c_short, + intmax_t: intmax_t, + pointer: *const c_void, + ptrdiff_t: ptrdiff_t, + ssize_t: ssize_t + } + let untyped = match *self { + VaArg::c_char(i) => Untyped { c_char: i }, + VaArg::c_double(i) => Untyped { c_double: i }, + VaArg::c_int(i) => Untyped { c_int: i }, + VaArg::c_long(i) => Untyped { c_long: i }, + VaArg::c_longlong(i) => Untyped { c_longlong: i }, + VaArg::c_short(i) => Untyped { c_short: i }, + VaArg::intmax_t(i) => Untyped { intmax_t: i }, + VaArg::pointer(i) => Untyped { pointer: i }, + VaArg::ptrdiff_t(i) => Untyped { ptrdiff_t: i }, + VaArg::ssize_t(i) => Untyped { ssize_t: i } + }; + match (arg.fmtkind, arg.intkind) { + (FmtKind::Percent, _) => panic!("Can't call transmute on %"), + + (FmtKind::Char, _) | + (FmtKind::Unsigned, IntKind::Byte) | + (FmtKind::Signed, IntKind::Byte) => VaArg::c_char(untyped.c_char), + (FmtKind::Unsigned, IntKind::Short) | + (FmtKind::Signed, IntKind::Short) => VaArg::c_short(untyped.c_short), + (FmtKind::Unsigned, IntKind::Int) | + (FmtKind::Signed, IntKind::Int) => VaArg::c_int(untyped.c_int), + (FmtKind::Unsigned, IntKind::Long) | + (FmtKind::Signed, IntKind::Long) => VaArg::c_long(untyped.c_long), + (FmtKind::Unsigned, IntKind::LongLong) | + (FmtKind::Signed, IntKind::LongLong) => VaArg::c_longlong(untyped.c_longlong), + (FmtKind::Unsigned, IntKind::IntMax) | + (FmtKind::Signed, IntKind::IntMax) => VaArg::intmax_t(untyped.intmax_t), + (FmtKind::Unsigned, IntKind::PtrDiff) | + (FmtKind::Signed, IntKind::PtrDiff) => VaArg::ptrdiff_t(untyped.ptrdiff_t), + (FmtKind::Unsigned, IntKind::Size) | + (FmtKind::Signed, IntKind::Size) => VaArg::ssize_t(untyped.ssize_t), + + (FmtKind::AnyNotation, _) | (FmtKind::Decimal, _) | (FmtKind::Scientific, _) + => VaArg::c_double(untyped.c_double), + + (FmtKind::GetWritten, _) | (FmtKind::Pointer, _) | (FmtKind::String, _) + => VaArg::pointer(untyped.pointer), + } + } } #[derive(Default)] struct VaListCache { @@ -133,8 +194,12 @@ struct VaListCache { i: usize } impl VaListCache { - unsafe fn get(&mut self, i: usize, ap: &mut VaList, arg: Option<&PrintfArg>) -> VaArg { + unsafe fn get(&mut self, i: usize, ap: &mut VaList, default: Option<&PrintfArg>) -> VaArg { if let Some(&arg) = self.args.get(i) { + let mut arg = arg; + if let Some(default) = default { + arg = arg.transmute(default); + } return arg; } while self.args.len() < i { @@ -144,8 +209,8 @@ impl VaListCache { // defaulting to c_int. self.args.push(VaArg::c_int(ap.arg::())) } - self.args.push(match arg { - Some(arg) => VaArg::arg_from(arg, ap), + self.args.push(match default { + Some(default) => VaArg::arg_from(default, ap), None => VaArg::c_int(ap.arg::()) }); self.args[i] diff --git a/tests/expected/stdio/printf.stdout b/tests/expected/stdio/printf.stdout index 19b75259c0..32cc6950e3 100644 --- a/tests/expected/stdio/printf.stdout +++ b/tests/expected/stdio/printf.stdout @@ -26,6 +26,9 @@ Positional madness: 4 3 2 00002 |Fiz |Buz | Fiz| Tot| +int: 5 double: 0.100000 0.200000 0.300000 0.400000 +-1717986918 0.100000 +-1717986918 0.200000 Float madness: 1.234568e+02 diff --git a/tests/stdio/printf.c b/tests/stdio/printf.c index ea7b8dde02..414619a5a9 100644 --- a/tests/stdio/printf.c +++ b/tests/stdio/printf.c @@ -33,6 +33,10 @@ int main(void) { printf("%3$d %2$d %1$d\n", 2, 3, 4); printf("%.*3$d\n", 2, 0, 5); printf("|%-*6$.*5$s|%-*6$.*5$s|%*6$.*5$s|%*6$.*5$s|\n", "Fizz", "Buzz", "FizzBuzz", "TotalBuzz", 3, 8); + printf("int: %*6$d double: %lf %lf %lf %lf\n", 5, 0.1, 0.2, 0.3, 0.4, 10); + printf("%1$d %1$lf\n", 5, 0.1); + printf("%1$d %lf\n", 5, 0.2); + //printf("int: %*6$d no info on middle types\n", 5, 0.1, 0.2, 0.3, 0.4, 10); puts("\nFloat madness:"); printf("%20e\n", 123.456789123);