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

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

Issue 19593010: Add UTF-8 encoder. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Add empty string test. Created 7 years, 5 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
« no previous file with comments | « sdk/lib/convert/convert.dart ('k') | tests/lib/convert/utf8_encode_test.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 const UTF8 = const Utf8Codec(); 7 const UTF8 = const Utf8Codec();
8 8
9 /** 9 /**
10 * A [Utf8Codec] encodes strings to utf-8 code units (bytes) and decodes 10 * A [Utf8Codec] encodes strings to utf-8 code units (bytes) and decodes
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
51 51
52 /** 52 /**
53 * A [Utf8Encoder] converts strings to their UTF-8 code units (a list of 53 * A [Utf8Encoder] converts strings to their UTF-8 code units (a list of
54 * unsigned 8-bit integers). 54 * unsigned 8-bit integers).
55 */ 55 */
56 class Utf8Encoder extends Converter<String, List<int>> { 56 class Utf8Encoder extends Converter<String, List<int>> {
57 /** 57 /**
58 * Converts [string] to its UTF-8 code units (a list of 58 * Converts [string] to its UTF-8 code units (a list of
59 * unsigned 8-bit integers). 59 * unsigned 8-bit integers).
60 */ 60 */
61 List<int> convert(String string) => OLD_UTF_LIB.encodeUtf8(string); 61 List<int> convert(String string) {
62 // Create a new encoder with a length that is guaranteed to be big enough.
63 // A single code unit uses at most 3 bytes. Two code units at most 4.
64 _Utf8Encoder encoder = new _Utf8Encoder.withBufferSize(string.length * 3);
65 int endPosition = encoder._fillBuffer(string, 0, string.length);
66 assert(endPosition >= string.length - 1);
67 if (endPosition != string.length) {
68 int lastCodeUnit = string.codeUnitAt(string.length - 1);
69 assert(_isLeadSurrogate(lastCodeUnit));
70 // We use a non-surrogate as `nextUnit` so that _writeSurrogate just
71 // writes the lead-surrogate.
72 bool wasCombined = encoder._writeSurrogate(lastCodeUnit, 0);
73 assert(!wasCombined);
74 }
75 return encoder._buffer.sublist(0, encoder._bufferIndex);
76 }
62 } 77 }
63 78
64 /** 79 /**
65 * A [Utf8Decoder] converts UTF-8 code units (lists of unsigned 8-bit integers) 80 * This class encodes Strings to UTF-8 code units (unsigned 8 bit integers).
81 */
82 // TODO(floitsch): make this class public.
83 class _Utf8Encoder {
84 int _carry = 0;
85 int _bufferIndex = 0;
86 final Uint8List _buffer;
87
88 static const _DEFAULT_BYTE_BUFFER_SIZE = 1024;
89
90 _Utf8Encoder() : this.withBufferSize(_DEFAULT_BYTE_BUFFER_SIZE);
91
92 _Utf8Encoder.withBufferSize(int bufferSize)
93 : _buffer = new Uint8List(bufferSize);
94
95 /**
96 * Tries to combine the given [leadingSurrogate] with the [nextCodeUnit] and
97 * writes it to [_buffer].
98 *
99 * Returns true if the [nextCodeUnit] was combined with the
100 * [leadingSurrogate]. If it wasn't then nextCodeUnit has not been written
101 * yet.
102 */
103 bool _writeSurrogate(int leadingSurrogate, int nextCodeUnit) {
104 int byteIndex = _bufferIndex;
Søren Gjesse 2013/07/23 14:22:47 Why this local variable?
floitsch 2013/07/23 14:41:07 Initially I sent the byteIndex as argument. But it
105 if (_isTailSurrogate(nextCodeUnit)) {
106 int rune = _combineSurrogatePair(leadingSurrogate, nextCodeUnit);
107 // If the rune is encoded with 2 code-units then it must be encoded
108 // with 4 bytes in UTF-8.
109 assert(rune > _THREE_BYTE_LIMIT);
110 assert(rune <= _FOUR_BYTE_LIMIT);
111 _buffer[byteIndex++] = 0xF0 | (rune >> 18);
112 _buffer[byteIndex++] = 0x80 | ((rune >> 12) & 0x3f);
113 _buffer[byteIndex++] = 0x80 | ((rune >> 6) & 0x3f);
114 _buffer[byteIndex++] = 0x80 | (rune & 0x3f);
115 _bufferIndex = byteIndex;
116 return true;
117 } else {
118 // TODO(floitsch): allow to throw on malformed strings.
119 // Encode the half-surrogate directly into UTF-8. This yields
120 // invalid UTF-8, but we started out with invalid UTF-16.
121
122 // Surrogates are always encoded in 3 bytes in UTF-8.
123 _buffer[byteIndex++] = 0xE0 | (leadingSurrogate >> 12);
124 _buffer[byteIndex++] = 0x80 | ((leadingSurrogate >> 6) & 0x3f);
125 _buffer[byteIndex++] = 0x80 | (leadingSurrogate & 0x3f);
126 _bufferIndex = byteIndex;
127 return false;
128 }
129 }
130
131 /**
132 * Fills the [_buffer] with as many characters as possible.
133 *
134 * Does not encode any trailing lead-surrogate. This must be done by the
135 * caller.
136 *
137 * Returns the position in the string. The returned index points to the
138 * first code unit that hasn't been encoded.
139 */
140 int _fillBuffer(String str, int start, int end) {
141 if (start != end && _isLeadSurrogate(str.codeUnitAt(end - 1))) {
142 // Don't handle a trailing lead-surrogate in this loop. The caller has
143 // to deal with those.
144 end--;
145 }
146 int stringIndex;
147 for (stringIndex = start; stringIndex < end; stringIndex++) {
148 int codeUnit = str.codeUnitAt(stringIndex);
149 // ASCII has the same representation in UTF-8 and UTF-16.
150 if (codeUnit < _ONE_BYTE_LIMIT) {
151 if (_bufferIndex >= _buffer.length) break;
152 _buffer[_bufferIndex++] = codeUnit;
153 } else if (_isLeadSurrogate(codeUnit)) {
154 if (_bufferIndex + 3 >= _buffer.length) break;
155 // Note that it is safe to read the next code unit. We decremented
156 // [end] above when the last valid code unit was a leading surrogate.
157 int nextCodeUnit = str.codeUnitAt(stringIndex + 1);
158 bool wasCombined = _writeSurrogate(codeUnit, nextCodeUnit);
159 if (wasCombined) stringIndex++;
160 } else {
161 int rune = codeUnit;
162 if (rune <= _TWO_BYTE_LIMIT) {
163 if (_bufferIndex + 1 >= _buffer.length) break;
164 _buffer[_bufferIndex++] = 0xC0 | (rune >> 6);
165 _buffer[_bufferIndex++] = 0x80 | (rune & 0x3f);
166 } else {
167 assert(rune <= _THREE_BYTE_LIMIT);
168 if (_bufferIndex + 2 >= _buffer.length) break;
169 _buffer[_bufferIndex++] = 0xE0 | (rune >> 12);
170 _buffer[_bufferIndex++] = 0x80 | ((rune >> 6) & 0x3f);
171 _buffer[_bufferIndex++] = 0x80 | (rune & 0x3f);
172 }
173 }
174 }
175 return stringIndex;
176 }
177 }
178
179 /**
180 * This class converts UTF-8 code units (lists of unsigned 8-bit integers)
66 * to a string. 181 * to a string.
67 */ 182 */
68 class Utf8Decoder extends Converter<List<int>, String> { 183 class Utf8Decoder extends Converter<List<int>, String> {
69 final bool _allowMalformed; 184 final bool _allowMalformed;
70 185
71 /** 186 /**
72 * Instantiates a new [Utf8Decoder]. 187 * Instantiates a new [Utf8Decoder].
73 * 188 *
74 * The optional [allowMalformed] argument defines how [convert] deals 189 * The optional [allowMalformed] argument defines how [convert] deals
75 * with invalid or unterminated character sequences. 190 * with invalid or unterminated character sequences.
(...skipping 167 matching lines...) Expand 10 before | Expand all | Expand 10 after
243 } 358 }
244 break loop; 359 break loop;
245 } 360 }
246 if (expectedUnits > 0) { 361 if (expectedUnits > 0) {
247 _value = value; 362 _value = value;
248 _expectedUnits = expectedUnits; 363 _expectedUnits = expectedUnits;
249 _extraUnits = extraUnits; 364 _extraUnits = extraUnits;
250 } 365 }
251 } 366 }
252 } 367 }
OLDNEW
« no previous file with comments | « sdk/lib/convert/convert.dart ('k') | tests/lib/convert/utf8_encode_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698