about summary refs log tree commit diff
path: root/src/libcore/uint-template.rs
diff options
context:
space:
mode:
authorGraydon Hoare <graydon@mozilla.com>2012-06-04 18:05:34 -0700
committerGraydon Hoare <graydon@mozilla.com>2012-06-04 18:06:59 -0700
commit7803488a43eb24864105506b3825f605bc87d693 (patch)
tree0d7c8fb13116959dba91aef625a0f563c8733d22 /src/libcore/uint-template.rs
parent903033bb03d05b66cbbf79a58fc9989887e35b65 (diff)
Implement stack-only variants of int/uint str conversion and output.
Diffstat (limited to 'src/libcore/uint-template.rs')
-rw-r--r--src/libcore/uint-template.rs92
1 files changed, 68 insertions, 24 deletions
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<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,30 +125,53 @@ 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,
+
+         0u8,0u8,0u8,0u8,0u8, 0u8,0u8,0u8,0u8,0u8,
+         0u8,0u8,0u8,0u8,0u8, 0u8,0u8,0u8,0u8,0u8,
+
+         0u8,0u8,0u8,0u8,0u8, 0u8,0u8,0u8,0u8,0u8,
+         0u8,0u8,0u8,0u8,0u8, 0u8,0u8,0u8,0u8,0u8,
+
+         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; }
+        }
 
-    buf += [0u8];
+        assert 0u < i && i < len;
 
-    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;
-    }
+        if neg {
+            i -= 1u;
+            *ptr::mut_offset(mp, i) = '-' as u8;
+        }
 
-    unsafe {
-        let s = unsafe::reinterpret_cast(buf);
-        unsafe::forget(buf);
-        ret s;
+        vec::unsafe::form_slice(ptr::offset(p, i),
+                                len - i, f)
     }
 }
 
@@ -146,6 +179,17 @@ fn to_str(num: T, radix: uint) -> str {
 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() {
     assert from_str("0") == some(0u as T);