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 |