Index: sdk/lib/core/string.dart |
diff --git a/sdk/lib/core/string.dart b/sdk/lib/core/string.dart |
index 7f2dcbd48b4fc51306c3fcf896c0c1c91a876b14..e685523b4e98caced7f2e3e03f777a66403ee7b0 100644 |
--- a/sdk/lib/core/string.dart |
+++ b/sdk/lib/core/string.dart |
@@ -189,3 +189,97 @@ abstract class String implements Comparable, Pattern { |
*/ |
String toUpperCase(); |
} |
+ |
+class Runes implements Iterable<int> { |
+ String _string; |
+ Runes(this._string); |
+ RuneIterator get iterator => new RuneIterator(_string); |
+} |
+ |
+/** [Iterator] for reading Unicode code points out of a Dart string. */ |
+class RuneIterator implements BiDirectionalIterator<int> { |
+ /** String being iterated. */ |
+ final String _string; |
+ /** Position before the current code point. */ |
+ int _position; |
+ /** Position after the current code point. */ |
+ int _nextPosition; |
+ /** |
+ * Current code point. |
+ * |
+ * If the iterator has hit either end, the [_currentCodePoint] is null |
+ * and [: _position == _nextPosition :]. |
+ */ |
+ int _currentCodePoint; |
+ |
+ /** Create an iterator positioned at the beginning of the string. */ |
+ RuneIterator(String string) |
+ : _string = string, _position = 0, _nextPosition = 0; |
+ |
+ /** |
+ * Create an iterator positioned before the [start]th code unit of the string. |
+ * |
+ * A [moveNext] will make the following code point the current value, and a |
+ * [movePrevious] will make the preceding code pount the current value. |
+ * |
+ * If the [start] position is in the middle of a surrogate pair, then the |
+ * first result in either direction will be an unmatched surrogate. |
+ */ |
+ RuneIterator.at(String string, int start) |
+ : _string = string, _position = start, _nextPosition = start { |
+ if (start < 0 || start > string.length) { |
+ throw new RangeError.range(start, 0, string.length); |
+ } |
+ } |
+ |
+ int get current => _currentCodePoint; |
+ |
+ bool moveNext() { |
erikcorry
2013/01/30 13:26:55
This one is factored into two functions, whereas t
Lasse Reichstein Nielsen
2013/01/30 14:03:37
ACK. I was using _readCharForward in the .at const
|
+ _position = _nextPosition; |
+ if (_position == _string.length) { |
+ _currentCodePoint = null; |
+ return false; |
+ } |
+ _readCharForward(); |
+ return true; |
+ } |
+ |
+ // Decodes code units at _position, updates _currentCodePoint, _nextPosition. |
+ void _readCharForward() { |
+ int codeUnit = _string.charCodeAt(_position); |
+ int nextPosition = _position + 1; |
+ if ((codeUnit & 0xFC00) == 0xD800 && nextPosition < _string.length) { |
+ int nextCodeUnit = _string.charCodeAt(nextPosition); |
+ if ((nextCodeUnit & 0xFC00) == 0xDC00) { |
+ _nextPosition = nextPosition + 1; |
+ _currentCodePoint = |
+ 0x10000 + ((nextCodeUnit & 0x3FF) << 10) | (codeUnit & 0x3FF); |
Lasse Reichstein Nielsen
2013/01/30 14:03:37
Good catch, these two are swapped (copy-n-paste bu
|
+ return; |
+ } |
+ } |
+ _nextPosition = nextPosition; |
+ _currentCodePoint = codeUnit; |
+ } |
+ |
+ bool movePrevious() { |
+ if (_position == 0) { |
+ _currentCodePoint = null; |
+ return false; |
+ } |
+ int position = _position - 1; |
+ _nextPosition = _position; |
+ int codeUnit = _string.charCodeAt(position); |
+ if ((codeUnit & 0xFC00) == 0xDC00 && position > 0) { |
+ int prevCodeUnit = _string.charCodeAt(position - 1); |
+ if ((prevCodeUnit & 0xFC00) == 0xD800) { |
+ _position = position - 1; |
+ _currentCodePoint = |
+ 0x10000 + ((prevCodeUnit & 0x3FF) << 10) | (codeUnit & 0x3FF); |
+ return true; |
+ } |
+ } |
+ _position = position; |
+ _currentCodePoint = codeUnit; |
+ return true; |
+ } |
+} |