| Index: runtime/bin/string_stream.dart
|
| diff --git a/runtime/bin/string_stream.dart b/runtime/bin/string_stream.dart
|
| index bedbc55a2fca1b44890fd88ed4224e4f676422ba..536eda1b56551c943506b744e69a2c396c91f803 100644
|
| --- a/runtime/bin/string_stream.dart
|
| +++ b/runtime/bin/string_stream.dart
|
| @@ -28,6 +28,21 @@ interface _StringDecoder {
|
| }
|
|
|
|
|
| +class _StringDecoders {
|
| + static _StringDecoder decoder(Encoding encoding) {
|
| + if (encoding == Encoding.UTF_8) {
|
| + return new _UTF8Decoder();
|
| + } else if (encoding == Encoding.ISO_8859_1) {
|
| + return new _Latin1Decoder();
|
| + } else if (encoding == Encoding.ASCII) {
|
| + return new _AsciiDecoder();
|
| + } else {
|
| + throw new StreamException("Unsupported encoding ${encoding.name}");
|
| + }
|
| + }
|
| +}
|
| +
|
| +
|
| class DecoderException implements Exception {
|
| const DecoderException([String this.message]);
|
| String toString() => "DecoderException: $message";
|
| @@ -142,6 +157,47 @@ class _StringDecoderBase implements _StringDecoder {
|
| }
|
|
|
|
|
| +// 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();
|
| + }
|
| + addChar(value);
|
| + return true;
|
| + }
|
| +}
|
| +
|
| +
|
| // Utility class for decoding ascii data delivered as a stream of
|
| // bytes.
|
| class _AsciiDecoder extends _StringDecoderBase {
|
| @@ -173,62 +229,121 @@ class _Latin1Decoder extends _StringDecoderBase {
|
| }
|
|
|
|
|
| -// 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) {
|
| +// Interface for encoders encoding string data into binary data.
|
| +interface _StringEncoder {
|
| + List<int> encodeString(String string);
|
| +}
|
| +
|
| +
|
| +// Utility class for encoding a string into UTF-8 byte stream.
|
| +class _UTF8Encoder implements _StringEncoder {
|
| + List<int> encodeString(String string) {
|
| + int size = _encodingSize(string);
|
| + ByteArray result = new ByteArray(size);
|
| + _encodeString(string, result);
|
| + return result;
|
| + }
|
| +
|
| + static int _encodingSize(String string) => _encodeString(string, null);
|
| +
|
| + static int _encodeString(String string, List<int> buffer) {
|
| + int pos = 0;
|
| + int length = string.length;
|
| + for (int i = 0; i < length; i++) {
|
| int additionalBytes;
|
| - if ((value & 0xe0) == 0xc0) { // 110xxxxx
|
| - value = value & 0x1F;
|
| + int charCode = string.charCodeAt(i);
|
| + if (charCode <= 0x007F) {
|
| + additionalBytes = 0;
|
| + if (buffer != null) buffer[pos] = charCode;
|
| + } else if (charCode <= 0x07FF) {
|
| + // 110xxxxx (xxxxx is top 5 bits).
|
| + if (buffer != null) buffer[pos] = ((charCode >> 6) & 0x1F) | 0xC0;
|
| additionalBytes = 1;
|
| - } else if ((value & 0xf0) == 0xe0) { // 1110xxxx
|
| - value = value & 0x0F;
|
| + } else if (charCode <= 0xFFFF) {
|
| + // 1110xxxx (xxxx is top 4 bits)
|
| + if (buffer != null) buffer[pos] = ((charCode >> 12) & 0x0F)| 0xE0;
|
| additionalBytes = 2;
|
| - } else { // 11110xxx
|
| - value = value & 0x07;
|
| + } else {
|
| + // 11110xxx (xxx is top 3 bits)
|
| + if (buffer != null) buffer[pos] = ((charCode >> 18) & 0x07) | 0xF0;
|
| additionalBytes = 3;
|
| }
|
| - // Check if there are enough bytes to decode the character. Otherwise
|
| - // return false.
|
| - if (_bufferList.length < additionalBytes + 1) {
|
| - return false;
|
| + pos++;
|
| + if (buffer != null) {
|
| + for (int i = additionalBytes; i > 0; i--) {
|
| + // 10xxxxxx (xxxxxx is next 6 bits from the top).
|
| + buffer[pos++] = ((charCode >> (6 * (i - 1))) & 0x3F) | 0x80;
|
| + }
|
| + } else {
|
| + pos += additionalBytes;
|
| }
|
| - // 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);
|
| + }
|
| + return pos;
|
| + }
|
| +}
|
| +
|
| +
|
| +// Utility class for encoding a string into a Latin1 byte stream.
|
| +class _Latin1Encoder implements _StringEncoder {
|
| + List<int> encodeString(String string) {
|
| + ByteArray result = new ByteArray(string.length);
|
| + for (int i = 0; i < string.length; i++) {
|
| + int charCode = string.charCodeAt(i);
|
| + if (charCode > 255) {
|
| + throw new EncoderException(
|
| + "No ISO_8859_1 encoding for code point $charCode");
|
| }
|
| - } else {
|
| - // Remove the value peeked from the buffer list.
|
| - _bufferList.next();
|
| + result[i] = charCode;
|
| }
|
| - addChar(value);
|
| - return true;
|
| + return result;
|
| }
|
| }
|
|
|
|
|
| -class _StringInputStream implements StringInputStream {
|
| - _StringInputStream(InputStream this._input, [String encoding])
|
| - : _encoding = encoding {
|
| - if (_encoding === null) {
|
| - _encoding = "UTF-8";
|
| +// Utility class for encoding a string into an ASCII byte stream.
|
| +class _AsciiEncoder implements _StringEncoder {
|
| + List<int> encodeString(String string) {
|
| + ByteArray result = new ByteArray(string.length);
|
| + for (int i = 0; i < string.length; i++) {
|
| + int charCode = string.charCodeAt(i);
|
| + if (charCode > 127) {
|
| + throw new EncoderException(
|
| + "No ASCII encoding for code point $charCode");
|
| + }
|
| + result[i] = charCode;
|
| }
|
| - if (_encoding == "UTF-8") {
|
| - _decoder = new _UTF8Decoder();
|
| - } else if (_encoding == "ISO-8859-1") {
|
| - _decoder = new _Latin1Decoder();
|
| - } else if (_encoding == "ASCII") {
|
| - _decoder = new _AsciiDecoder();
|
| + return result;
|
| + }
|
| +}
|
| +
|
| +
|
| +class _StringEncoders {
|
| + static _StringEncoder encoder(Encoding encoding) {
|
| + if (encoding == Encoding.UTF_8) {
|
| + return new _UTF8Encoder();
|
| + } else if (encoding == Encoding.ISO_8859_1) {
|
| + return new _Latin1Encoder();
|
| + } else if (encoding == Encoding.ASCII) {
|
| + return new _AsciiEncoder();
|
| } else {
|
| - throw new StreamException("Unsupported encoding $_encoding");
|
| + throw new StreamException("Unsupported encoding ${encoding.name}");
|
| }
|
| + }
|
| +}
|
| +
|
| +
|
| +class EncoderException implements Exception {
|
| + const EncoderException([String this.message]);
|
| + String toString() => "EncoderException: $message";
|
| + final String message;
|
| +}
|
| +
|
| +
|
| +class _StringInputStream implements StringInputStream {
|
| + _StringInputStream(InputStream this._input,
|
| + [Encoding encoding = Encoding.UTF_8])
|
| + : _encoding = encoding {
|
| + _decoder = _StringDecoders.decoder(encoding);
|
| _input.onData = _onData;
|
| _input.onClosed = _onClosed;
|
| }
|
| @@ -257,7 +372,7 @@ class _StringInputStream implements StringInputStream {
|
|
|
| int available() => _decoder.available();
|
|
|
| - String get encoding() => _encoding;
|
| + Encoding get encoding() => _encoding;
|
|
|
| bool get closed() => _inputClosed && _decoder.isEmpty();
|
|
|
| @@ -390,7 +505,7 @@ class _StringInputStream implements StringInputStream {
|
| }
|
|
|
| InputStream _input;
|
| - String _encoding;
|
| + Encoding _encoding;
|
| _StringDecoder _decoder;
|
| bool _inputClosed = false; // Is the underlying input stream closed?
|
| bool _closed = false; // Is this stream closed.
|
|
|