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:
+64
-19
@@ -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(())
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user