Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(435)

Unified Diff: sdk/lib/convert/line_splitter.dart

Issue 1240623002: Add split function to LineSplitter class in dart:convert. (Closed) Base URL: https://github.com/dart-lang/sdk.git@master
Patch Set: Addto changelog Created 5 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « CHANGELOG.md ('k') | tests/lib/convert/line_splitter_test.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: sdk/lib/convert/line_splitter.dart
diff --git a/sdk/lib/convert/line_splitter.dart b/sdk/lib/convert/line_splitter.dart
index 84cb22c71c1dcc5b27555b548faeff8d6c97001d..0af2f2f63dd59c0a01b438c1008a9a60dcdb5e82 100644
--- a/sdk/lib/convert/line_splitter.dart
+++ b/sdk/lib/convert/line_splitter.dart
@@ -4,21 +4,53 @@
part of dart.convert;
+// Character constants.
+const int _LF = 10;
+const int _CR = 13;
+
/**
- * This class splits [String] values into individual lines.
+ * A [Converter] that splits a [String] into individual lines.
+ *
+ * A line is terminated by either a CR (U+000D), a LF (U+000A), a
+ * CR+LF sequence (DOS line ending),
+ * and a final non-empty line can be ended by the end of the string.
+ *
+ * The returned lines do not contain the line terminators.
*/
class LineSplitter extends Converter<String, List<String>> {
const LineSplitter();
- List<String> convert(String data) {
- var lines = new List<String>();
-
- _LineSplitterSink._addSlice(data, 0, data.length, true, lines.add);
-
- return lines;
+ /// Split [lines] into individual lines.
+ ///
+ /// If [start] and [end] are provided, only split the contents of
+ /// `lines.substring(start, end)`. The [start] and [end] values must
+ /// specify a valid sub-range of [lines]
+ /// (`0 <= start <= end <= lines.length`).
+ static Iterable<String> split(String lines, [int start = 0, int end]) sync* {
floitsch 2015/08/20 13:17:41 Generally we try to keep types in the core librari
Lasse Reichstein Nielsen 2015/08/20 14:00:31 I'd rather we had added "sync*<int>" like we asked
+ end = RangeError.checkValidRange(start, end, lines.length);
+ int sliceStart = start;
+ int char = 0;
+ for (int i = start; i < end; i++) {
+ int previousChar = char;
+ char = lines.codeUnitAt(i);
+ if (char != _CR) {
+ if (char != _LF) continue;
+ if (previousChar == _CR) {
+ sliceStart = i + 1;
+ continue;
+ }
+ }
+ yield lines.substring(sliceStart, i);
+ sliceStart = i + 1;
+ }
+ if (sliceStart < end) {
+ yield lines.substring(sliceStart, end);
+ }
}
+ List<String> convert(String data) => split(data).toList();
+
StringConversionSink startChunkedConversion(Sink<String> sink) {
if (sink is! StringConversionSink) {
sink = new StringConversionSink.from(sink);
@@ -29,65 +61,76 @@ class LineSplitter extends Converter<String, List<String>> {
// TODO(floitsch): deal with utf8.
class _LineSplitterSink extends StringConversionSinkBase {
- static const int _LF = 10;
- static const int _CR = 13;
-
final StringConversionSink _sink;
+ /// The carry-over from the previous chunk.
+ ///
+ /// If the previous slice ended in a line without a line terminator,
+ /// then the next slice may continue the line.
String _carry;
+ /// Whether to skip a leading LF character from the next slice.
+ ///
+ /// If the previous slice ended on a CR character, a following LF
+ /// would be part of the same line termination, and should be ignored.
+ ///
+ /// Only `true` when [_carry] is `null`.
+ bool _skipLeadingLF = false;
+
_LineSplitterSink(this._sink);
void addSlice(String chunk, int start, int end, bool isLast) {
+ end = RangeError.checkValidRange(start, end, chunk.length);
+ // If the chunk is empty, it's probably because it's the last one.
+ // Handle that here, so we know the range is non-empty below.
+ if (start >= end) {
+ if (isLast) close();
+ return;
+ }
if (_carry != null) {
+ assert(!_skipLeadingLF);
chunk = _carry + chunk.substring(start, end);
start = 0;
end = chunk.length;
_carry = null;
+ } else if (_skipLeadingLF) {
+ if (chunk.codeUnitAt(start) == _LF) {
+ start += 1;
+ }
+ _skipLeadingLF = false;
}
- _carry = _addSlice(chunk, start, end, isLast, _sink.add);
- if (isLast) _sink.close();
+ _addLines(chunk, start, end);
+ if (isLast) close();
}
void close() {
- addSlice('', 0, 0, true);
+ if (_carry != null) {
+ _sink.add(_carry);
+ _carry = null;
+ }
+ _sink.close();
}
- static String _addSlice(String chunk, int start, int end, bool isLast,
- void adder(String val)) {
-
- int pos = start;
- while (pos < end) {
- int skip = 0;
- int char = chunk.codeUnitAt(pos);
- if (char == _LF) {
- skip = 1;
- } else if (char == _CR) {
- skip = 1;
- if (pos + 1 < end) {
- if (chunk.codeUnitAt(pos + 1) == _LF) {
- skip = 2;
- }
- } else if (!isLast) {
- return chunk.substring(start, end);
+ void _addLines(String lines, int start, int end) {
+ int sliceStart = start;
+ int char = 0;
+ for (int i = start; i < end; i++) {
+ int previousChar = char;
+ char = lines.codeUnitAt(i);
+ if (char != _CR) {
+ if (char != _LF) continue;
+ if (previousChar == _CR) {
+ sliceStart = i + 1;
+ continue;
}
}
- if (skip > 0) {
- adder(chunk.substring(start, pos));
- start = pos = pos + skip;
- } else {
- pos++;
- }
+ _sink.add(lines.substring(sliceStart, i));
+ sliceStart = i + 1;
}
- if (pos != start) {
- var carry = chunk.substring(start, pos);
- if (isLast) {
- // Add remaining
- adder(carry);
- } else {
- return carry;
- }
+ if (sliceStart < end) {
+ _carry = lines.substring(sliceStart, end);
+ } else {
+ _skipLeadingLF = (char == _CR);
}
- return null;
}
}
« no previous file with comments | « CHANGELOG.md ('k') | tests/lib/convert/line_splitter_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698