Merge branch 'printf' into 'master'
Implement almost all of printf See merge request redox-os/relibc!171
This commit is contained in:
+523
-107
@@ -1,120 +1,536 @@
|
||||
use alloc::string::String;
|
||||
use alloc::string::ToString;
|
||||
use alloc::vec::Vec;
|
||||
use core::fmt::Write as CoreWrite;
|
||||
use core::{ptr, slice, str};
|
||||
use core::ops::Range;
|
||||
use core::{fmt, ptr, slice, str};
|
||||
|
||||
use c_str::CStr;
|
||||
use io::{self, Write};
|
||||
use platform::types::*;
|
||||
use platform::{self, WriteByte};
|
||||
use va_list::VaList;
|
||||
use va_list::{VaList, VaPrimitive};
|
||||
|
||||
pub unsafe fn printf<W: WriteByte>(w: W, format: *const c_char, mut ap: VaList) -> c_int {
|
||||
let mut w = platform::CountingWriter::new(w);
|
||||
#[derive(PartialEq, Eq)]
|
||||
enum IntKind {
|
||||
Byte,
|
||||
Short,
|
||||
Int,
|
||||
Long,
|
||||
LongLong,
|
||||
IntMax,
|
||||
PtrDiff,
|
||||
Size,
|
||||
}
|
||||
|
||||
let format = slice::from_raw_parts(format as *const u8, usize::max_value());
|
||||
|
||||
let mut found_percent = false;
|
||||
for &b in format.iter() {
|
||||
// check for NUL
|
||||
if b == 0 {
|
||||
break;
|
||||
}
|
||||
|
||||
if found_percent {
|
||||
if match b as char {
|
||||
'%' => {
|
||||
found_percent = false;
|
||||
w.write_char('%')
|
||||
}
|
||||
'c' => {
|
||||
let a = ap.get::<u32>();
|
||||
|
||||
found_percent = false;
|
||||
|
||||
w.write_u8(a as u8)
|
||||
}
|
||||
'd' | 'i' => {
|
||||
let a = ap.get::<c_int>();
|
||||
|
||||
found_percent = false;
|
||||
|
||||
w.write_fmt(format_args!("{}", a))
|
||||
}
|
||||
'f' | 'F' => {
|
||||
let a = ap.get::<f64>();
|
||||
|
||||
found_percent = false;
|
||||
|
||||
w.write_fmt(format_args!("{}", a))
|
||||
}
|
||||
'n' => {
|
||||
let _a = ap.get::<c_int>();
|
||||
|
||||
found_percent = false;
|
||||
Ok(())
|
||||
}
|
||||
'p' => {
|
||||
let a = ap.get::<usize>();
|
||||
|
||||
found_percent = false;
|
||||
|
||||
w.write_fmt(format_args!("0x{:x}", a))
|
||||
}
|
||||
's' => {
|
||||
let a = ap.get::<*const c_char>();
|
||||
|
||||
found_percent = false;
|
||||
if a != ptr::null() {
|
||||
let a_cstr = CStr::from_ptr(a);
|
||||
w.write_str(str::from_utf8_unchecked(a_cstr.to_bytes()))
|
||||
} else {
|
||||
w.write_str("NULL")
|
||||
}
|
||||
}
|
||||
'u' => {
|
||||
let a = ap.get::<c_uint>();
|
||||
|
||||
found_percent = false;
|
||||
|
||||
w.write_fmt(format_args!("{}", a))
|
||||
}
|
||||
'x' => {
|
||||
let a = ap.get::<c_uint>();
|
||||
|
||||
found_percent = false;
|
||||
|
||||
w.write_fmt(format_args!("{:x}", a))
|
||||
}
|
||||
'X' => {
|
||||
let a = ap.get::<c_uint>();
|
||||
|
||||
found_percent = false;
|
||||
|
||||
w.write_fmt(format_args!("{:X}", a))
|
||||
}
|
||||
'o' => {
|
||||
let a = ap.get::<c_uint>();
|
||||
|
||||
found_percent = false;
|
||||
|
||||
w.write_fmt(format_args!("{:o}", a))
|
||||
}
|
||||
'-' => Ok(()),
|
||||
'+' => Ok(()),
|
||||
' ' => Ok(()),
|
||||
'#' => Ok(()),
|
||||
'0'...'9' => Ok(()),
|
||||
_ => Ok(()),
|
||||
}.is_err()
|
||||
{
|
||||
return -1;
|
||||
trait IntoUsize {
|
||||
fn into_usize(self) -> usize;
|
||||
fn from_usize(i: usize) -> Self;
|
||||
}
|
||||
macro_rules! impl_intousize {
|
||||
($($kind:tt;)*) => {
|
||||
$(impl IntoUsize for $kind {
|
||||
fn into_usize(self) -> usize {
|
||||
self as usize
|
||||
}
|
||||
} else if b == b'%' {
|
||||
found_percent = true;
|
||||
} else {
|
||||
if w.write_u8(b).is_err() {
|
||||
return -1;
|
||||
fn from_usize(i: usize) -> Self {
|
||||
i as Self
|
||||
}
|
||||
})*
|
||||
}
|
||||
}
|
||||
impl_intousize! {
|
||||
i32;
|
||||
u32;
|
||||
i64;
|
||||
u64;
|
||||
isize;
|
||||
usize;
|
||||
}
|
||||
impl<T> IntoUsize for *const T {
|
||||
fn into_usize(self) -> usize {
|
||||
self as usize
|
||||
}
|
||||
fn from_usize(i: usize) -> Self {
|
||||
i as Self
|
||||
}
|
||||
}
|
||||
impl<T> IntoUsize for *mut T {
|
||||
fn into_usize(self) -> usize {
|
||||
self as usize
|
||||
}
|
||||
fn from_usize(i: usize) -> Self {
|
||||
i as Self
|
||||
}
|
||||
}
|
||||
impl IntoUsize for f32 {
|
||||
fn into_usize(self) -> usize {
|
||||
self.to_bits() as usize
|
||||
}
|
||||
fn from_usize(i: usize) -> Self {
|
||||
Self::from_bits(i as u32)
|
||||
}
|
||||
}
|
||||
impl IntoUsize for f64 {
|
||||
fn into_usize(self) -> usize {
|
||||
self.to_bits() as usize
|
||||
}
|
||||
fn from_usize(i: usize) -> Self {
|
||||
Self::from_bits(i as u64)
|
||||
}
|
||||
}
|
||||
|
||||
struct BufferedVaList {
|
||||
list: VaList,
|
||||
buf: Vec<usize>,
|
||||
i: usize
|
||||
}
|
||||
impl BufferedVaList {
|
||||
fn new(list: VaList) -> Self {
|
||||
Self {
|
||||
list,
|
||||
buf: Vec::new(),
|
||||
i: 0
|
||||
}
|
||||
}
|
||||
unsafe fn get<T: VaPrimitive + IntoUsize>(&mut self, i: Option<usize>) -> T {
|
||||
match i {
|
||||
None => self.next(),
|
||||
Some(i) => self.index(i),
|
||||
}
|
||||
}
|
||||
unsafe fn next<T: VaPrimitive + IntoUsize>(&mut self) -> T {
|
||||
if self.i >= self.buf.len() {
|
||||
self.buf.push(self.list.get::<T>().into_usize());
|
||||
}
|
||||
let arg = T::from_usize(self.buf[self.i]);
|
||||
self.i += 1;
|
||||
arg
|
||||
}
|
||||
unsafe fn index<T: VaPrimitive + IntoUsize>(&mut self, i: usize) -> T {
|
||||
while self.buf.len() < i {
|
||||
// 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.
|
||||
|
||||
w.written as c_int
|
||||
self.buf.push(self.list.get::<usize>());
|
||||
}
|
||||
T::from_usize(self.buf[i - 1])
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn pop_int_raw(format: &mut *const u8) -> Option<usize> {
|
||||
let mut int = None;
|
||||
while let Some(digit) = (**format as char).to_digit(10) {
|
||||
*format = format.offset(1);
|
||||
if int.is_none() {
|
||||
int = Some(0);
|
||||
}
|
||||
*int.as_mut().unwrap() *= 10;
|
||||
*int.as_mut().unwrap() += digit as usize;
|
||||
}
|
||||
int
|
||||
}
|
||||
unsafe fn pop_int(format: &mut *const u8, ap: &mut BufferedVaList) -> Option<usize> {
|
||||
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::<usize>(i))
|
||||
}
|
||||
}
|
||||
|
||||
Some(ap.next::<usize>())
|
||||
} else {
|
||||
pop_int_raw(format)
|
||||
}
|
||||
}
|
||||
unsafe fn fmt_int<I>(fmt: u8, i: I) -> String
|
||||
where I: fmt::Display + fmt::Octal + fmt::LowerHex + fmt::UpperHex
|
||||
{
|
||||
match fmt {
|
||||
b'o' => format!("{:o}", i),
|
||||
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)
|
||||
}
|
||||
}
|
||||
fn pad<W: Write>(w: &mut W, current_side: bool, pad_char: u8, range: Range<usize>) -> io::Result<()> {
|
||||
if current_side {
|
||||
for _ in range {
|
||||
w.write_all(&[pad_char])?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
fn float_string(float: c_double, precision: usize, trim: bool) -> String {
|
||||
let mut string = format!("{:.p$}", float, p = precision);
|
||||
if trim {
|
||||
let truncate = {
|
||||
let mut slice = string.trim_right_matches('0');
|
||||
if slice.ends_with('.') {
|
||||
slice.len() - 1
|
||||
} else {
|
||||
slice.len()
|
||||
}
|
||||
};
|
||||
string.truncate(truncate);
|
||||
}
|
||||
string
|
||||
}
|
||||
fn fmt_float_exp<W: Write>(
|
||||
w: &mut W,
|
||||
exp_fmt: u8,
|
||||
range: Option<(isize, isize)>,
|
||||
trim: bool,
|
||||
precision: usize,
|
||||
mut float: c_double,
|
||||
left: bool,
|
||||
pad_space: usize,
|
||||
pad_zero: usize,
|
||||
) -> io::Result<bool> {
|
||||
let mut exp: isize = 0;
|
||||
while float >= 10.0 || float <= -10.0 {
|
||||
float /= 10.0;
|
||||
exp += 1;
|
||||
}
|
||||
while (float > 0.0 && float < 1.0) || (float > -1.0 && float < 0.0) {
|
||||
float *= 10.0;
|
||||
exp -= 1;
|
||||
}
|
||||
|
||||
if range.map(|(start, end)| exp >= start && exp < end).unwrap_or(false) {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
let mut exp2 = exp;
|
||||
let mut exp_len = 1;
|
||||
while exp2 >= 10 {
|
||||
exp2 /= 10;
|
||||
exp_len += 1;
|
||||
}
|
||||
|
||||
let string = float_string(float, precision, trim);
|
||||
let mut len = string.len() + 2 + 2.max(exp_len);
|
||||
|
||||
pad(w, !left, b' ', len..pad_space)?;
|
||||
let bytes = if string.starts_with('-') {
|
||||
w.write_all(&[b'-'])?;
|
||||
&string.as_bytes()[1..]
|
||||
} else {
|
||||
string.as_bytes()
|
||||
};
|
||||
pad(w, !left, b'0', len..pad_zero)?;
|
||||
w.write_all(bytes)?;
|
||||
write!(w, "{}{:+03}", exp_fmt as char, exp)?;
|
||||
pad(w, left, b' ', len..pad_space)?;
|
||||
|
||||
Ok(true)
|
||||
}
|
||||
fn fmt_float_normal<W: Write>(
|
||||
w: &mut W,
|
||||
trim: bool,
|
||||
precision: usize,
|
||||
float: c_double,
|
||||
left: bool,
|
||||
pad_space: usize,
|
||||
pad_zero: usize,
|
||||
) -> io::Result<usize> {
|
||||
let string = float_string(float, precision, trim);
|
||||
|
||||
pad(w, !left, b' ', string.len()..pad_space)?;
|
||||
let bytes = if string.starts_with('-') {
|
||||
w.write_all(&[b'-'])?;
|
||||
&string.as_bytes()[1..]
|
||||
} else {
|
||||
string.as_bytes()
|
||||
};
|
||||
pad(w, true, b'0', string.len()..pad_zero)?;
|
||||
w.write_all(bytes)?;
|
||||
pad(w, left, b' ', string.len()..pad_space)?;
|
||||
|
||||
Ok(string.len())
|
||||
}
|
||||
unsafe fn inner_printf<W: Write>(w: W, format: *const c_char, mut ap: VaList) -> io::Result<c_int> {
|
||||
let mut 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);
|
||||
}
|
||||
}
|
||||
|
||||
// Flags:
|
||||
let mut alternate = false;
|
||||
let mut zero = false;
|
||||
let mut left = false;
|
||||
let mut sign_reserve = false;
|
||||
let mut sign_always = false;
|
||||
|
||||
loop {
|
||||
match *format {
|
||||
b'#' => alternate = true,
|
||||
b'0' => zero = true,
|
||||
b'-' => left = true,
|
||||
b' ' => sign_reserve = true,
|
||||
b'+' => sign_always = true,
|
||||
_ => break
|
||||
}
|
||||
format = format.offset(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) {
|
||||
int@Some(_) => int,
|
||||
None => return Ok(-1)
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let pad_space = if zero { 0 } else { min_width };
|
||||
let pad_zero = if zero { min_width } else { 0 };
|
||||
|
||||
// Integer size:
|
||||
let mut kind = IntKind::Int;
|
||||
loop {
|
||||
kind = match *format {
|
||||
b'h' => if kind == IntKind::Short || kind == IntKind::Byte {
|
||||
IntKind::Byte
|
||||
} else {
|
||||
IntKind::Short
|
||||
},
|
||||
b'j' => IntKind::IntMax,
|
||||
b'l' => if kind == IntKind::Long || kind == IntKind::LongLong {
|
||||
IntKind::LongLong
|
||||
} else {
|
||||
IntKind::Long
|
||||
},
|
||||
b'q' | b'L' => IntKind::LongLong,
|
||||
b't' => IntKind::PtrDiff,
|
||||
b'z' => IntKind::Size,
|
||||
_ => break,
|
||||
};
|
||||
|
||||
format = format.offset(1);
|
||||
}
|
||||
|
||||
// Finally, type:
|
||||
match *format {
|
||||
b'%' => w.write_all(&[b'%'])?,
|
||||
b'd' | b'i' => {
|
||||
let string = match kind {
|
||||
// VaList does not seem to support these two:
|
||||
// IntKind::Byte => ap.get::<c_char>(index).to_string(),
|
||||
// IntKind::Short => ap.get::<c_short>(index).to_string(),
|
||||
IntKind::Byte => (ap.get::<c_int>(index) as c_char).to_string(),
|
||||
IntKind::Short => (ap.get::<c_int>(index) as c_short).to_string(),
|
||||
IntKind::Int => ap.get::<c_int>(index).to_string(),
|
||||
IntKind::Long => ap.get::<c_long>(index).to_string(),
|
||||
IntKind::LongLong => ap.get::<c_longlong>(index).to_string(),
|
||||
IntKind::PtrDiff => ap.get::<ptrdiff_t>(index).to_string(),
|
||||
IntKind::Size => ap.get::<ssize_t>(index).to_string(),
|
||||
IntKind::IntMax => ap.get::<intmax_t>(index).to_string(),
|
||||
};
|
||||
let positive = !string.starts_with('-');
|
||||
let zero = precision == Some(0) && string == "0";
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
pad(w, !left, b' ', final_len..pad_space)?;
|
||||
|
||||
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))?;
|
||||
|
||||
if !zero {
|
||||
w.write_all(bytes)?;
|
||||
}
|
||||
|
||||
pad(w, left, b' ', final_len..pad_space)?;
|
||||
},
|
||||
b'o' | b'u' | b'x' | b'X' => {
|
||||
let fmt = *format;
|
||||
let string = match kind {
|
||||
// VaList does not seem to support these two:
|
||||
// IntKind::Byte => fmt_int(kind ap.get::<c_char>(index)),
|
||||
// IntKind::Short => fmt_int(kind ap.get::<c_short>(index)),
|
||||
IntKind::Byte => fmt_int(fmt, ap.get::<c_uint>(index) as c_uchar),
|
||||
IntKind::Short => fmt_int(fmt, ap.get::<c_uint>(index) as c_ushort),
|
||||
IntKind::Int => fmt_int(fmt, ap.get::<c_uint>(index)),
|
||||
IntKind::Long => fmt_int(fmt, ap.get::<c_ulong>(index)),
|
||||
IntKind::LongLong => fmt_int(fmt, ap.get::<c_ulonglong>(index)),
|
||||
IntKind::PtrDiff => fmt_int(fmt, ap.get::<ptrdiff_t>(index)),
|
||||
IntKind::Size => fmt_int(fmt, ap.get::<size_t>(index)),
|
||||
IntKind::IntMax => fmt_int(fmt, ap.get::<uintmax_t>(index)),
|
||||
};
|
||||
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 => 1,
|
||||
b'x' |
|
||||
b'X' => 2,
|
||||
_ => 0
|
||||
}
|
||||
} else { 0 };
|
||||
|
||||
if zero {
|
||||
len = 0;
|
||||
final_len = 0;
|
||||
}
|
||||
|
||||
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'])?,
|
||||
_ => ()
|
||||
}
|
||||
}
|
||||
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::<c_double>(index);
|
||||
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::<c_double>(index);
|
||||
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::<c_double>(index);
|
||||
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::<*const c_char>(index);
|
||||
|
||||
if ptr.is_null() {
|
||||
w.write_all(b"(null)")?;
|
||||
} else {
|
||||
let mut len = 0;
|
||||
while *ptr.offset(len) != 0 {
|
||||
len += 1;
|
||||
}
|
||||
|
||||
let len = (len as usize).min(precision.unwrap_or(::core::usize::MAX));
|
||||
|
||||
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)?;
|
||||
}
|
||||
},
|
||||
b'c' => {
|
||||
// if kind == IntKind::Long || kind == IntKind::LongLong, handle wint_t
|
||||
|
||||
let c = ap.get::<c_int>(index) as c_char;
|
||||
|
||||
pad(w, !left, b' ', 1..pad_space)?;
|
||||
w.write_all(&[c as u8])?;
|
||||
pad(w, left, b' ', 1..pad_space)?;
|
||||
},
|
||||
b'p' => {
|
||||
let ptr = ap.get::<*const c_void>(index);
|
||||
|
||||
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' ', 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)?;
|
||||
},
|
||||
b'n' => {
|
||||
let ptr = ap.get::<*mut c_int>(index);
|
||||
*ptr = w.written as c_int;
|
||||
},
|
||||
_ => return Ok(-1)
|
||||
}
|
||||
}
|
||||
format = format.offset(1);
|
||||
}
|
||||
Ok(w.written as c_int)
|
||||
}
|
||||
pub unsafe fn printf<W: Write>(w: W, format: *const c_char, ap: VaList) -> c_int {
|
||||
inner_printf(w, format, ap).unwrap_or(-1)
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ unsafe fn inner_scanf<R: Read>(
|
||||
|
||||
macro_rules! read {
|
||||
() => {{
|
||||
let mut buf = &mut [byte];
|
||||
let buf = &mut [byte];
|
||||
match r.read(buf) {
|
||||
Ok(0) => false,
|
||||
Ok(_) => {
|
||||
@@ -117,13 +117,17 @@ unsafe fn inner_scanf<R: Read>(
|
||||
let mut kind = IntKind::Int;
|
||||
loop {
|
||||
kind = match c {
|
||||
b'h' => if kind == IntKind::Short {
|
||||
b'h' => if kind == IntKind::Short || kind == IntKind::Byte {
|
||||
IntKind::Byte
|
||||
} else {
|
||||
IntKind::Short
|
||||
},
|
||||
b'j' => IntKind::IntMax,
|
||||
b'l' => IntKind::Long,
|
||||
b'l' => if kind == IntKind::Long || kind == IntKind::LongLong {
|
||||
IntKind::LongLong
|
||||
} else {
|
||||
IntKind::Long
|
||||
},
|
||||
b'q' | b'L' => IntKind::LongLong,
|
||||
b't' => IntKind::PtrDiff,
|
||||
b'z' => IntKind::Size,
|
||||
|
||||
+78
-37
@@ -1,6 +1,6 @@
|
||||
use alloc::vec::Vec;
|
||||
use core::{fmt, ptr};
|
||||
use io::{self, Read};
|
||||
use io::{self, Read, Write};
|
||||
|
||||
pub use self::allocator::*;
|
||||
|
||||
@@ -95,68 +95,88 @@ impl Read for FileReader {
|
||||
}
|
||||
|
||||
pub struct StringWriter(pub *mut u8, pub usize);
|
||||
|
||||
impl StringWriter {
|
||||
pub unsafe fn write(&mut self, buf: &[u8]) {
|
||||
impl Write for StringWriter {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
if self.1 > 1 {
|
||||
let copy_size = buf.len().min(self.1 - 1);
|
||||
ptr::copy_nonoverlapping(
|
||||
buf.as_ptr(),
|
||||
self.0,
|
||||
copy_size
|
||||
);
|
||||
self.1 -= copy_size;
|
||||
unsafe {
|
||||
ptr::copy_nonoverlapping(
|
||||
buf.as_ptr(),
|
||||
self.0,
|
||||
copy_size
|
||||
);
|
||||
self.1 -= copy_size;
|
||||
|
||||
self.0 = self.0.offset(copy_size as isize);
|
||||
*self.0 = 0;
|
||||
self.0 = self.0.offset(copy_size as isize);
|
||||
*self.0 = 0;
|
||||
}
|
||||
|
||||
Ok(copy_size)
|
||||
} else {
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Write for StringWriter {
|
||||
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||
unsafe { self.write(s.as_bytes()) };
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
impl fmt::Write for StringWriter {
|
||||
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||
unsafe {
|
||||
// can't fail
|
||||
self.write(s.as_bytes()).unwrap();
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl WriteByte for StringWriter {
|
||||
fn write_u8(&mut self, byte: u8) -> fmt::Result {
|
||||
unsafe { self.write(&[byte]) };
|
||||
unsafe {
|
||||
// can't fail
|
||||
self.write(&[byte]).unwrap();
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct UnsafeStringWriter(pub *mut u8);
|
||||
|
||||
impl UnsafeStringWriter {
|
||||
pub unsafe fn write(&mut self, buf: &[u8]) {
|
||||
ptr::copy_nonoverlapping(
|
||||
buf.as_ptr(),
|
||||
self.0,
|
||||
buf.len()
|
||||
);
|
||||
*self.0.offset(buf.len() as isize) = b'\0';
|
||||
self.0 = self.0.offset(buf.len() as isize);
|
||||
impl Write for UnsafeStringWriter {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
unsafe {
|
||||
ptr::copy_nonoverlapping(
|
||||
buf.as_ptr(),
|
||||
self.0,
|
||||
buf.len()
|
||||
);
|
||||
*self.0.offset(buf.len() as isize) = b'\0';
|
||||
self.0 = self.0.offset(buf.len() as isize);
|
||||
}
|
||||
Ok(buf.len())
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Write for UnsafeStringWriter {
|
||||
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||
unsafe { self.write(s.as_bytes()) };
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
impl fmt::Write for UnsafeStringWriter {
|
||||
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||
unsafe {
|
||||
// can't fail
|
||||
self.write(s.as_bytes()).unwrap();
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl WriteByte for UnsafeStringWriter {
|
||||
fn write_u8(&mut self, byte: u8) -> fmt::Result {
|
||||
unsafe { self.write(&[byte]) };
|
||||
unsafe {
|
||||
// can't fail
|
||||
self.write(&[byte]).unwrap();
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct UnsafeStringReader(pub *const u8);
|
||||
|
||||
impl Read for UnsafeStringReader {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
unsafe {
|
||||
@@ -197,3 +217,24 @@ impl<T: WriteByte> WriteByte for CountingWriter<T> {
|
||||
self.inner.write_u8(byte)
|
||||
}
|
||||
}
|
||||
impl<T: Write> Write for CountingWriter<T> {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
let res = self.inner.write(buf);
|
||||
if let Ok(written) = res {
|
||||
self.written += written;
|
||||
}
|
||||
res
|
||||
}
|
||||
fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
|
||||
match self.inner.write_all(&buf) {
|
||||
Ok(()) => (),
|
||||
Err(ref err) if err.kind() == io::ErrorKind::WriteZero => (),
|
||||
Err(err) => return Err(err)
|
||||
}
|
||||
self.written += buf.len();
|
||||
Ok(())
|
||||
}
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
self.inner.flush()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
cos(3.14) = -0.9999987483024597
|
||||
cos(3.140000) = -0.999999
|
||||
|
||||
@@ -7,4 +7,36 @@ uint: 32
|
||||
hex: beef
|
||||
HEX: C0FFEE
|
||||
string: end
|
||||
len of previous write: 94
|
||||
%n returned 51, total len of write: 94
|
||||
|
||||
Padding madness:
|
||||
001 +2
|
||||
c
|
||||
0x00000ff
|
||||
001
|
||||
0 0x1
|
||||
1
|
||||
(00123) ( 123)
|
||||
(-0123) ( -123)
|
||||
( )
|
||||
0xabcdef
|
||||
(nil)
|
||||
|
||||
Positional madness:
|
||||
4 3 2
|
||||
00002
|
||||
|Fiz |Buz | Fiz| Tot|
|
||||
|
||||
Float madness:
|
||||
1.234568e+02
|
||||
1.000000E-05
|
||||
123.456789
|
||||
0.000010
|
||||
-1.234568e+02
|
||||
-00000001.234568e+02
|
||||
100000
|
||||
1e+06
|
||||
1.000000e+06
|
||||
0.0001
|
||||
1E-05
|
||||
1.000000E-05
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
2, { sa: 12, ia: 345, ib: 0, ic: 0, fa: 0, da: 0, ptr: 0x0, char: a, string: }
|
||||
3, { sa: 12, ia: 18, ib: 837, ic: 8, fa: 0, da: 0, ptr: 0x0, char: a, string: }
|
||||
2, { sa: 12, ia: 18, ib: 837, ic: 8, fa: 0.10000000149011612, da: 0.2, ptr: 0x0, char: a, string: }
|
||||
1, { sa: 12, ia: 18, ib: 837, ic: 8, fa: 0.10000000149011612, da: 0.2, ptr: 0xabcdef, char: a, string: }
|
||||
1, { sa: 12, ia: 18, ib: 837, ic: 8, fa: 0.10000000149011612, da: 0.2, ptr: 0xabcdef, char: a, string: Hello }
|
||||
1, { sa: 12, ia: 15, ib: 837, ic: 8, fa: 0.10000000149011612, da: 0.2, ptr: 0xabcdef, char: a, string: Hello }
|
||||
2, { sa: 12, ia: 15, ib: 837, ic: 8, fa: 0.10000000149011612, da: 0.2, ptr: 0xabcdef, char: h, string: elllo }
|
||||
1, { sa: 12, ia: 0, ib: 8, ic: 8, fa: 0.10000000149011612, da: 0.2, ptr: 0xabcdef, char: h, string: elllo }
|
||||
0, { sa: 12, ia: 0, ib: 8, ic: 8, fa: 0.10000000149011612, da: 0.2, ptr: 0xabcdef, char: h, string: elllo }
|
||||
2, { sa: 12, ia: 345, ib: 0, ic: 0, fa: 0.000000, da: 0.000000, ptr: (nil), char: a, string: }
|
||||
3, { sa: 12, ia: 18, ib: 837, ic: 8, fa: 0.000000, da: 0.000000, ptr: (nil), char: a, string: }
|
||||
2, { sa: 12, ia: 18, ib: 837, ic: 8, fa: 0.100000, da: 0.200000, ptr: (nil), char: a, string: }
|
||||
1, { sa: 12, ia: 18, ib: 837, ic: 8, fa: 0.100000, da: 0.200000, ptr: 0xabcdef, char: a, string: }
|
||||
1, { sa: 12, ia: 18, ib: 837, ic: 8, fa: 0.100000, da: 0.200000, ptr: 0xabcdef, char: a, string: Hello }
|
||||
1, { sa: 12, ia: 15, ib: 837, ic: 8, fa: 0.100000, da: 0.200000, ptr: 0xabcdef, char: a, string: Hello }
|
||||
2, { sa: 12, ia: 15, ib: 837, ic: 8, fa: 0.100000, da: 0.200000, ptr: 0xabcdef, char: h, string: elllo }
|
||||
1, { sa: 12, ia: 0, ib: 8, ic: 8, fa: 0.100000, da: 0.200000, ptr: 0xabcdef, char: h, string: elllo }
|
||||
0, { sa: 12, ia: 0, ib: 8, ic: 8, fa: 0.100000, da: 0.200000, ptr: 0xabcdef, char: h, string: elllo }
|
||||
|
||||
@@ -1 +1 @@
|
||||
-3.14
|
||||
-3.140000
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
d: 0 Endptr: "a 1 hello"
|
||||
d: 1 Endptr: " hello"
|
||||
d: 1 Endptr: " hello 2"
|
||||
d: 10.123 Endptr: ""
|
||||
d: 10.123 Endptr: ""
|
||||
d: -5.3 Endptr: ""
|
||||
d: 16.071044921875 Endptr: ""
|
||||
d: 1.13671875 Endptr: ""
|
||||
d: 3.12890625 Endptr: ""
|
||||
d: 0.000000 Endptr: "a 1 hello"
|
||||
d: 1.000000 Endptr: " hello"
|
||||
d: 1.000000 Endptr: " hello 2"
|
||||
d: 10.123000 Endptr: ""
|
||||
d: 10.123000 Endptr: ""
|
||||
d: -5.300000 Endptr: ""
|
||||
d: 16.071045 Endptr: ""
|
||||
d: 1.136719 Endptr: ""
|
||||
d: 3.128906 Endptr: ""
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
rust
|
||||
libc we trust
|
||||
NULL
|
||||
NULL
|
||||
(null)
|
||||
(null)
|
||||
RUST
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
--- Running: test --test0 -a
|
||||
getopt_long returned 1, argument test0=NULL
|
||||
Option -a with value NULL
|
||||
getopt_long returned 1, argument test0=(null)
|
||||
Option -a with value (null)
|
||||
--- Running: test --test1 -a
|
||||
getopt_long returned 0, set flag to 2, argument test1=NULL
|
||||
Option -a with value NULL
|
||||
getopt_long returned 0, set flag to 2, argument test1=(null)
|
||||
Option -a with value (null)
|
||||
--- Running: test --test2 -a
|
||||
getopt_long returned 3, argument test2=NULL
|
||||
Option -a with value NULL
|
||||
getopt_long returned 3, argument test2=(null)
|
||||
Option -a with value (null)
|
||||
--- Running: test --test2=arg -a
|
||||
getopt_long returned 3, argument test2=arg
|
||||
Option -a with value NULL
|
||||
Option -a with value (null)
|
||||
--- Running: test --test3 -a
|
||||
getopt_long returned 4, argument test3=-a
|
||||
--- Running: test --test3=arg -a
|
||||
getopt_long returned 4, argument test3=arg
|
||||
Option -a with value NULL
|
||||
Option -a with value (null)
|
||||
--- Running: test --test3 arg -a
|
||||
getopt_long returned 4, argument test3=arg
|
||||
Option -a with value NULL
|
||||
Option -a with value (null)
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
Processing 11 UTF-8 code units: [ 7a c3 9f e6 b0 b4 f0 9f 8d 8c 0 ]
|
||||
into 5 wchar_t units: [ 7a df 6c34 1f34c 0 ]
|
||||
Processing 11 UTF-8 code units: [ 0x7a 0xc3 0x9f 0xe6 0xb0 0xb4 0xf0 0x9f 0x8d 0x8c 0 ]
|
||||
into 5 wchar_t units: [ 0x7a 0xdf 0x6c34 0x1f34c 0 ]
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
Processing 5 wchar_t units: [ 7a df 6c34 1f34c 0 ]
|
||||
into 11 UTF-8 code units: [ 7a c3 9f e6 b0 b4 f0 9f 8d 8c 0 ]
|
||||
Processing 5 wchar_t units: [ 0x7a 0xdf 0x6c34 0x1f34c 0 ]
|
||||
into 11 UTF-8 code units: [ 0x7a 0xc3 0x9f 0xe6 0xb0 0xb4 0xf0 0x9f 0x8d 0x8c 0 ]
|
||||
|
||||
+37
-2
@@ -1,17 +1,52 @@
|
||||
#include <stdio.h>
|
||||
|
||||
int main(int argc, char ** argv) {
|
||||
int sofar = 0;
|
||||
int len = printf(
|
||||
"percent: %%\nstring: %s\nchar: %c\nchar: %c\nint: %d\nuint: %u\nhex: %x\nHEX: %X\nstring: %s\n",
|
||||
"percent: %%\nstring: %s\nchar: %c\nchar: %c\nint: %d\n%nuint: %u\nhex: %x\nHEX: %X\nstring: %s\n",
|
||||
"String",
|
||||
'c',
|
||||
254,
|
||||
-16,
|
||||
&sofar,
|
||||
32,
|
||||
0xbeef,
|
||||
0xC0FFEE,
|
||||
"end"
|
||||
);
|
||||
printf("len of previous write: %d\n", len);
|
||||
printf("%%n returned %d, total len of write: %d\n", sofar, len);
|
||||
|
||||
puts("\nPadding madness:");
|
||||
printf("% -5.3d %+3d\n", 1, 2);
|
||||
printf("%4c\n", 'c');
|
||||
printf("%#10.7x\n", 0xFF);
|
||||
printf("%#4.3o\n", 01);
|
||||
printf("%#x %#x\n", 0, 1);
|
||||
printf("%.0d %.0d\n", 0, 1);
|
||||
printf("(%05d) (%5d)\n", 123, 123);
|
||||
printf("(%05d) (%5d)\n", -123, -123);
|
||||
printf("(%13.0d)\n", 0);
|
||||
printf("%p\n", (void*) 0xABCDEF);
|
||||
printf("%p\n", (void*) 0);
|
||||
|
||||
puts("\nPositional madness:");
|
||||
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);
|
||||
|
||||
puts("\nFloat madness:");
|
||||
printf("%20e\n", 123.456789123);
|
||||
printf("%20E\n", 0.00001);
|
||||
printf("%20f\n", 123.456789123);
|
||||
printf("%20F\n", 0.00001);
|
||||
printf("%20e\n", -123.456789123);
|
||||
printf("%020e\n", -123.456789123);
|
||||
|
||||
printf("%g\n", 100000.0);
|
||||
printf("%g\n", 1000000.0);
|
||||
printf("%e\n", 1000000.0);
|
||||
printf("%G\n", 0.0001);
|
||||
printf("%G\n", 0.00001);
|
||||
printf("%E\n", 0.00001);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user