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

Side by Side Diff: sdk/lib/convert/latin1.dart

Issue 23224014: Add Latin-1 converters to convert library. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 7 years, 4 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 unified diff | Download patch | Annotate | Revision Log
OLDNEW
(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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698