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

Side by Side 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 unified diff | Download patch
« no previous file with comments | « CHANGELOG.md ('k') | tests/lib/convert/line_splitter_test.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 part of dart.convert; 5 part of dart.convert;
6 6
7 // Character constants.
8 const int _LF = 10;
9 const int _CR = 13;
10
7 /** 11 /**
8 * This class splits [String] values into individual lines. 12 * A [Converter] that splits a [String] into individual lines.
13 *
14 * A line is terminated by either a CR (U+000D), a LF (U+000A), a
15 * CR+LF sequence (DOS line ending),
16 * and a final non-empty line can be ended by the end of the string.
17 *
18 * The returned lines do not contain the line terminators.
9 */ 19 */
10 class LineSplitter extends Converter<String, List<String>> { 20 class LineSplitter extends Converter<String, List<String>> {
11 21
12 const LineSplitter(); 22 const LineSplitter();
13 23
14 List<String> convert(String data) { 24 /// Split [lines] into individual lines.
15 var lines = new List<String>(); 25 ///
26 /// If [start] and [end] are provided, only split the contents of
27 /// `lines.substring(start, end)`. The [start] and [end] values must
28 /// specify a valid sub-range of [lines]
29 /// (`0 <= start <= end <= lines.length`).
30 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
31 end = RangeError.checkValidRange(start, end, lines.length);
32 int sliceStart = start;
33 int char = 0;
34 for (int i = start; i < end; i++) {
35 int previousChar = char;
36 char = lines.codeUnitAt(i);
37 if (char != _CR) {
38 if (char != _LF) continue;
39 if (previousChar == _CR) {
40 sliceStart = i + 1;
41 continue;
42 }
43 }
44 yield lines.substring(sliceStart, i);
45 sliceStart = i + 1;
46 }
47 if (sliceStart < end) {
48 yield lines.substring(sliceStart, end);
49 }
50 }
16 51
17 _LineSplitterSink._addSlice(data, 0, data.length, true, lines.add); 52 List<String> convert(String data) => split(data).toList();
18
19 return lines;
20 }
21 53
22 StringConversionSink startChunkedConversion(Sink<String> sink) { 54 StringConversionSink startChunkedConversion(Sink<String> sink) {
23 if (sink is! StringConversionSink) { 55 if (sink is! StringConversionSink) {
24 sink = new StringConversionSink.from(sink); 56 sink = new StringConversionSink.from(sink);
25 } 57 }
26 return new _LineSplitterSink(sink); 58 return new _LineSplitterSink(sink);
27 } 59 }
28 } 60 }
29 61
30 // TODO(floitsch): deal with utf8. 62 // TODO(floitsch): deal with utf8.
31 class _LineSplitterSink extends StringConversionSinkBase { 63 class _LineSplitterSink extends StringConversionSinkBase {
32 static const int _LF = 10;
33 static const int _CR = 13;
34
35 final StringConversionSink _sink; 64 final StringConversionSink _sink;
36 65
66 /// The carry-over from the previous chunk.
67 ///
68 /// If the previous slice ended in a line without a line terminator,
69 /// then the next slice may continue the line.
37 String _carry; 70 String _carry;
38 71
72 /// Whether to skip a leading LF character from the next slice.
73 ///
74 /// If the previous slice ended on a CR character, a following LF
75 /// would be part of the same line termination, and should be ignored.
76 ///
77 /// Only `true` when [_carry] is `null`.
78 bool _skipLeadingLF = false;
79
39 _LineSplitterSink(this._sink); 80 _LineSplitterSink(this._sink);
40 81
41 void addSlice(String chunk, int start, int end, bool isLast) { 82 void addSlice(String chunk, int start, int end, bool isLast) {
83 end = RangeError.checkValidRange(start, end, chunk.length);
84 // If the chunk is empty, it's probably because it's the last one.
85 // Handle that here, so we know the range is non-empty below.
86 if (start >= end) {
87 if (isLast) close();
88 return;
89 }
42 if (_carry != null) { 90 if (_carry != null) {
91 assert(!_skipLeadingLF);
43 chunk = _carry + chunk.substring(start, end); 92 chunk = _carry + chunk.substring(start, end);
44 start = 0; 93 start = 0;
45 end = chunk.length; 94 end = chunk.length;
46 _carry = null; 95 _carry = null;
96 } else if (_skipLeadingLF) {
97 if (chunk.codeUnitAt(start) == _LF) {
98 start += 1;
99 }
100 _skipLeadingLF = false;
47 } 101 }
48 _carry = _addSlice(chunk, start, end, isLast, _sink.add); 102 _addLines(chunk, start, end);
49 if (isLast) _sink.close(); 103 if (isLast) close();
50 } 104 }
51 105
52 void close() { 106 void close() {
53 addSlice('', 0, 0, true); 107 if (_carry != null) {
108 _sink.add(_carry);
109 _carry = null;
110 }
111 _sink.close();
54 } 112 }
55 113
56 static String _addSlice(String chunk, int start, int end, bool isLast, 114 void _addLines(String lines, int start, int end) {
57 void adder(String val)) { 115 int sliceStart = start;
58 116 int char = 0;
59 int pos = start; 117 for (int i = start; i < end; i++) {
60 while (pos < end) { 118 int previousChar = char;
61 int skip = 0; 119 char = lines.codeUnitAt(i);
62 int char = chunk.codeUnitAt(pos); 120 if (char != _CR) {
63 if (char == _LF) { 121 if (char != _LF) continue;
64 skip = 1; 122 if (previousChar == _CR) {
65 } else if (char == _CR) { 123 sliceStart = i + 1;
66 skip = 1; 124 continue;
67 if (pos + 1 < end) {
68 if (chunk.codeUnitAt(pos + 1) == _LF) {
69 skip = 2;
70 }
71 } else if (!isLast) {
72 return chunk.substring(start, end);
73 } 125 }
74 } 126 }
75 if (skip > 0) { 127 _sink.add(lines.substring(sliceStart, i));
76 adder(chunk.substring(start, pos)); 128 sliceStart = i + 1;
77 start = pos = pos + skip;
78 } else {
79 pos++;
80 }
81 } 129 }
82 if (pos != start) { 130 if (sliceStart < end) {
83 var carry = chunk.substring(start, pos); 131 _carry = lines.substring(sliceStart, end);
84 if (isLast) { 132 } else {
85 // Add remaining 133 _skipLeadingLF = (char == _CR);
86 adder(carry);
87 } else {
88 return carry;
89 }
90 } 134 }
91 return null;
92 } 135 }
93 } 136 }
OLDNEW
« 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