| OLD | NEW |
| (Empty) | |
| 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 |
| 3 // BSD-style license that can be found in the LICENSE file. |
| 4 |
| 5 part of dart.utf; |
| 6 |
| 7 class _HelperStreamController<T> extends StreamController<T> { |
| 8 final Function onPauseChanged; |
| 9 |
| 10 _HelperStreamController(this.onPauseChanged); |
| 11 |
| 12 void onPauseStateChange() { |
| 13 onPauseChanged(); |
| 14 } |
| 15 } |
| 16 |
| 17 abstract class _StringDecoder implements StreamTransformer<List<int>, String> { |
| 18 _HelperStreamController<String> _controller; |
| 19 StreamSubscription<List<int>> _subscription; |
| 20 List<int> _carry; |
| 21 List<int> _buffer; |
| 22 int _replacementChar; |
| 23 bool _paused = false; |
| 24 |
| 25 _StringDecoder(int this._replacementChar) { |
| 26 _controller = new _HelperStreamController<String>(_onPauseChanged); |
| 27 } |
| 28 |
| 29 void _onPauseChanged() { |
| 30 _paused = _controller.isPaused; |
| 31 if (_subscription == null) return; |
| 32 if (_paused) { |
| 33 _subscription.pause(); |
| 34 } else { |
| 35 _subscription.resume(); |
| 36 } |
| 37 } |
| 38 |
| 39 Stream<String> bind(Stream<List<int>> stream) { |
| 40 _subscription = stream.listen( |
| 41 _onData, |
| 42 onError: _controller.signalError, |
| 43 onDone: () { |
| 44 if (_carry != null) { |
| 45 _controller.add(new String.fromCharCodes( |
| 46 new List.fixedLength(_carry.length, fill: _replacementChar))); |
| 47 } |
| 48 _controller.close(); |
| 49 }, |
| 50 unsubscribeOnError: false); |
| 51 if (_paused) _subscription.pause(); |
| 52 return _controller.stream; |
| 53 } |
| 54 |
| 55 void _onData(List<int> bytes) { |
| 56 _buffer = <int>[]; |
| 57 List<int> carry = _carry; |
| 58 _carry = null; |
| 59 int pos = 0; |
| 60 int available = bytes.length; |
| 61 // If we have carry-over data, start from negative index, indicating carry |
| 62 // index. |
| 63 int goodChars = 0; |
| 64 if (carry != null) pos = -carry.length; |
| 65 while (pos < available) { |
| 66 int currentPos = pos; |
| 67 int getNext() { |
| 68 if (pos < 0) { |
| 69 return carry[pos++ + carry.length]; |
| 70 } else if (pos < available) { |
| 71 return bytes[pos++]; |
| 72 } |
| 73 return null; |
| 74 } |
| 75 int consumed = _processBytes(getNext); |
| 76 if (consumed > 0) { |
| 77 goodChars = _buffer.length; |
| 78 } else if (consumed == 0) { |
| 79 _buffer.length = goodChars; |
| 80 if (currentPos < 0) { |
| 81 _carry = []; |
| 82 _carry.addAll(carry); |
| 83 _carry.addAll(bytes); |
| 84 } else { |
| 85 _carry = bytes.getRange(currentPos, bytes.length - currentPos); |
| 86 } |
| 87 break; |
| 88 } else { |
| 89 // Invalid byte at position pos - 1 |
| 90 _buffer.length = goodChars; |
| 91 _addChar(-1); |
| 92 goodChars = _buffer.length; |
| 93 } |
| 94 } |
| 95 if (_buffer.length > 0) { |
| 96 // Limit to 'goodChars', if lower than actual charCodes in the buffer. |
| 97 _controller.add(new String.fromCharCodes(_buffer)); |
| 98 } |
| 99 _buffer = null; |
| 100 } |
| 101 |
| 102 int _processBytes(int getNext()); |
| 103 |
| 104 void _addChar(int char) { |
| 105 if (char > 0x10FFFF || char < 0) char = _replacementChar; |
| 106 _buffer.add(char); |
| 107 } |
| 108 } |
| 109 |
| 110 /** |
| 111 * StringTransformer that decodes a stream of UTF-8 encoded bytes. |
| 112 */ |
| 113 class Utf8DecoderTransformer extends _StringDecoder { |
| 114 Utf8DecoderTransformer( |
| 115 [int replacementChar = UNICODE_REPLACEMENT_CHARACTER_CODEPOINT]) |
| 116 : super(replacementChar); |
| 117 |
| 118 int _processBytes(int getNext()) { |
| 119 int value = getNext(); |
| 120 if ((value & 0xFF) != value) return -1; // Not a byte. |
| 121 if ((value & 0x80) == 0x80) { |
| 122 int additionalBytes; |
| 123 int min; |
| 124 if ((value & 0xe0) == 0xc0) { // 110xxxxx |
| 125 value = value & 0x1F; |
| 126 additionalBytes = 1; |
| 127 min = 0x80; |
| 128 } else if ((value & 0xf0) == 0xe0) { // 1110xxxx |
| 129 value = value & 0x0F; |
| 130 additionalBytes = 2; |
| 131 min = 0x800; |
| 132 } else if ((value & 0xf8) == 0xf0) { // 11110xxx |
| 133 value = value & 0x07; |
| 134 additionalBytes = 3; |
| 135 min = 0x10000; |
| 136 } else if ((value & 0xfc) == 0xf8) { // 111110xx |
| 137 value = value & 0x03; |
| 138 additionalBytes = 4; |
| 139 min = 0x200000; |
| 140 } else if ((value & 0xfe) == 0xfc) { // 1111110x |
| 141 value = value & 0x01; |
| 142 additionalBytes = 5; |
| 143 min = 0x4000000; |
| 144 } else { |
| 145 return -1; |
| 146 } |
| 147 for (int i = 0; i < additionalBytes; i++) { |
| 148 int next = getNext(); |
| 149 if (next == null) return 0; // Not enough chars, reset. |
| 150 if ((next & 0xc0) != 0x80 || (next & 0xff) != next) return -1; |
| 151 value = value << 6 | (next & 0x3f); |
| 152 } |
| 153 // Invalid charCode if less then minimum expected. |
| 154 if (value < min) value = -1; |
| 155 _addChar(value); |
| 156 return 1 + additionalBytes; |
| 157 } |
| 158 _addChar(value); |
| 159 return 1; |
| 160 } |
| 161 } |
| 162 |
| 163 |
| 164 abstract class _StringEncoder implements StreamTransformer<String, List<int>> { |
| 165 _HelperStreamController<List<int>> _controller; |
| 166 StreamSubscription<String> _subscription; |
| 167 |
| 168 void _onPauseChanged() { |
| 169 if (_controller.isPaused) { |
| 170 _subscription.pause(); |
| 171 } else { |
| 172 _subscription.resume(); |
| 173 } |
| 174 } |
| 175 Stream<List<int>> bind(Stream<String> stream) { |
| 176 _controller = new _HelperStreamController(_onPauseChanged); |
| 177 _subscription = stream.listen( |
| 178 (string) => _controller.add(_processString(string)), |
| 179 onError: _controller.signalError, |
| 180 onDone: _controller.close, |
| 181 unsubscribeOnError: false); |
| 182 return _controller.stream; |
| 183 } |
| 184 |
| 185 List<int> _processString(String string); |
| 186 } |
| 187 |
| 188 /** |
| 189 * StringTransformer that UTF-8 encodes a stream of strings. |
| 190 */ |
| 191 class Utf8EncoderTransformer extends _StringEncoder { |
| 192 List<int> _processString(String string) { |
| 193 var bytes = []; |
| 194 int pos = 0; |
| 195 List<int> codepoints = _utf16CodeUnitsToCodepoints(string.charCodes); |
| 196 int length = codepoints.length; |
| 197 for (int i = 0; i < length; i++) { |
| 198 int additionalBytes; |
| 199 int charCode = codepoints[i]; |
| 200 if (charCode <= 0x007F) { |
| 201 additionalBytes = 0; |
| 202 bytes.add(charCode); |
| 203 } else if (charCode <= 0x07FF) { |
| 204 // 110xxxxx (xxxxx is top 5 bits). |
| 205 bytes.add(((charCode >> 6) & 0x1F) | 0xC0); |
| 206 additionalBytes = 1; |
| 207 } else if (charCode <= 0xFFFF) { |
| 208 // 1110xxxx (xxxx is top 4 bits) |
| 209 bytes.add(((charCode >> 12) & 0x0F)| 0xE0); |
| 210 additionalBytes = 2; |
| 211 } else { |
| 212 // 11110xxx (xxx is top 3 bits) |
| 213 bytes.add(((charCode >> 18) & 0x07) | 0xF0); |
| 214 additionalBytes = 3; |
| 215 } |
| 216 for (int i = additionalBytes; i > 0; i--) { |
| 217 // 10xxxxxx (xxxxxx is next 6 bits from the top). |
| 218 bytes.add(((charCode >> (6 * (i - 1))) & 0x3F) | 0x80); |
| 219 } |
| 220 pos += additionalBytes + 1; |
| 221 } |
| 222 return bytes; |
| 223 } |
| 224 } |
| OLD | NEW |