Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS d.file | |
| 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. | |
| 4 | |
| 5 library pub.cr_lf_remover; | |
| 6 | |
| 7 import 'dart:convert'; | |
| 8 | |
| 9 import 'package:charcode/ascii.dart'; | |
| 10 | |
| 11 /// A converter that removes CR characters immediately preceding LF characters. | |
| 12 /// | |
| 13 /// This is useful when piping output from a Dart process on Windows. Due to | |
| 14 /// sdk#19334, an extra CR is added before every LF, even if one already | |
| 15 /// existed, which can cause data to become corrupted. By converting CR LFs to | |
| 16 /// LFs, this class ensures that when the output is emitted through the parent | |
| 17 /// process's stdout it will be identical to the output emitted by the child. | |
| 18 /// | |
| 19 /// Note that this will only work properly for Dart subprocesses, because | |
| 20 /// sdk#19334 guarantees that they will *never* emit an LF without a preceding | |
| 21 /// CR. | |
| 22 class CRLFRemover extends Converter<List<int>, List<int>> { | |
| 23 const CRLFRemover(); | |
| 24 | |
| 25 List<int> convert(List<int> data) { | |
| 26 var result; | |
| 27 var sink = startChunkedConversion( | |
| 28 new ByteConversionSink.withCallback((result_) => result = result_)); | |
| 29 sink.add(data); | |
| 30 sink.close(); | |
| 31 return result; | |
| 32 } | |
| 33 | |
| 34 ChunkedConversionSink startChunkedConversion(Sink<List<int>> sink) { | |
| 35 if (sink is! ByteConversionSink) sink = new ByteConversionSink.from(sink); | |
|
Bob Nystrom
2015/07/08 22:12:15
Document this.
nweiz
2015/07/09 00:30:14
Done.
| |
| 36 return new _CRLFSink(sink); | |
| 37 } | |
| 38 } | |
| 39 | |
| 40 /// The custom sink used to do chunked removal of CRs. | |
| 41 class _CRLFSink extends ByteConversionSink { | |
| 42 /// Whether the last chunk ended in CR. | |
| 43 /// | |
| 44 /// If it did, that CR wasn't printed in case it needs to be removed. | |
| 45 var _wasCR = false; | |
| 46 | |
| 47 /// The underlying sink. | |
| 48 final ByteConversionSink _sink; | |
| 49 | |
| 50 _CRLFSink(this._sink); | |
| 51 | |
| 52 void add(Iterable<int> chunk) { | |
| 53 if (chunk is! List) chunk = chunk.toList(); | |
| 54 addSlice(chunk, 0, chunk.length, false); | |
| 55 } | |
| 56 | |
| 57 void addSlice(List<int> chunk, int start, int end, bool isLast) { | |
| 58 var wasCR = _wasCR; | |
| 59 var nextSliceStart = start; | |
| 60 for (var i = start; i < end; i++) { | |
| 61 if (wasCR && chunk[i] == $lf) { | |
| 62 if (i != start) { | |
| 63 _sink.addSlice(chunk, nextSliceStart, i - 1, false); | |
| 64 nextSliceStart = i; | |
| 65 } | |
| 66 } else if (_wasCR && i == start) { | |
| 67 // If the last chunk ended in a CR and this chunk *doesn't* start with | |
| 68 // an LF, we need to write the CR from the last chunk. | |
| 69 _sink.add([$cr]); | |
| 70 } | |
| 71 | |
| 72 wasCR = chunk[i] == $cr; | |
| 73 } | |
| 74 | |
| 75 _wasCR = wasCR; | |
| 76 var finalSliceEnd = wasCR ? end - 1 : end; | |
| 77 _sink.addSlice(chunk, nextSliceStart, finalSliceEnd, isLast); | |
| 78 } | |
| 79 | |
| 80 void close() { | |
| 81 if (_wasCR) _sink.add([$cr]); | |
| 82 _sink.close(); | |
| 83 } | |
| 84 } | |
| OLD | NEW |