about summary refs log tree commit diff
path: root/src/libstd
diff options
context:
space:
mode:
authorHuon Wilson <dbau.pp+github@gmail.com>2014-02-17 00:09:45 +1100
committerHuon Wilson <dbau.pp+github@gmail.com>2014-02-17 23:53:49 +1100
commitb7656d048feb828af12278a6028c17b0c86241b3 (patch)
tree0c789698c04fcfdd706d4f5aa5b229dac5486b5d /src/libstd
parent493a4b63c1d6791ae7d2001123d8953bd62aa443 (diff)
std: convert str::from_utf16 to an external iterator.
Fixes #12316.
Diffstat (limited to 'src/libstd')
-rw-r--r--src/libstd/str.rs65
1 files changed, 38 insertions, 27 deletions
diff --git a/src/libstd/str.rs b/src/libstd/str.rs
index 8214382fb0d..93abf8bc542 100644
--- a/src/libstd/str.rs
+++ b/src/libstd/str.rs
@@ -824,41 +824,52 @@ pub fn is_utf16(v: &[u16]) -> bool {
     }
 }
 
-/// Iterates over the utf-16 characters in the specified slice, yielding each
-/// decoded unicode character to the function provided.
+/// An iterator that decodes UTF-16 encoded codepoints from a vector
+/// of `u16`s.
 ///
-/// # Failures
-///
-/// * Fails on invalid utf-16 data
-pub fn utf16_chars(v: &[u16], f: |char|) {
-    let len = v.len();
-    let mut i = 0u;
-    while i < len && v[i] != 0u16 {
-        let u = v[i];
-
-        if  u <= 0xD7FF_u16 || u >= 0xE000_u16 {
-            f(unsafe { cast::transmute(u as u32) });
-            i += 1u;
+/// Fails when it encounters invalid UTF-16 data.
+pub struct UTF16Chars<'a> {
+    priv iter: vec::Items<'a, u16>
+}
+impl<'a> Iterator<char> for UTF16Chars<'a> {
+    fn next(&mut self) -> Option<char> {
+        let u = match self.iter.next() {
+            Some(u) => *u,
+            None => return None
+        };
+        match char::from_u32(u as u32) {
+            Some(c) => Some(c),
+            None => {
+                let u2 = *self.iter.next().expect("UTF16Chars: unmatched lead surrogate");
+                if u < 0xD7FF || u > 0xDBFF ||
+                    u2 < 0xDC00 || u2 > 0xDFFF {
+                    fail!("UTF16Chars: invalid surrogate pair")
+                }
 
-        } else {
-            let u2 = v[i+1u];
-            assert!(u >= 0xD800_u16 && u <= 0xDBFF_u16);
-            assert!(u2 >= 0xDC00_u16 && u2 <= 0xDFFF_u16);
-            let mut c: u32 = (u - 0xD800_u16) as u32;
-            c = c << 10;
-            c |= (u2 - 0xDC00_u16) as u32;
-            c |= 0x1_0000_u32;
-            f(unsafe { cast::transmute(c) });
-            i += 2u;
+                let mut c = (u - 0xD800) as u32 << 10 | (u2 - 0xDC00) as u32 | 0x1_0000;
+                char::from_u32(c)
+            }
         }
     }
+
+    fn size_hint(&self) -> (uint, Option<uint>) {
+        let (low, high) = self.iter.size_hint();
+        // we could be entirely surrogates (2 elements per char), or
+        // entirely non-surrogates (1 element per char)
+        (low / 2, high)
+    }
+}
+
+/// Create an iterator over the UTF-16 encoded codepoints in `v`.
+///
+/// The iterator fails if it attempts to decode invalid UTF-16 data.
+pub fn utf16_chars<'a>(v: &'a [u16]) -> UTF16Chars<'a> {
+    UTF16Chars { iter : v.iter() }
 }
 
 /// Allocates a new string from the utf-16 slice provided
 pub fn from_utf16(v: &[u16]) -> ~str {
-    let mut buf = with_capacity(v.len());
-    utf16_chars(v, |ch| buf.push_char(ch));
-    buf
+    utf16_chars(v).collect()
 }
 
 /// Allocates a new string with the specified capacity. The string returned is