stdio, string, platform: fix a bug in printf() involving chars

Because we were previously converting the bytes in the format
string into Rust's char type and then printing that using the
format machinery, byte values that were not valid single-byte
UTF-8 characters failed to print correctly.  I found this while
trying to implement qsort() because the output of my test program
was mysteriously incorrect despite it working when I used glibc.
This commit is contained in:
Alex Lyon
2018-05-11 22:53:36 -07:00
parent 1bcc40c08f
commit 0cabecd5b5
5 changed files with 92 additions and 59 deletions
+64 -19
View File
@@ -58,6 +58,30 @@ pub unsafe fn cstr_from_bytes_with_nul_unchecked(bytes: &[u8]) -> *const c_char
&*(bytes as *const [u8] as *const c_char)
}
// NOTE: defined here rather than in string because memcpy() is useful in multiple crates
pub unsafe fn memcpy(s1: *mut c_void, s2: *const c_void, n: usize) -> *mut c_void {
let mut i = 0;
while i + 7 < n {
*(s1.offset(i as isize) as *mut u64) = *(s2.offset(i as isize) as *const u64);
i += 8;
}
while i < n {
*(s1 as *mut u8).offset(i as isize) = *(s2 as *const u8).offset(i as isize);
i += 1;
}
s1
}
pub trait Write: fmt::Write {
fn write_u8(&mut self, byte: u8) -> fmt::Result;
}
impl<'a, W: Write> Write for &'a mut W {
fn write_u8(&mut self, byte: u8) -> fmt::Result {
(**self).write_u8(byte)
}
}
pub struct FileWriter(pub c_int);
impl FileWriter {
@@ -73,6 +97,13 @@ impl fmt::Write for FileWriter {
}
}
impl Write for FileWriter {
fn write_u8(&mut self, byte: u8) -> fmt::Result {
self.write(&[byte]);
Ok(())
}
}
pub struct FileReader(pub c_int);
impl FileReader {
@@ -85,21 +116,19 @@ pub struct StringWriter(pub *mut u8, pub usize);
impl StringWriter {
pub unsafe fn write(&mut self, buf: &[u8]) {
for &b in buf.iter() {
if self.1 == 0 {
break;
} else if self.1 == 1 {
*self.0 = b'\0';
} else {
*self.0 = b;
}
if self.1 > 0 {
let copy_size = buf.len().min(self.1 - 1);
memcpy(
self.0 as *mut c_void,
buf.as_ptr() as *const c_void,
copy_size,
);
*self.0.offset(copy_size as isize) = b'\0';
self.0 = self.0.offset(1);
self.1 -= 1;
if self.1 > 0 {
*self.0 = b'\0';
}
// XXX: i believe this correctly mimics the behavior from before, but it seems
// incorrect (the next write will write after the NUL)
self.0 = self.0.offset(copy_size as isize + 1);
self.1 -= copy_size + 1;
}
}
}
@@ -111,15 +140,24 @@ impl fmt::Write for StringWriter {
}
}
impl Write for StringWriter {
fn write_u8(&mut self, byte: u8) -> fmt::Result {
unsafe { self.write(&[byte]) };
Ok(())
}
}
pub struct UnsafeStringWriter(pub *mut u8);
impl UnsafeStringWriter {
pub unsafe fn write(&mut self, buf: &[u8]) {
for &b in buf.iter() {
*self.0 = b;
self.0 = self.0.offset(1);
*self.0 = b'\0';
}
memcpy(
self.0 as *mut c_void,
buf.as_ptr() as *const c_void,
buf.len(),
);
*self.0.offset(buf.len() as isize) = b'\0';
self.0 = self.0.offset(buf.len() as isize);
}
}
@@ -129,3 +167,10 @@ impl fmt::Write for UnsafeStringWriter {
Ok(())
}
}
impl Write for UnsafeStringWriter {
fn write_u8(&mut self, byte: u8) -> fmt::Result {
unsafe { self.write(&[byte]) };
Ok(())
}
}