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

Unified Diff: runtime/bin/string_stream.dart

Issue 8318009: Update the streams interfaces (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Addressed review comments from ager@ Created 9 years, 2 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 | « runtime/bin/socket_stream.dart ('k') | tests/standalone/src/EchoServerStreamReadUntilTest.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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..e90004e7e1706d2143496c839202db309567993c
--- /dev/null
+++ b/runtime/bin/string_stream.dart
@@ -0,0 +1,329 @@
+// 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;
+}
+
+
+// Interface for decoders decoding binary data into objects of type T.
+interface _Decoder<T> {
+ // Add more binary data to be decoded. The ownership of the buffer
+ // is transfered to the decoder and the caller most not modify it any more.
+ int write(List<int> buffer);
+
+ // Returns whether any decoded data is available.
+ bool isEmpty();
+
+ // Get the data decoded since the last call to decode.
+ T get decoded();
+}
+
+
+class DecoderException implements Exception {
+ const DecoderException([String this.message]);
+ String toString() => "DecoderException: $message";
+ final String message;
+}
+
+
+// Utility class for decoding UTF-8 from data delivered as a stream of
+// bytes.
+class _StringDecoderBase implements _Decoder<String> {
+ _StringDecoderBase()
+ : _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;
+ }
+ }
+ return buffer.length;
+ }
+
+ // Check if any characters have been decoded since the last call to decode.
+ bool isEmpty() {
+ return _result.isEmpty();
+ }
+
+ // Return the string decoded since the last call to decode.
+ String get decoded() {
+ if (isEmpty()) {
+ return null;
+ } else {
+ String result = _result.toString();
+ _result = new StringBuffer();
+ return result;
+ }
+ }
+
+ abstract bool _processNext();
+
+ _BufferList _bufferList;
+ StringBuffer _result;
+}
+
+
+// Utility class for decoding ascii data delivered as a stream of
+// bytes.
+class _AsciiDecoder extends _StringDecoderBase {
+ // Process the next ascii encoded character.
+ bool _processNext() {
+ while (_bufferList.length > 0) {
+ int byte = _bufferList.next();
+ if (byte > 127) {
+ throw new DecoderException("Illegal ASCII character $byte");
+ }
+ _result.addCharCode(byte);
+ }
+ return true;
+ }
+}
+
+
+// Utility class for decoding Latin-1 data delivered as a stream of
+// bytes.
+class _Latin1Decoder extends _StringDecoderBase {
+ // Process the next Latin-1 encoded character.
+ bool _processNext() {
+ while (_bufferList.length > 0) {
+ int byte = _bufferList.next();
+ _result.addCharCode(byte);
+ }
+ return true;
+ }
+}
+
+
+// Utility class for decoding UTF-8 from data delivered as a stream of
+// bytes.
+class _UTF8Decoder extends _StringDecoderBase {
+ // 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 enough bytes to decode the character. Otherwise
+ // 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;
+ }
+}
+
+
+class _StringInputStream implements StringInputStream {
+ _StringInputStream(InputStream this._input, [String this._encoding]) {
+ if (_encoding == null) {
+ _encoding = "UTF-8";
+ }
+ if (_encoding == "UTF-8") {
+ _decoder = new _UTF8Decoder();
+ } else if (_encoding == "ISO-8859-1") {
+ _decoder = new _Latin1Decoder();
+ } else if (_encoding == "ASCII") {
+ _decoder = new _AsciiDecoder();
+ } else {
+ throw new StreamException("Unsupported encoding $_encoding");
+ }
+ _input.dataHandler = _dataHandler;
+ _input.closeHandler = _closeHandler;
+ }
+
+ String read() {
+ // If there is buffered data return that first.
+ var decodedString = _decoder.decoded;
+ if (_buffer != null) {
+ var result = _buffer;
+ _resetBuffer();
+ if (decodedString != null) result += decodedString;
+ return result;
+ } else {
+ if (decodedString != null) {
+ return decodedString;
+ } else {
+ _readData();
+ return _decoder.decoded;
+ }
+ }
+ }
+
+ String readLine() {
+ // Get line from the buffer if possible.
+ if (_buffer != null) {
+ var result = _readLineFromBuffer();
+ if (result != null) return result;
+ }
+ // Try to fill more data into the buffer and read a line.
+ if (_fillBuffer()) {
+ if (_eof && _buffer == null) return null;
+ return _readLineFromBuffer();
+ }
+ return null;
+ }
+
+ String get encoding() => _encoding;
+
+ bool get closed() => _closed;
+
+ void set dataHandler(void callback()) {
+ _clientDataHandler = callback;
+ }
+
+ void set closeHandler(void callback()) {
+ _clientCloseHandler = callback;
+ }
+
+ void _dataHandler() {
+ _readData();
+ if (!_decoder.isEmpty() && _clientDataHandler != null) {
+ _clientDataHandler();
+ }
+ }
+
+ void _closeHandler() {
+ _closed = true;
+ if (_clientDataHandler != null) _clientDataHandler();
+ if (_clientCloseHandler != null) _clientCloseHandler();
+ }
+
+ void _readData() {
+ List<int> data = _input.read();
+ if (data != null) {
+ _decoder.write(data);
+ }
+ }
+
+ String _readLineFromBuffer() {
+ // Both \n or \r indicates a new line. If \r is followed by \n the
+ // \n is part of the line breaking character.
+ for (int i = _bufferLineStart; i < _buffer.length; i++) {
+ String char = _buffer[i];
+ if (char == '\r') {
+ if (i == _buffer.length - 1) {
+ if (_eof) {
+ var result = _buffer.substring(_bufferLineStart, i);
+ _resetBuffer();
+ return result;
+ } else {
+ return null;
+ }
+ }
+ var result = _buffer.substring(_bufferLineStart, i);
+ _bufferLineStart = i + 1;
+ if (_buffer[_bufferLineStart] == '\n') _bufferLineStart++;
+ return result;
+ } else if (char == '\n') {
+ var result = _buffer.substring(_bufferLineStart, i);
+ _bufferLineStart = i + 1;
+ return result;
+ }
+ }
+ if (_eof) {
+ var result = _buffer;
+ _resetBuffer();
+ return result;
+ }
+ return null;
+ }
+
+ void _resetBuffer() {
+ _buffer = null;
+ _bufferLineStart = null;
+ }
+
+ // Fill decoded data into the buffer. Returns true if more data was
+ // added or end of file was reached.
+ bool _fillBuffer() {
+ if (_eof) return false;
+ if (_buffer != null && _bufferLineStart == _buffer.length) {
+ _buffer = null;
+ _bufferLineStart = null;
+ }
+ _readData();
+ var decodedString = _decoder.decoded;
+ if (decodedString == null && _closed) {
+ _eof = true;
+ return true;
+ }
+ if (_buffer == null) {
+ _buffer = decodedString;
+ if (_buffer != null) {
+ _bufferLineStart = 0;
+ return true;
+ }
+ } else if (decodedString != null) {
+ _buffer = _buffer.substring(_bufferLineStart) + decodedString;
+ _bufferLineStart = 0;
+ return true;
+ }
+ return false;
+ }
+
+ InputStream _input;
+ String _encoding;
+ _Decoder _decoder;
+ String _buffer; // String can be buffered here if readLine is used.
+ int _bufferLineStart; // Current offset into _buffer if any.
+ bool _closed = false;
+ bool _eof = false; // Has all data been read from the decoder?
+ var _clientDataHandler;
+ var _clientCloseHandler;
+}
« no previous file with comments | « runtime/bin/socket_stream.dart ('k') | tests/standalone/src/EchoServerStreamReadUntilTest.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698