| OLD | NEW |
| 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 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 | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 part of dart.convert; | 5 part of dart.convert; |
| 6 | 6 |
| 7 /** | 7 /** |
| 8 * An instance of the default implementation of the [Latin1Codec]. | 8 * An instance of the default implementation of the [Latin1Codec]. |
| 9 * | 9 * |
| 10 * This instance provides a convenient access to the most common ISO Latin 1 | 10 * This instance provides a convenient access to the most common ISO Latin 1 |
| 11 * use cases. | 11 * use cases. |
| 12 * | 12 * |
| 13 * Examples: | 13 * Examples: |
| 14 * | 14 * |
| 15 * var encoded = LATIN1.encode("blåbærgrød"); | 15 * var encoded = LATIN1.encode("blåbærgrød"); |
| 16 * var decoded = LATIN1.decode([0x62, 0x6c, 0xe5, 0x62, 0xe6, | 16 * var decoded = LATIN1.decode([0x62, 0x6c, 0xe5, 0x62, 0xe6, |
| 17 * 0x72, 0x67, 0x72, 0xf8, 0x64]); | 17 * 0x72, 0x67, 0x72, 0xf8, 0x64]); |
| 18 */ | 18 */ |
| 19 const LATIN1 = const Latin1Codec(); | 19 const LATIN1 = const Latin1Codec(); |
| 20 | 20 |
| 21 const int _LATIN1_MASK = 0xFF; |
| 22 |
| 21 /** | 23 /** |
| 22 * A [LatinCodec] encodes strings to ISO Latin-1 (aka ISO-8859-1) bytes | 24 * A [LatinCodec] encodes strings to ISO Latin-1 (aka ISO-8859-1) bytes |
| 23 * and decodes Latin-1 bytes to strings. | 25 * and decodes Latin-1 bytes to strings. |
| 24 */ | 26 */ |
| 25 class Latin1Codec extends _Encoding { | 27 class Latin1Codec extends _Encoding { |
| 26 final bool _allowInvalid; | 28 final bool _allowInvalid; |
| 27 /** | 29 /** |
| 28 * Instantiates a new [Latin1Codec]. | 30 * Instantiates a new [Latin1Codec]. |
| 29 * | 31 * |
| 30 * If [allowInvalid] is true, the [decode] method and the converter | 32 * If [allowInvalid] is true, the [decode] method and the converter |
| (...skipping 27 matching lines...) Expand all Loading... |
| 58 Converter<String, List<int>> get encoder => const Latin1Encoder(); | 60 Converter<String, List<int>> get encoder => const Latin1Encoder(); |
| 59 | 61 |
| 60 Converter<List<int>, String> get decoder => | 62 Converter<List<int>, String> get decoder => |
| 61 _allowInvalid ? const Latin1Decoder(allowInvalid: true) | 63 _allowInvalid ? const Latin1Decoder(allowInvalid: true) |
| 62 : const Latin1Decoder(allowInvalid: false); | 64 : const Latin1Decoder(allowInvalid: false); |
| 63 } | 65 } |
| 64 | 66 |
| 65 /** | 67 /** |
| 66 * This class converts strings of only ISO Latin-1 characters to bytes. | 68 * This class converts strings of only ISO Latin-1 characters to bytes. |
| 67 */ | 69 */ |
| 68 class Latin1Encoder extends Converter<String, List<int>> { | 70 class Latin1Encoder extends _UnicodeSubsetEncoder { |
| 69 const Latin1Encoder(); | 71 const Latin1Encoder() : super(_LATIN1_MASK); |
| 70 | |
| 71 /** | |
| 72 * Converts [string] to its Latin-1 bytes (a list of | |
| 73 * unsigned 8-bit integers). | |
| 74 */ | |
| 75 List<int> convert(String string) { | |
| 76 // TODO(11971): Use Uint8List when possible. | |
| 77 List result = new List<int>(string.length); | |
| 78 for (int i = 0; i < string.length; i++) { | |
| 79 var codeUnit = string.codeUnitAt(i); | |
| 80 if ((codeUnit & ~0xFF) != 0) { | |
| 81 throw new ArgumentError("String contains non-Latin-1 characters."); | |
| 82 } | |
| 83 result[i] = codeUnit; | |
| 84 } | |
| 85 return result; | |
| 86 } | |
| 87 | |
| 88 /** | |
| 89 * Starts a chunked conversion. | |
| 90 * | |
| 91 * The converter works more efficiently if the given [sink] is a | |
| 92 * [ByteConversionSink]. | |
| 93 */ | |
| 94 StringConversionSink startChunkedConversion( | |
| 95 ChunkedConversionSink<List<int>> sink) { | |
| 96 if (sink is! ByteConversionSink) { | |
| 97 sink = new ByteConversionSink.from(sink); | |
| 98 } | |
| 99 return new _Latin1EncoderSink(sink); | |
| 100 } | |
| 101 | |
| 102 // Override the base-class' bind, to provide a better type. | |
| 103 Stream<List<int>> bind(Stream<String> stream) => super.bind(stream); | |
| 104 } | |
| 105 | |
| 106 /** | |
| 107 * This class encodes chunked strings to bytes (unsigned 8-bit | |
| 108 * integers). | |
| 109 */ | |
| 110 class _Latin1EncoderSink extends StringConversionSinkBase { | |
| 111 static const _DEFAULT_BYTE_BUFFER_SIZE = 1024; | |
| 112 final ByteConversionSink _sink; | |
| 113 | |
| 114 // TODO(11971): Use Uint8List when available. | |
| 115 List<int> _buffer = new List<int>(_DEFAULT_BYTE_BUFFER_SIZE); | |
| 116 int _bufferIndex = 0; | |
| 117 | |
| 118 _Latin1EncoderSink(this._sink); | |
| 119 | |
| 120 void close() { | |
| 121 if (_bufferIndex > 0) { | |
| 122 _sink.addSlice(_buffer, 0, _bufferIndex, true); | |
| 123 } else { | |
| 124 _sink.close(); | |
| 125 } | |
| 126 } | |
| 127 | |
| 128 void addSlice(String source, int start, int end, bool isLast) { | |
| 129 if (start < 0 || start > source.length) { | |
| 130 throw new RangeError.range(start, 0, source.length); | |
| 131 } | |
| 132 if (end < start || end > source.length) { | |
| 133 throw new RangeError.range(end, start, source.length); | |
| 134 } | |
| 135 for (int i = start; i < end; i++) { | |
| 136 int codeUnit = source.codeUnitAt(i); | |
| 137 if ((codeUnit & ~0xFF) != 0) { | |
| 138 throw new ArgumentError("Source contains non-Latin-1 characters."); | |
| 139 } | |
| 140 _buffer[_bufferIndex] = codeUnit; | |
| 141 _bufferIndex++; | |
| 142 if (_bufferIndex == _buffer.length) { | |
| 143 _sink.addSlice(_buffer, 0, _bufferIndex, false); | |
| 144 _bufferIndex = 0; | |
| 145 } | |
| 146 } | |
| 147 if (isLast) close(); | |
| 148 } | |
| 149 } | 72 } |
| 150 | 73 |
| 151 /** | 74 /** |
| 152 * This class converts Latin-1 bytes (lists of unsigned 8-bit integers) | 75 * This class converts Latin-1 bytes (lists of unsigned 8-bit integers) |
| 153 * to a string. | 76 * to a string. |
| 154 */ | 77 */ |
| 155 class Latin1Decoder extends Converter<List<int>, String> { | 78 class Latin1Decoder extends _UnicodeSubsetDecoder { |
| 156 final bool _allowInvalid; | |
| 157 | |
| 158 /** | 79 /** |
| 159 * Instantiates a new [Latin1Decoder]. | 80 * Instantiates a new [Latin1Decoder]. |
| 160 * | 81 * |
| 161 * The optional [allowInvalid] argument defines how [convert] deals | 82 * The optional [allowInvalid] argument defines how [convert] deals |
| 162 * with invalid bytes. | 83 * with invalid bytes. |
| 163 * | 84 * |
| 164 * If it is `true`, [convert] replaces invalid bytes with the Unicode | 85 * If it is `true`, [convert] replaces invalid bytes with the Unicode |
| 165 * Replacement character `U+FFFD` (�). | 86 * Replacement character `U+FFFD` (�). |
| 166 * Otherwise it throws a [FormatException]. | 87 * Otherwise it throws a [FormatException]. |
| 167 */ | 88 */ |
| 168 const Latin1Decoder({ bool allowInvalid: false }) | 89 const Latin1Decoder({ bool allowInvalid: false }) |
| 169 : this._allowInvalid = allowInvalid; | 90 : super(allowInvalid, _LATIN1_MASK); |
| 170 | |
| 171 /** | |
| 172 * Converts the Latin=1 [bytes] (a list of unsigned 8-bit integers) to the | |
| 173 * corresponding string. | |
| 174 */ | |
| 175 String convert(List<int> bytes) { | |
| 176 for (int i = 0; i < bytes.length; i++) { | |
| 177 int byte = bytes[i]; | |
| 178 if ((byte & ~0xFF) != 0) { | |
| 179 if (!_allowInvalid) { | |
| 180 throw new FormatException("Non-byte in byte list"); | |
| 181 } | |
| 182 return _convertInvalid(bytes); | |
| 183 } | |
| 184 } | |
| 185 return new String.fromCharCodes(bytes); | |
| 186 } | |
| 187 | |
| 188 String _convertInvalid(List<int> bytes) { | |
| 189 StringBuffer buffer = new StringBuffer(); | |
| 190 for (int i = 0; i < bytes.length; i++) { | |
| 191 int value = bytes[i]; | |
| 192 if ((value & ~0xFF) != 0) value = 0xFFFD; | |
| 193 buffer.writeCharCode(value); | |
| 194 } | |
| 195 return buffer.toString(); | |
| 196 } | |
| 197 | 91 |
| 198 /** | 92 /** |
| 199 * Starts a chunked conversion. | 93 * Starts a chunked conversion. |
| 200 * | 94 * |
| 201 * The converter works more efficiently if the given [sink] is a | 95 * The converter works more efficiently if the given [sink] is a |
| 202 * [StringConversionSink]. | 96 * [StringConversionSink]. |
| 203 */ | 97 */ |
| 204 ByteConversionSink startChunkedConversion( | 98 ByteConversionSink startChunkedConversion( |
| 205 ChunkedConversionSink<String> sink) { | 99 ChunkedConversionSink<String> sink) { |
| 206 StringConversionSink stringSink; | 100 StringConversionSink stringSink; |
| 207 if (sink is StringConversionSink) { | 101 if (sink is StringConversionSink) { |
| 208 stringSink = sink; | 102 stringSink = sink; |
| 209 } else { | 103 } else { |
| 210 stringSink = new StringConversionSink.from(sink); | 104 stringSink = new StringConversionSink.from(sink); |
| 211 } | 105 } |
| 212 // TODO(lrn): Use stringSink.asUtf16Sink() if it becomes available. | 106 // TODO(lrn): Use stringSink.asUtf16Sink() if it becomes available. |
| 213 return new _Latin1DecoderSink(_allowInvalid, stringSink); | 107 return new _Latin1DecoderSink(_allowInvalid, stringSink); |
| 214 } | 108 } |
| 215 | |
| 216 // Override the base-class's bind, to provide a better type. | |
| 217 Stream<String> bind(Stream<List<int>> stream) => super.bind(stream); | |
| 218 } | 109 } |
| 219 | 110 |
| 220 class _Latin1DecoderSink extends ByteConversionSinkBase { | 111 class _Latin1DecoderSink extends ByteConversionSinkBase { |
| 221 final bool _allowInvalid; | 112 final bool _allowInvalid; |
| 222 StringConversionSink _sink; | 113 StringConversionSink _sink; |
| 223 _Latin1DecoderSink(this._allowInvalid, this._sink); | 114 _Latin1DecoderSink(this._allowInvalid, this._sink); |
| 224 | 115 |
| 225 void close() { | 116 void close() { |
| 226 _sink.close(); | 117 _sink.close(); |
| 227 } | 118 } |
| (...skipping 12 matching lines...) Expand all Loading... |
| 240 } | 131 } |
| 241 | 132 |
| 242 void addSlice(List<int> source, int start, int end, bool isLast) { | 133 void addSlice(List<int> source, int start, int end, bool isLast) { |
| 243 if (start < 0 || start > source.length) { | 134 if (start < 0 || start > source.length) { |
| 244 throw new RangeError.range(start, 0, source.length); | 135 throw new RangeError.range(start, 0, source.length); |
| 245 } | 136 } |
| 246 if (end < start || end > source.length) { | 137 if (end < start || end > source.length) { |
| 247 throw new RangeError.range(end, start, source.length); | 138 throw new RangeError.range(end, start, source.length); |
| 248 } | 139 } |
| 249 for (int i = start; i < end; i++) { | 140 for (int i = start; i < end; i++) { |
| 250 if ((source[i] & ~0xFF) != 0) { | 141 if ((source[i] & ~_LATIN1_MASK) != 0) { |
| 251 if (_allowInvalid) { | 142 if (_allowInvalid) { |
| 252 if (i > start) _addSliceToSink(source, start, i, false); | 143 if (i > start) _addSliceToSink(source, start, i, false); |
| 253 // Add UTF-8 encoding of U+FFFD. | 144 // Add UTF-8 encoding of U+FFFD. |
| 254 _addSliceToSink(const[0xFFFD], 0, 1, false); | 145 _addSliceToSink(const[0xFFFD], 0, 1, false); |
| 255 start = i + 1; | 146 start = i + 1; |
| 256 } else { | 147 } else { |
| 257 throw new FormatException("Source contains non-Latin-1 characters."); | 148 throw new FormatException("Source contains non-Latin-1 characters."); |
| 258 } | 149 } |
| 259 } | 150 } |
| 260 } | 151 } |
| 261 if (start < end) { | 152 if (start < end) { |
| 262 _addSliceToSink(source, start, end, isLast); | 153 _addSliceToSink(source, start, end, isLast); |
| 263 } else if (isLast) { | 154 } else if (isLast) { |
| 264 close(); | 155 close(); |
| 265 } | 156 } |
| 266 } | 157 } |
| 267 } | 158 } |
| OLD | NEW |