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

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

Issue 17580014: dart:convert library. Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Update Created 7 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 | « sdk/lib/convert/json.dart ('k') | sdk/lib/convert/string_converter.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
new file mode 100644
index 0000000000000000000000000000000000000000..c29b2c730feff884c216c5345c060d19db730ae1
--- /dev/null
+++ b/sdk/lib/convert/line_splitter.dart
@@ -0,0 +1,288 @@
+// Copyright (c) 2013, 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.
+
+part of dart.convert;
+
+class LineSplitter extends Converter<String, List<String>> {
+ LineSplitter();
+
+ ChunkedConversionSink<Object> startChunkedConversion(
+ ChunkedConversionSink sink) {
+ return new _LineSplitterSink(outputInterface.adapt(sink));
+ }
+
+ ChunkedConversionInterface get inputInterface =>
+ StringConversionSink.INTERFACE;
+
+ ChunkedConversionInterface get outputInterface =>
+ MultiStringConversionSink.INTERFACE;
+}
+
+class LineFuser extends Converter<List<String>, String> {
+ LineFuser();
+
+ String convert(List<String> input) => input.join("\n");
+
+ fuse(Converter other) => other.fuseInput(this);
+ fuseInput(Converter other) => new FusedConverter(other, this);
+
+ MultiStringConversionSink startChunkedConversion(ChunkedConversionSink sink) {
+ return new _LineFuserSink(outputInterface.adapt(sink));
+ }
+
+ ChunkedConversionInterface get inputInterface =>
+ StringConversionSink.INTERFACE;
+}
+
+class _LineSplitterSink extends StringConversionSinkBase {
+ static const int _LF = 10;
+ static const int _CR = 13;
+
+ Utf8Handler _utf8Handler;
+ MultiStringConversionSink _sink;
+ StringBuffer _carry;
+ // True if the last chunk finished with a _CR.
+ bool _ignoreLineFeed = false;
+
+ _LineSplitterSink(this._sink);
+
+ bool get _hasPartialUtf8Input {
+ return _utf8Handler != null && _utf8Handler.hasPartialInput;
+ }
+
+ bool get _hasPartialInput {
+ if (_carry != null && _carry.isNotEmpty) return true;
+ return _hasPartialUtf8Input;
+ }
+
+ int _findFirstLineSplit(List<int> charCodes, int start, int end) {
+ for (int i = start; i < end; i++) {
+ int codeUnit = charCodes[i];
+ if (codeUnit == _LF || codeUnit == _CR) {
+ return i;
+ };
+ }
+ return end;
+ }
+
+ void _addCarry(List<int> charCodes, int start, int end) {
+ if (_utf8Handler != null && _utf8Handler.hasPartialInput) {
+ throw "Incomplete UTF8 sequence";
+ }
+ if (_carry == null) _carry = new StringBuffer();
+ for (int i = start; i < end; i++) {
+ _carry.writeCharCode(charCodes[i]);
+ }
+ }
+
+ void _addUtf8Carry(List<int> codeUnits, int start, int end) {
+ if (_utf8Handler == null) _utf8Handler = new Utf8Handler();
+ if (_carry == null) _carry = new StringBuffer();
+ _utf8Handler.convertToSink(codeUnits, start, end, _carry);
+ }
+
+ void _split(
+ List<int> charCodes,
+ int startIndex,
+ int endIndex,
+ bool isLast,
+ void addLine(List<int> charCodes, int start, int end, bool isLast),
+ void addCarry(List<int> charCodes, int start, int end)) {
+ int lineStart = startIndex;
+ // If the last chunk ended with a carriage-return we have to ignore a
+ // leading line feed.
+ if (_ignoreLineFeed) {
+ if (startIndex != endIndex && charCodes[startIndex] == _LF) {
+ startIndex++;
+ _ignoreLineFeed = false;
+ }
+ }
+ // Set the `_ignoreLineFeed` boolean for the next chunk.
+ if (startIndex != endIndex) {
+ if (charCodes[endIndex - 1] == _CR) {
+ _ignoreLineFeed = true;
+ }
+ }
+ // If there is a carry we have to append the next part of the line to the
+ // buffer.
+ if (_hasPartialInput) {
+ int firstSplit = _findFirstLineSplit(charCodes, startIndex, endIndex);
+ addCarry(charCodes, startIndex, firstSplit);
+ if (firstSplit == endIndex) {
+ // If `isLast` then nothing comes anymore. Send the carry.
+ if (isLast) {
+ if (_hasPartialUtf8Input) {
+ throw "Incomplete Utf8 sequence";
+ }
+ _sink.addString(_carry.toString(), true);
+ }
+ // Otherwise just return and wait for the next chunk.
+ return;
+ } else {
+ // Send the string that is in the carry and reset it.
+ _sink.addString(_carry.toString(), false);
+ _carry.clear();
+ lineStart = firstSplit + 1;
+ if (charCodes[firstSplit] == _CR &&
+ firstSplit + 1 < endIndex &&
+ charCodes[firstSplit + 1] == _LF) {
+ // Ignore LF following a CR.
+ lineStart++;
+ }
+ }
+ }
+ assert(!_hasPartialInput);
+
+ // At this point the carry is empty. We are now exclusively working on the
+ // new chunk.
+ for (int i = lineStart; i < endIndex; i++) {
+ int codeUnit = charCodes[i];
+ if (codeUnit == _LF) {
+ bool isLastLine = isLast && i == endIndex - 1;
+ addLine(charCodes, lineStart, i, isLastLine);
+ lineStart = i + 1;
+ } else if (codeUnit == _CR) {
+ int lineEnd = i;
+ if (i + 1 < charCodes.length && charCodes[i + 1] == _LF) {
+ i++;
+ }
+ bool isLastLine = isLast && i == endIndex - 1;
+ addLine(charCodes, lineStart, lineEnd, isLastLine);
+ lineStart = i + 1;
+ }
+ }
+ if (lineStart != endIndex) {
+ // The last part of the chunk was not consumed. Store it in the carry or
+ // send it directly if this is the last chunk.
+ if (isLast) {
+ addLine(charCodes, lineStart, endIndex, true);
+ } else {
+ addCarry(charCodes, lineStart, endIndex);
+ }
+ } else if (isLast) {
+ _sink.close();
+ }
+ }
+
+ void add(String str) => addString(str, false);
+
+ void addString(String str, bool isLast) {
+ _split(str.codeUnits, 0, str.length, isLast, _sink.addCodeUnits, _addCarry);
+ }
+
+ void addCodeUnits(List<int> codeUnits, int start, int end, bool isLast) {
+ _split(codeUnits, start, end, isLast, _sink.addCodeUnits, _addCarry);
+ }
+
+ void addRunes(List<int> runes, int start, int end, bool isLast) {
+ _split(runes, start, end, isLast, _sink.addRunes, _addCarry);
+ }
+
+ void addUtf8(List<int> utf8Units, int start, int end, bool isLast) {
+ _split(utf8Units, start, end, isLast, _sink.addUtf8, _addUtf8Carry);
+ }
+
+ void addIsoLatin1(List<int> codeUnits, int start, int end, bool isLast) {
+ _split(codeUnits, start, end, isLast, _sink.addIsoLatin1, _addCarry);
+ }
+
+ void addAscii(List<int> codeUnits, int start, int end, bool isLast) {
+ _split(codeUnits, start, end, isLast, _sink.addAscii, _addCarry);
+ }
+}
+
+class _LineFuserSink implements MultiStringConversionSink {
+ static const _NL = 10; // New line code.
+ bool needsSeparator = false;
+ StringConversionSink _sink;
+
+ _LineFuserSink(this._sink);
+
+ var _handler = null;
+
+ void writeNewLineIfNecessary() {
+ if (needsSeparator) _sink.writeCharCode(_NL);
+ needsSeparator = true;
+ }
+
+ void addNonChunked(List<String> list) {
+ _sink.writeAll(list, "\n");
+ _sink.close();
+ }
+
+ void add(String str) {
+ writeNewLineIfNecessary();
+ _sink.add(str);
+ }
+
+ void addString(String str, bool isLast) {
+ writeNewLineIfNecessary();
+ _sink.addString(str, isLast);
+ }
+
+ void addCodeUnits(List<int> codeUnits, int start, int end, bool isLast) {
+ writeNewLineIfNecessary();
+ _sink.addCodeUnits(codeUnits, start, end, isLast);
+ }
+ void addRunes(List<int> runes, int start, int end, bool isLast) {
+ writeNewLineIfNecessary();
+ _sink.addRunes(runes, start, end, isLast);
+ }
+ void addUtf8(List<int> utf8Units, int start, int end, bool isLast) {
+ writeNewLineIfNecessary();
+ _sink.addUtf8(utf8Units, start, end, isLast);
+ }
+ void addIsoLatin1(List<int> codeUnits, int start, int end, bool isLast) {
+ writeNewLineIfNecessary();
+ _sink.addIsoLatin1(codeUnits, start, end, isLast);
+ }
+ void addAscii(List<int> codeUnits, int start, int end, bool isLast) {
+ writeNewLineIfNecessary();
+ _sink.addAscii(codeUnits, start, end, isLast);
+ }
+ void iterateCodeUnits(StringIterator iterator, bool isLast) {
+ writeNewLineIfNecessary();
+ _sink.iterateCodeUnits(iterator, isLast);
+ }
+ void iterateRunes(StringIterator iterator, bool isLast) {
+ writeNewLineIfNecessary();
+ _sink.iterateRunes(iterator, isLast);
+ }
+ void iterateUtf8(StringIterator iterator, bool isLast) {
+ writeNewLineIfNecessary();
+ _sink.iterateUtf8(iterator, isLast);
+ }
+ void iterateIsoLatin1(StringIterator iterator, bool isLast) {
+ writeNewLineIfNecessary();
+ _sink.iterateIsoLatin1(iterator, isLast);
+ }
+ void iterateAscii(StringIterator iterator, bool isLast) {
+ writeNewLineIfNecessary();
+ _sink.iterateAscii(iterator, isLast);
+ }
+
+ void write(Object o) {
+ writeNewLineIfNecessary();
+ _sink.write(o);
+ }
+ void writeln([Object o]) {
+ writeNewLineIfNecessary();
+ _sink.writeln(o);
+ }
+ void writeCharCode(int charCode) {
+ writeNewLineIfNecessary();
+ _sink.writeCharCode(charCode);
+ }
+ void writeAll(Iterable objects, [String separator = ""]) {
+ writeNewLineIfNecessary();
+ _sink.writeAll(objects, separator);
+ }
+
+ close() {
+ _sink.close();
+ }
+
+ ChunkedConversionInterface get interface =>
+ MultiStringConversionSink.INTERFACE;
+}
« no previous file with comments | « sdk/lib/convert/json.dart ('k') | sdk/lib/convert/string_converter.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698