Index: runtime/bin/string_stream.dart |
diff --git a/runtime/bin/string_stream.dart b/runtime/bin/string_stream.dart |
new file mode 100644 |
index 0000000000000000000000000000000000000000..5f4c118902e7ff519469e1ecc359c5cd00c1b1e1 |
--- /dev/null |
+++ b/runtime/bin/string_stream.dart |
@@ -0,0 +1,148 @@ |
+// Copyright (c) 2011, 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. |
+ |
+// Utility class which can deliver bytes one by one from a number of |
+// buffers added. |
+class BufferList { |
+ BufferList() : _index = 0, _length = 0, _buffers = new Queue(); |
+ |
+ void add(List<int> buffer) { |
+ _buffers.addLast(buffer); |
+ _length += buffer.length; |
+ } |
+ |
+ int peek() { |
+ return _buffers.first()[_index]; |
+ } |
+ |
+ int next() { |
+ int value = _buffers.first()[_index++]; |
+ _length--; |
+ if (_index == _buffers.first().length) { |
+ _buffers.removeFirst(); |
+ _index = 0; |
+ } |
+ return value; |
+ } |
+ |
+ int get length() => _length; |
+ |
+ int _length; |
+ Queue<List<int>> _buffers; |
+ int _index; |
+} |
+ |
+ |
+// Utility class for decoding UTF-8 from data delivered as a stream of |
+// bytes. |
+class UTF8Decoder { |
+ UTF8Decoder() |
+ : _bufferList = new BufferList(), |
+ _result = new StringBuffer(); |
+ |
+ // Add UTF-8 encoded data. |
+ int write(List<int> buffer) { |
+ _bufferList.add(buffer); |
+ // Decode as many bytes into characters as possible. |
+ while (_bufferList.length > 0) { |
+ if (!_processNext()) { |
+ break; |
+ } |
+ } |
+ } |
+ |
+ // Check if any characters have been decoded so far. |
+ bool isEmpty() { |
+ return _result.isEmpty(); |
+ } |
+ |
+ // Return the string decoded so far. |
+ String get decodedString() { |
+ if (isEmpty()) { |
+ return null; |
+ } else { |
+ String result = _result.toString(); |
+ _result = new StringBuffer(); |
+ return result; |
+ } |
+ } |
+ |
+ // Process the next UTF-8 encoded character. |
+ bool _processNext() { |
+ // Peek the next byte to calculate the number of bytes required |
+ // for the next character. |
+ int value = _bufferList.peek() & 0xFF; |
+ if ((value & 0x80) == 0x80) { |
+ int additionalBytes; |
+ if ((value & 0xe0) == 0xc0) { // 110xxxxx |
+ value = value & 0x1F; |
+ additionalBytes = 1; |
+ } else if ((value & 0xf0) == 0xe0) { // 1110xxxx |
+ value = value & 0x0F; |
+ additionalBytes = 2; |
+ } else { // 11110xxx |
+ value = value & 0x07; |
+ additionalBytes = 3; |
+ } |
+ // Check if there are not enough bytes to decode the character |
+ // return false. |
+ if (_bufferList.length < additionalBytes + 1) { |
+ return false; |
+ } |
+ // Remove the value peeked from the buffer list. |
+ _bufferList.next(); |
+ for (int i = 0; i < additionalBytes; i++) { |
+ int byte = _bufferList.next(); |
+ value = value << 6 | (byte & 0x3F); |
+ } |
+ } else { |
+ // Remove the value peeked from the buffer list. |
+ _bufferList.next(); |
+ } |
+ _result.addCharCode(value); |
+ return true; |
+ } |
+ |
+ BufferList _bufferList; |
+ StringBuffer _result; |
+} |
+ |
+ |
+class _StringInputStream implements StringInputStream { |
+ _StringInputStream(InputStream2 this._input, [String this._encoding]) { |
+ if (_encoding == null) { |
+ _encoding = "UTF-8"; |
+ } |
+ if (_encoding != "UTF-8") { |
+ throw new StreamException("Unsupported encoding $_encoding"); |
+ } |
+ _decoder = new UTF8Decoder(); |
+ _input.dataHandler = _dataHandler; |
+ } |
+ |
+ String read() { |
+ return _decoder.decodedString; |
+ } |
+ |
+ String get encoding() => _encoding; |
+ |
+ void set dataHandler(void callback()) { |
+ _clientDataHandler = callback; |
+ } |
+ |
+ void _dataHandler() { |
+ List<int> buffer = _input.read(); |
+ if (buffer != null) { |
+ _decoder.write(buffer); |
+ } |
+ if (!_decoder.isEmpty() && _clientDataHandler != null) { |
+ _clientDataHandler(); |
+ } |
+ } |
+ |
+ InputStream2 _input; |
+ String _encoding; |
+ UTF8Decoder _decoder; |
+ var _clientDataHandler; |
+} |