mirror of
https://github.com/dart-lang/sdk
synced 2024-10-04 15:30:34 +00:00
Added implementation of String.codeUnits.
Based on ListIterable implementation that should be reusable for a lot of our existing length/indexing-based iterables too. Review URL: https://codereview.chromium.org//12261009 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@18506 260f80e4-7a28-3924-810f-c04153c831b5
This commit is contained in:
parent
c10172597f
commit
dd31afbeca
|
@ -433,9 +433,7 @@ class _StringBase {
|
|||
return result;
|
||||
}
|
||||
|
||||
Iterable<int> get codeUnits {
|
||||
throw new UnimplementedError("String.codeUnits");
|
||||
}
|
||||
Iterable<int> get codeUnits => new CodeUnits(this);
|
||||
|
||||
Runes get runes => new Runes(this);
|
||||
|
||||
|
|
|
@ -4,6 +4,317 @@
|
|||
|
||||
part of dart._collection.dev;
|
||||
|
||||
|
||||
/**
|
||||
* An [Iterable] for classes that have efficient [length] and [elementAt].
|
||||
*
|
||||
* All other methods are implemented in terms of [length] and [elementAt],
|
||||
* including [iterator].
|
||||
*/
|
||||
abstract class ListIterable<E> extends Iterable<E> {
|
||||
int get length;
|
||||
E elementAt(int i);
|
||||
|
||||
Iterator<E> get iterator => new ListIterableIterator<E>(this);
|
||||
|
||||
void forEach(void action(E element)) {
|
||||
int length = this.length;
|
||||
for (int i = 0; i < length; i++) {
|
||||
action(elementAt(i));
|
||||
if (length != this.length) {
|
||||
throw new ConcurrentModificationError(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool get isEmpty => length != 0;
|
||||
|
||||
E get first {
|
||||
if (length == 0) throw new StateError("No elements");
|
||||
return elementAt(0);
|
||||
}
|
||||
|
||||
E get last {
|
||||
if (length == 0) throw new StateError("No elements");
|
||||
return elementAt(length - 1);
|
||||
}
|
||||
|
||||
E get single {
|
||||
if (length == 0) throw new StateError("No elements");
|
||||
if (length > 1) throw new StateError("Too many elements");
|
||||
return elementAt(0);
|
||||
}
|
||||
|
||||
bool contains(E element) {
|
||||
int length = this.length;
|
||||
for (int i = 0; i < length; i++) {
|
||||
if (elementAt(i) == element) return true;
|
||||
if (length != this.length) {
|
||||
throw new ConcurrentModificationError(this);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool every(bool test(E element)) {
|
||||
int length = this.length;
|
||||
for (int i = 0; i < length; i++) {
|
||||
if (!test(elementAt(i))) return false;
|
||||
if (length != this.length) {
|
||||
throw new ConcurrentModificationError(this);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool any(bool test(E element)) {
|
||||
int length = this.length;
|
||||
for (int i = 0; i < length; i++) {
|
||||
if (test(elementAt(i))) return true;
|
||||
if (length != this.length) {
|
||||
throw new ConcurrentModificationError(this);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
E firstMatching(bool test(E element), { E orElse() }) {
|
||||
int length = this.length;
|
||||
for (int i = 0; i < length; i++) {
|
||||
E element = elementAt(i);
|
||||
if (test(element)) return element;
|
||||
if (length != this.length) {
|
||||
throw new ConcurrentModificationError(this);
|
||||
}
|
||||
}
|
||||
if (orElse != null) return orElse();
|
||||
throw new StateError("No matching element");
|
||||
}
|
||||
|
||||
E lastMatching(bool test(E element), { E orElse() }) {
|
||||
int length = this.length;
|
||||
for (int i = length - 1; i >= 0; i++) {
|
||||
E element = elementAt(i);
|
||||
if (test(element)) return element;
|
||||
if (length != this.length) {
|
||||
throw new ConcurrentModificationError(this);
|
||||
}
|
||||
}
|
||||
if (orElse != null) return orElse();
|
||||
throw new StateError("No matching element");
|
||||
}
|
||||
|
||||
E singleMatching(bool test(E element)) {
|
||||
int length = this.length;
|
||||
E match = null;
|
||||
bool matchFound = false;
|
||||
for (int i = 0; i < length; i++) {
|
||||
E element = elementAt(i);
|
||||
if (test(element)) {
|
||||
if (matchFound) {
|
||||
throw new StateError("More than one matching element");
|
||||
}
|
||||
matchFound = true;
|
||||
match = element;
|
||||
}
|
||||
if (length != this.length) {
|
||||
throw new ConcurrentModificationError(this);
|
||||
}
|
||||
}
|
||||
if (matchFound) return match;
|
||||
throw new StateError("No matching element");
|
||||
}
|
||||
|
||||
E min([int compare(E a, E b)]) {
|
||||
if (length == 0) return null;
|
||||
if (compare == null) {
|
||||
var defaultCompare = Comparable.compare;
|
||||
compare = defaultCompare;
|
||||
}
|
||||
E min = elementAt(0);
|
||||
int length = this.length;
|
||||
for (int i = 1; i < length; i++) {
|
||||
E element = elementAt(i);
|
||||
if (compare(min, element) > 0) {
|
||||
min = element;
|
||||
}
|
||||
if (length != this.length) {
|
||||
throw new ConcurrentModificationError(this);
|
||||
}
|
||||
}
|
||||
return min;
|
||||
}
|
||||
|
||||
E max([int compare(E a, E b)]) {
|
||||
if (length == 0) return null;
|
||||
if (compare == null) {
|
||||
var defaultCompare = Comparable.compare;
|
||||
compare = defaultCompare;
|
||||
}
|
||||
E max = elementAt(0);
|
||||
int length = this.length;
|
||||
for (int i = 1; i < length; i++) {
|
||||
E element = elementAt(i);
|
||||
if (compare(max, element) < 0) {
|
||||
max = element;
|
||||
}
|
||||
if (length != this.length) {
|
||||
throw new ConcurrentModificationError(this);
|
||||
}
|
||||
}
|
||||
return max;
|
||||
}
|
||||
|
||||
String join([String separator]) {
|
||||
int length = this.length;
|
||||
if (separator != null && !separator.isEmpty) {
|
||||
if (length == 0) return "";
|
||||
String first = "${elementAt(0)}";
|
||||
if (length != this.length) {
|
||||
throw new ConcurrentModificationError(this);
|
||||
}
|
||||
StringBuffer buffer = new StringBuffer(first);
|
||||
for (int i = 1; i < length; i++) {
|
||||
buffer.add(separator);
|
||||
buffer.add("${elementAt(i)}");
|
||||
if (length != this.length) {
|
||||
throw new ConcurrentModificationError(this);
|
||||
}
|
||||
}
|
||||
return buffer.toString();
|
||||
} else {
|
||||
StringBuffer buffer = new StringBuffer();
|
||||
for (int i = 0; i < length; i++) {
|
||||
buffer.add("${elementAt(i)}");
|
||||
if (length != this.length) {
|
||||
throw new ConcurrentModificationError(this);
|
||||
}
|
||||
}
|
||||
return buffer.toString();
|
||||
}
|
||||
}
|
||||
|
||||
Iterable<E> where(bool test(E element)) => super.where(test);
|
||||
|
||||
Iterable map(f(E element)) => new MappedIterable(this, f);
|
||||
|
||||
Iterable mappedBy(f(E element)) => super.mappedBy(f);
|
||||
|
||||
reduce(var initialValue, combine(var previousValue, E element)) {
|
||||
var value = initialValue;
|
||||
int length = this.length;
|
||||
for (int i = 0; i < length; i++) {
|
||||
value = reduce(value, elementAt(i));
|
||||
if (length != this.length) {
|
||||
throw new ConcurrentModificationError(this);
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
Iterable<E> skip(int count) => new SubListIterable(this, count, null);
|
||||
|
||||
Iterable<E> skipWhile(bool test(E element)) => super.skipWhile(test);
|
||||
|
||||
Iterable<E> take(int count) => new SubListIterable(this, 0, count);
|
||||
|
||||
Iterable<E> takeWhile(bool test(E element)) => super.takeWhile(test);
|
||||
|
||||
List<E> toList() {
|
||||
List<E> result = new List(length);
|
||||
for (int i = 0; i < length; i++) {
|
||||
result[i] = elementAt(i);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Set<E> toSet() {
|
||||
Set<E> result = new Set<E>();
|
||||
for (int i = 0; i < length; i++) {
|
||||
result.add(elementAt(i));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
abstract class SubListIterable<E> extends ListIterable<E> {
|
||||
final Iterable<E> _iterable;
|
||||
final int _start;
|
||||
/** If null, represents the length of the iterable. */
|
||||
final int _endOrLength;
|
||||
|
||||
SubListIterable(this._iterable, this._start, this._endOrLength);
|
||||
|
||||
int get _endIndex {
|
||||
int length = _iterable.length;
|
||||
if (_endOrLength == null || _endOrLength > length) return length;
|
||||
return _endOrLength;
|
||||
}
|
||||
|
||||
int get _startIndex {
|
||||
int length = _iterable.length;
|
||||
if (_start > length) return length;
|
||||
return _start;
|
||||
}
|
||||
|
||||
int get length {
|
||||
int length = _iterable.length;
|
||||
if (_start >= length) return 0;
|
||||
if (_endOrLength == null || _endOrLength >= length) {
|
||||
return length - _start;
|
||||
}
|
||||
return _endOrLength - _start;
|
||||
}
|
||||
|
||||
E elementAt(int index) {
|
||||
int realIndex = _startIndex + index;
|
||||
if (index < 0 || realIndex >= _endIndex) {
|
||||
throw new RangeError.range(index, 0, length);
|
||||
}
|
||||
return _iterable.elementAt(realIndex);
|
||||
}
|
||||
|
||||
Iterable<E> skip(int count) {
|
||||
if (count < 0) throw new ArgumentError(count);
|
||||
return new SubListIterable(_iterable, _start + count, _endOrLength);
|
||||
}
|
||||
|
||||
Iterable<E> take(int count) {
|
||||
if (count < 0) throw new ArgumentError(count);
|
||||
if (_endOrLength == null) {
|
||||
return new SubListIterable(_iterable, _start, _start + count);
|
||||
} else {
|
||||
newEnd = _start + count;
|
||||
if (_endOrLength < newEnd) return this;
|
||||
return new SubListIterable(_iterable, _start, newEnd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ListIterableIterator<E> implements Iterator<E> {
|
||||
final Iterable<E> _iterable;
|
||||
final int _length;
|
||||
int _index;
|
||||
E _current;
|
||||
ListIterableIterator(Iterable<E> iterable)
|
||||
: _iterable = iterable, _length = iterable.length, _index = 0;
|
||||
|
||||
E get current => _current;
|
||||
|
||||
bool moveNext() {
|
||||
if (_length != _iterable.length) {
|
||||
throw new ConcurrentModificationError(_iterable);
|
||||
}
|
||||
if (_index == _length) {
|
||||
_current = null;
|
||||
return false;
|
||||
}
|
||||
_current = _iterable.elementAt(_index);
|
||||
_index++;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
typedef T _Transformation<S, T>(S value);
|
||||
|
||||
class MappedIterable<S, T> extends Iterable<T> {
|
||||
|
|
|
@ -149,9 +149,7 @@ class JSString implements String {
|
|||
return result;
|
||||
}
|
||||
|
||||
Iterable<int> get codeUnits {
|
||||
throw new UnimplementedError("String.codeUnits");
|
||||
}
|
||||
Iterable<int> get codeUnits => new CodeUnits(this);
|
||||
|
||||
Runes get runes => new Runes(this);
|
||||
|
||||
|
|
|
@ -449,7 +449,6 @@ class RuneIterator implements BiDirectionalIterator<int> {
|
|||
return string.substring(_position, _nextPosition);
|
||||
}
|
||||
|
||||
|
||||
bool moveNext() {
|
||||
_position = _nextPosition;
|
||||
if (_position == string.length) {
|
||||
|
@ -492,3 +491,16 @@ class RuneIterator implements BiDirectionalIterator<int> {
|
|||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An [Iterable] of the UTF-16 code units of a [String] in index order.
|
||||
*/
|
||||
class CodeUnits extends ListIterable<int> {
|
||||
/** The string that this is the code units of. */
|
||||
String string;
|
||||
|
||||
CodeUnits(this.string);
|
||||
|
||||
int get length => string.length;
|
||||
int elementAt(int i) => string.codeUnitAt(i);
|
||||
}
|
||||
|
|
44
tests/corelib/string_codeunits_test.dart
Normal file
44
tests/corelib/string_codeunits_test.dart
Normal file
|
@ -0,0 +1,44 @@
|
|||
// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
main() {
|
||||
test(String s) {
|
||||
Iterable<int> units = s.codeUnits;
|
||||
List<int> expectedUnits = <int>[];
|
||||
for (int i = 0; i < s.length; i++) {
|
||||
expectedUnits.add(s.codeUnitAt(i));
|
||||
}
|
||||
|
||||
Expect.equals(s.length, units.length);
|
||||
for (int i = 0; i < s.length; i++) {
|
||||
Expect.equals(s.codeUnitAt(i), units.elementAt(i));
|
||||
}
|
||||
|
||||
// for-in
|
||||
var res = [];
|
||||
for (int unit in units) {
|
||||
res.add(unit);
|
||||
}
|
||||
Expect.listEquals(expectedUnits, res);
|
||||
|
||||
// .map
|
||||
Expect.listEquals(expectedUnits.map((x) => x.toRadixString(16)).toList(),
|
||||
units.map((x) => x.toRadixString(16)).toList());
|
||||
}
|
||||
|
||||
test("abc");
|
||||
test("\x00\u0000\u{000000}");
|
||||
test("\u{ffff}\u{10000}\u{10ffff}");
|
||||
String string = new String.fromCharCodes(
|
||||
[0xdc00, 0xd800, 61, 0xd800, 0xdc00, 62, 0xdc00, 0xd800]);
|
||||
test(string);
|
||||
|
||||
// Setting position in the middle of a surrogate pair is not allowed.
|
||||
var r = new CodeUnits("\u{10000}");
|
||||
var it = r.iterator;
|
||||
Expect.isTrue(it.moveNext());
|
||||
Expect.equals(0xD800, it.current);
|
||||
Expect.isTrue(it.moveNext());
|
||||
Expect.equals(0xDC00, it.current);
|
||||
}
|
Loading…
Reference in a new issue