Implement stack-only variants of int/uint str conversion and output.

This commit is contained in:
Graydon Hoare 2012-06-04 18:05:34 -07:00
parent 903033bb03
commit 7803488a43
3 changed files with 84 additions and 29 deletions

View File

@ -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<U>(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); }

View File

@ -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); }
}

View File

@ -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<U>(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() {