Chromium Code Reviews| 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.convert; | |
| 6 | |
| 7 /** | |
| 8 * An instance of the default implementation of the [Latin1Codec]. | |
| 9 * | |
| 10 * This instance provides a convenient access to the most common ISO Latin 1 | |
| 11 * use cases. | |
| 12 * | |
| 13 * Examples: | |
| 14 * | |
| 15 * var encoded = LATIN1.encode("blåbærgrød"); | |
| 16 * var decoded = LATIN1.decode([0x62, 0x6c, 0xe5, 0x62, 0xe6, | |
| 17 * 0x72, 0x67, 0x72, 0xf8, 0x64]); | |
| 18 */ | |
| 19 const LATIN1 = const Latin1Codec(); | |
| 20 | |
| 21 /** | |
| 22 * A [LatinCodec] encodes strings to ISO Latin-1 (aka ISO-8859-1) bytes | |
| 23 * and decodes Latin-1 bytes to strings. | |
| 24 */ | |
| 25 class Latin1Codec extends _Encoding { | |
| 26 final bool _allowInvalid; | |
| 27 /** | |
| 28 * Instantiates a new [Latin1Codec]. | |
| 29 * | |
| 30 * If [allowInvalid] is true, the [decode] method and the converter | |
| 31 * returned by [decoder] will default ti allowing invalid values. Invalid | |
|
floitsch
2013/08/21 14:07:33
to
Lasse Reichstein Nielsen
2013/08/22 08:28:37
Done.
| |
| 32 * values are decoded into the Unicode Replacement character (U+FFFD). | |
| 33 * Calls to the [decode] method can override this default. | |
| 34 * | |
| 35 * Encoders will not accept invalid (non Latin-1) characters. | |
| 36 */ | |
| 37 const Latin1Codec({bool allowInvalid: false}) : _allowInvalid = allowInvalid; | |
| 38 | |
| 39 /** | |
| 40 * Decodes the Latin-1 [bytes] (a list of unsigned 8-bit integers) to the | |
| 41 * corresponding string. | |
| 42 * | |
| 43 * If [bytes] contains values that are not in the range 0 .. 255, the decoder | |
| 44 * will eventually thrown a [FormatException]. | |
|
floitsch
2013/08/21 14:07:33
throw
Lasse Reichstein Nielsen
2013/08/22 08:28:37
Done.
| |
| 45 * | |
| 46 * If [allowInvalid] is not provided, it defaults to the value used to create | |
| 47 * this [Latin1Codec]. | |
| 48 */ | |
| 49 String decode(List<int> bytes, { bool allowInvalid }) { | |
| 50 if ((allowInvalid == null) ? _allowInvalid : allowInvalid) { | |
|
floitsch
2013/08/21 14:07:33
split into two lines:
if (allowInvalid == null) al
Lasse Reichstein Nielsen
2013/08/22 08:28:37
Done.
| |
| 51 return const Latin1Decoder(allowInvalid: true).convert(bytes); | |
| 52 } else { | |
| 53 return const Latin1Decoder(allowInvalid: false).convert(bytes); | |
| 54 } | |
| 55 } | |
| 56 | |
| 57 Converter<String, List<int>> get encoder => const Latin1Encoder(); | |
| 58 | |
| 59 Converter<List<int>, String> get decoder => | |
| 60 _allowInvalid ? const Latin1Decoder(allowInvalid: true) | |
| 61 : const Latin1Decoder(allowInvalid: false); | |
| 62 } | |
| 63 | |
| 64 /** | |
| 65 * This class converts strings of only ISO Latin-1 characters to bytes. | |
| 66 */ | |
| 67 class Latin1Encoder extends Converter<String, List<int>> { | |
| 68 const Latin1Encoder(); | |
| 69 | |
| 70 /** | |
| 71 * Converts [string] to its Latin-1 bytes (a list of | |
| 72 * unsigned 8-bit integers). | |
| 73 */ | |
| 74 List<int> convert(String string) { | |
| 75 for (int i = 0; i < string.length; i++) { | |
| 76 var codeUnit = string.codeUnitAt(i); | |
| 77 if ((codeUnit & ~0xFF) != 0) { | |
| 78 throw new ArgumentError("String contains non-Latin-1 characters."); | |
| 79 } | |
| 80 } | |
| 81 // TODO: Use Uint8List when possible. | |
|
floitsch
2013/08/21 14:07:33
There should be a bug-number.
Lasse Reichstein Nielsen
2013/08/22 08:28:37
Done.
| |
| 82 return new List<int>(string.length)..setAll(0, string.codeUnits); | |
|
floitsch
2013/08/21 14:07:33
You think it's faster this way?
I would allocate t
Lasse Reichstein Nielsen
2013/08/22 08:28:37
Good point, optimize for the valid case.
Done.
| |
| 83 } | |
| 84 | |
| 85 /** | |
| 86 * Starts a chunked conversion. | |
| 87 * | |
| 88 * The converter works more efficiently if the given [sink] is a | |
| 89 * [ByteConversionSink]. | |
| 90 */ | |
| 91 StringConversionSink startChunkedConversion( | |
| 92 ChunkedConversionSink<List<int>> sink) { | |
| 93 if (sink is! ByteConversionSink) { | |
| 94 sink = new ByteConversionSink.from(sink); | |
| 95 } | |
| 96 return new _Latin1EncoderSink(sink); | |
| 97 } | |
| 98 | |
| 99 // Override the base-class' bind, to provide a better type. | |
| 100 Stream<List<int>> bind(Stream<String> stream) => super.bind(stream); | |
| 101 } | |
| 102 | |
| 103 /** | |
| 104 * This class encodes chunked strings to bytes (unsigned 8-bit | |
| 105 * integers). | |
| 106 */ | |
| 107 class _Latin1EncoderSink extends StringConversionSinkBase { | |
| 108 static const _DEFAULT_BYTE_BUFFER_SIZE = 1024; | |
| 109 final ByteConversionSink _sink; | |
| 110 | |
| 111 // TODO(lrn): Use Uint8List when available. | |
|
floitsch
2013/08/21 14:07:33
There is probably a bug-number.
Lasse Reichstein Nielsen
2013/08/22 08:28:37
Done.
| |
| 112 List<int> _buffer = new List<int>(_DEFAULT_BYTE_BUFFER_SIZE); | |
| 113 int _bufferIndex = 0; | |
| 114 | |
| 115 _Latin1EncoderSink(this._sink); | |
| 116 | |
| 117 void close() { | |
| 118 if (_bufferIndex > 0) { | |
| 119 _sink.addSlice(_buffer, 0, _bufferIndex, true); | |
| 120 } else { | |
| 121 _sink.close(); | |
| 122 } | |
| 123 } | |
| 124 | |
| 125 void addSlice(String source, int start, int end, bool isLast) { | |
| 126 if (start < 0 || start > source.length) { | |
| 127 throw new RangeError.range(start, 0, source.length); | |
| 128 } | |
| 129 if (end < start || end > source.length) { | |
| 130 throw new RangeError.range(end, start, source.length); | |
| 131 } | |
| 132 for (int i = start; i < end; i++) { | |
| 133 int codeUnit = source.codeUnitAt(i); | |
| 134 if ((codeUnit & ~0xFF) != 0) { | |
| 135 throw new ArgumentError("Source contains non-Latin-1 characters."); | |
| 136 } | |
| 137 _buffer[_bufferIndex] = codeUnit; | |
| 138 _bufferIndex++; | |
| 139 if (_bufferIndex == _buffer.length) { | |
| 140 _sink.addSlice(_buffer, 0, _bufferIndex, false); | |
| 141 _bufferIndex = 0; | |
| 142 } | |
| 143 } | |
| 144 if (isLast) close(); | |
| 145 } | |
| 146 } | |
| 147 | |
| 148 /** | |
| 149 * This class converts Latin-1 bytes (lists of unsigned 8-bit integers) | |
| 150 * to a string. | |
| 151 */ | |
| 152 class Latin1Decoder extends Converter<List<int>, String> { | |
| 153 final bool _allowInvalid; | |
| 154 | |
| 155 /** | |
| 156 * Instantiates a new [Latin1Decoder]. | |
| 157 * | |
| 158 * The optional [allowInvalid] argument defines how [convert] deals | |
| 159 * with invalid bytes. | |
| 160 * | |
| 161 * If it is `true`, [convert] replaces invalid bytes with the Unicode | |
| 162 * Replacement character `U+FFFD` (�). | |
| 163 * Otherwise it throws a [FormatException]. | |
| 164 */ | |
| 165 const Latin1Decoder({ bool allowInvalid: false }) | |
| 166 : this._allowInvalid = allowInvalid; | |
| 167 | |
| 168 /** | |
| 169 * Converts the Latin=1 [bytes] (a list of unsigned 8-bit integers) to the | |
| 170 * corresponding string. | |
| 171 */ | |
| 172 String convert(List<int> bytes) { | |
| 173 for (int i = 0; i < bytes.length; i++) { | |
| 174 int byte = bytes[i]; | |
| 175 if ((byte & ~0xFF) != 0) { | |
| 176 if (!_allowInvalid) { | |
| 177 throw new FormatException("Non-byte in byte list"); | |
| 178 } | |
| 179 return _convertInvalid(bytes); | |
| 180 } | |
| 181 } | |
| 182 return new String.fromCharCodes(bytes); | |
| 183 } | |
| 184 | |
| 185 String _convertInvalid(List<int> bytes) { | |
| 186 StringBuffer buffer = new StringBuffer(); | |
| 187 for (int i = 0; i < bytes.length; i++) { | |
| 188 int value = bytes[i]; | |
| 189 if ((value & ~0xFF) != 0) value = 0xFFFD; | |
| 190 buffer.writeCharCode(value); | |
| 191 } | |
| 192 return buffer.toString(); | |
| 193 } | |
| 194 | |
| 195 /** | |
| 196 * Starts a chunked conversion. | |
| 197 * | |
| 198 * The converter works more efficiently if the given [sink] is a | |
| 199 * [StringConversionSink]. | |
| 200 */ | |
| 201 ByteConversionSink startChunkedConversion( | |
| 202 ChunkedConversionSink<String> sink) { | |
| 203 StringConversionSink stringSink; | |
| 204 if (sink is StringConversionSink) { | |
| 205 stringSink = sink; | |
| 206 } else { | |
| 207 stringSink = new StringConversionSink.from(sink); | |
| 208 } | |
| 209 // TODO(lrn): Use stringSink.asUtf16Sink() if it becomes available. | |
| 210 return new _Latin1DecoderSink(_allowInvalid, stringSink); | |
| 211 } | |
| 212 | |
| 213 // Override the base-class's bind, to provide a better type. | |
| 214 Stream<String> bind(Stream<List<int>> stream) => super.bind(stream); | |
| 215 } | |
| 216 | |
| 217 class _Latin1DecoderSink implements ByteConversionSink { | |
|
floitsch
2013/08/21 14:07:33
extends ByteConversionSinkBase
We want to be able
Lasse Reichstein Nielsen
2013/08/22 08:28:37
Done.
| |
| 218 final bool _allowInvalid; | |
| 219 StringConversionSink _sink; | |
| 220 _Latin1DecoderSink(this._allowInvalid, this._sink); | |
| 221 | |
| 222 void close() { | |
| 223 _sink.close(); | |
| 224 } | |
| 225 | |
| 226 void add(List<int> source) { | |
| 227 addSlice(source, 0, source.length, false); | |
| 228 } | |
| 229 | |
| 230 void _addSliceToSink(List<int> source, int start, int end, bool isLast) { | |
| 231 // If _sink was a UTF-16 conversion sink, just add the slice directly with | |
| 232 // _sink.addSlice(source, start, end, isLast). | |
| 233 _sink.add(new String.fromCharCodes(source.getRange(start, end))); | |
|
floitsch
2013/08/21 14:07:33
I would avoid the getRange if start and end are 0
Lasse Reichstein Nielsen
2013/08/22 08:28:37
Yes, this was kept deliberately simple because it'
| |
| 234 if (isLast) close(); | |
| 235 } | |
| 236 | |
| 237 void addSlice(List<int> source, int start, int end, bool isLast) { | |
| 238 if (start < 0 || start > source.length) { | |
| 239 throw new RangeError.range(start, 0, source.length); | |
| 240 } | |
| 241 if (end < start || end > source.length) { | |
| 242 throw new RangeError.range(end, start, source.length); | |
| 243 } | |
| 244 for (int i = start; i < end; i++) { | |
| 245 if ((source[i] & ~0xFF) != 0) { | |
| 246 if (_allowInvalid) { | |
| 247 if (i > start) _addSliceToSink(source, start, i); | |
| 248 // Add UTF-8 encoding of U+FFFD. | |
| 249 _addSliceToSink(const[0xFFFD], 0, 1, false); | |
| 250 start = i + 1; | |
| 251 } else { | |
| 252 throw new FormatException("Source contains non-Latin-1 characters."); | |
| 253 } | |
| 254 } | |
| 255 } | |
| 256 if (start < end) { | |
| 257 _addSliceToSink(source, start, end, isLast); | |
| 258 } else if (isLast) { | |
| 259 close(); | |
| 260 } | |
| 261 } | |
| 262 } | |
| OLD | NEW |