diff --git a/src/libcore/int-template.rs b/src/libcore/int-template.rs index 07c3cc9011e..c937c88145d 100644 --- a/src/libcore/int-template.rs +++ b/src/libcore/int-template.rs @@ -9,7 +9,7 @@ export is_nonpositive, is_nonnegative; export range; export compl; export abs; -export parse_buf, from_str, to_str, str; +export parse_buf, from_str, to_str, to_str_bytes, str; const min_value: T = -1 as T << (inst::bits - 1 as T); const max_value: T = min_value - 1 as T; @@ -96,6 +96,14 @@ fn to_str(n: T, radix: uint) -> str { } else { uint::to_str(n as uint, radix) }; } +fn to_str_bytes(n: T, radix: uint, f: fn([u8]/&) -> U) -> U { + if n < 0 as T { + uint::to_str_bytes(true, -n as uint, radix, f) + } else { + uint::to_str_bytes(false, n as uint, radix, f) + } +} + #[doc = "Convert to a string"] fn str(i: T) -> str { ret to_str(i, 10u); } diff --git a/src/libcore/io.rs b/src/libcore/io.rs index 937aa5976ef..d65836f34b3 100644 --- a/src/libcore/io.rs +++ b/src/libcore/io.rs @@ -503,7 +503,7 @@ fn u64_from_be_bytes(data: [u8], start: uint, size: uint) -> u64 { impl writer_util for writer { fn write_char(ch: char) { if ch as uint < 128u { - self.write([ch as u8]); + self.write([ch as u8]/&); } else { self.write_str(str::from_char(ch)); } @@ -513,9 +513,12 @@ impl writer_util for writer { self.write_str(s); self.write_str("\n"/&); } - fn write_int(n: int) { self.write_str(int::to_str(n, 10u)); } - fn write_uint(n: uint) { self.write_str(uint::to_str(n, 10u)); } - + fn write_int(n: int) { + int::to_str_bytes(n, 10u) {|buf| self.write(buf) } + } + fn write_uint(n: uint) { + uint::to_str_bytes(false, n, 10u) {|buf| self.write(buf) } + } fn write_le_uint(n: uint, size: uint) { u64_to_le_bytes(n as u64, size) {|v| self.write(v); } } diff --git a/src/libcore/uint-template.rs b/src/libcore/uint-template.rs index 6e513b2f5e2..a63d01e6e8e 100644 --- a/src/libcore/uint-template.rs +++ b/src/libcore/uint-template.rs @@ -8,7 +8,8 @@ export is_positive, is_negative; export is_nonpositive, is_nonnegative; export range; export compl; -export to_str, from_str, from_str_radix, str, parse_buf; +export to_str, to_str_bytes; +export from_str, from_str_radix, str, parse_buf; const min_value: T = 0 as T; const max_value: T = 0 as T - 1 as T; @@ -102,10 +103,19 @@ Convert to a string in a given base Fails if `radix` < 2 or `radix` > 16 "] -fn to_str(num: T, radix: uint) -> str { - assert (1u < radix && radix <= 16u); - let mut n = num; - let radix = radix as T; +fn to_str(num: T, radix: uint) -> str unsafe { + to_str_bytes(false, num, radix) {|slice| + vec::unpack_slice(slice) {|p, len| + str::unsafe::from_buf_len(p, len) + } + } +} + +#[doc = "Low-level helper routine for string conversion."] +fn to_str_bytes(neg: bool, num: T, radix: uint, + f: fn([u8]/&) -> U) -> U unsafe { + + #[inline(always)] fn digit(n: T) -> u8 { if n <= 9u as T { n as u8 + '0' as u8 @@ -115,36 +125,70 @@ fn to_str(num: T, radix: uint) -> str { fail; } } - if n == 0u as T { ret "0"; } - let mut buf: [mut u8] = [mut]; - vec::reserve(buf, 20u); // Enough room to hold any number + assert (1u < radix && radix <= 16u); - while n != 0u as T { - buf += [digit(n % radix)]; - n /= radix; - } + // Enough room to hold any number in any radix. + // Worst case: 64-bit number, binary-radix, with + // a leading negative sign = 65 bytes. + let buf : [mut u8]/65 = + [mut + 0u8,0u8,0u8,0u8,0u8, 0u8,0u8,0u8,0u8,0u8, + 0u8,0u8,0u8,0u8,0u8, 0u8,0u8,0u8,0u8,0u8, - buf += [0u8]; + 0u8,0u8,0u8,0u8,0u8, 0u8,0u8,0u8,0u8,0u8, + 0u8,0u8,0u8,0u8,0u8, 0u8,0u8,0u8,0u8,0u8, - let mut start_idx = 0u; - let mut end_idx = buf.len() - 2u; - while start_idx < end_idx { - vec::swap(buf, start_idx, end_idx); - start_idx += 1u; - end_idx -= 1u; - } + 0u8,0u8,0u8,0u8,0u8, 0u8,0u8,0u8,0u8,0u8, + 0u8,0u8,0u8,0u8,0u8, 0u8,0u8,0u8,0u8,0u8, - unsafe { - let s = unsafe::reinterpret_cast(buf); - unsafe::forget(buf); - ret s; + 0u8,0u8,0u8,0u8,0u8 + ]/65; + + // FIXME: post-snapshot, you can do this without + // the raw pointers and unsafe bits, and the + // codegen will prove it's all in-bounds, no + // extra cost. + + vec::unpack_slice(buf) {|p, len| + let mp = p as *mut u8; + let mut i = len; + let mut n = num; + let radix = radix as T; + loop { + i -= 1u; + assert 0u < i && i < len; + *ptr::mut_offset(mp, i) = digit(n % radix); + n /= radix; + if n == 0 as T { break; } + } + + assert 0u < i && i < len; + + if neg { + i -= 1u; + *ptr::mut_offset(mp, i) = '-' as u8; + } + + vec::unsafe::form_slice(ptr::offset(p, i), + len - i, f) } } #[doc = "Convert to a string"] fn str(i: T) -> str { ret to_str(i, 10u); } +#[test] +fn test_to_str() { + assert to_str(0 as T, 10u) == "0"; + assert to_str(1 as T, 10u) == "1"; + assert to_str(2 as T, 10u) == "2"; + assert to_str(11 as T, 10u) == "11"; + assert to_str(11 as T, 16u) == "b"; + assert to_str(255 as T, 16u) == "ff"; + assert to_str(0xff as T, 10u) == "255"; +} + #[test] #[ignore] fn test_from_str() {