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

Side by Side Diff: runtime/bin/string_stream.dart

Issue 9653026: Add writeString method to OutputStream (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 8 years, 9 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
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2012, 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 // Class for creating singleton encoding objects.
6 class _Encoding implements Encoding {
7 const _Encoding(String this.name);
8 final String name;
9 }
10
11
5 // Interface for decoders decoding binary data into string data. The 12 // Interface for decoders decoding binary data into string data. The
6 // decoder keeps track of line breaks during decoding. 13 // decoder keeps track of line breaks during decoding.
7 interface _StringDecoder { 14 interface _StringDecoder {
8 // Add more binary data to be decoded. The ownership of the buffer 15 // Add more binary data to be decoded. The ownership of the buffer
9 // is transfered to the decoder and the caller most not modify it any more. 16 // is transfered to the decoder and the caller most not modify it any more.
10 int write(List<int> buffer); 17 int write(List<int> buffer);
11 18
12 // Returns whether any decoded data is available. 19 // Returns whether any decoded data is available.
13 bool isEmpty(); 20 bool isEmpty();
14 21
15 // Get the number of line breaks present in the current decoded 22 // Get the number of line breaks present in the current decoded
16 // data. 23 // data.
17 int lineBreaks(); 24 int lineBreaks();
18 25
19 // Get the string data decoded since the last call to [decode] or 26 // Get the string data decoded since the last call to [decode] or
20 // [decodeLine]. Returns null if no decoded data is available. 27 // [decodeLine]. Returns null if no decoded data is available.
21 String get decoded(); 28 String get decoded();
22 29
23 // Get the string data decoded since the last call to [decode] or 30 // Get the string data decoded since the last call to [decode] or
24 // [decodeLine] up to the next line break present. Returns null if 31 // [decodeLine] up to the next line break present. Returns null if
25 // no line break is present. The line break character sequence is 32 // no line break is present. The line break character sequence is
26 // discarded. 33 // discarded.
27 String get decodedLine(); 34 String get decodedLine();
28 } 35 }
29 36
30 37
38 class _StringDecoders {
39 static _StringDecoder decoder(Encoding encoding) {
40 if (encoding == Encodings.UTF_8) {
41 return new _UTF8Decoder();
42 } else if (encoding == Encodings.ISO_8859_1) {
43 return new _Latin1Decoder();
44 } else if (encoding == Encodings.ASCII) {
45 return new _AsciiDecoder();
46 } else {
47 throw new StreamException("Unsupported encoding ${encoding.name}");
48 }
49 }
50 }
51
52
31 class DecoderException implements Exception { 53 class DecoderException implements Exception {
32 const DecoderException([String this.message]); 54 const DecoderException([String this.message]);
33 String toString() => "DecoderException: $message"; 55 String toString() => "DecoderException: $message";
34 final String message; 56 final String message;
35 } 57 }
36 58
37 59
38 // Utility class for decoding UTF-8 from data delivered as a stream of 60 // Utility class for decoding UTF-8 from data delivered as a stream of
39 // bytes. 61 // bytes.
40 class _StringDecoderBase implements _StringDecoder { 62 class _StringDecoderBase implements _StringDecoder {
(...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after
135 Queue<int> _lineBreakEnds; // Character position of known line breaks. 157 Queue<int> _lineBreakEnds; // Character position of known line breaks.
136 int _charOffset = 0; // Character number of the first character in the list. 158 int _charOffset = 0; // Character number of the first character in the list.
137 int _charCount = 0; // Total number of characters decoded. 159 int _charCount = 0; // Total number of characters decoded.
138 int _lastCharCode = -1; 160 int _lastCharCode = -1;
139 161
140 final int LF = 10; 162 final int LF = 10;
141 final int CR = 13; 163 final int CR = 13;
142 } 164 }
143 165
144 166
167 // Utility class for decoding UTF-8 from data delivered as a stream of
168 // bytes.
169 class _UTF8Decoder extends _StringDecoderBase {
170 // Process the next UTF-8 encoded character.
171 bool _processNext() {
172 // Peek the next byte to calculate the number of bytes required for
173 // the next character.
174 int value = _bufferList.peek() & 0xFF;
175 if ((value & 0x80) == 0x80) {
176 int additionalBytes;
177 if ((value & 0xe0) == 0xc0) { // 110xxxxx
178 value = value & 0x1F;
179 additionalBytes = 1;
180 } else if ((value & 0xf0) == 0xe0) { // 1110xxxx
181 value = value & 0x0F;
182 additionalBytes = 2;
183 } else { // 11110xxx
184 value = value & 0x07;
185 additionalBytes = 3;
186 }
187 // Check if there are enough bytes to decode the character. Otherwise
188 // return false.
189 if (_bufferList.length < additionalBytes + 1) {
190 return false;
191 }
192 // Remove the value peeked from the buffer list.
193 _bufferList.next();
194 for (int i = 0; i < additionalBytes; i++) {
195 int byte = _bufferList.next();
196 value = value << 6 | (byte & 0x3F);
197 }
198 } else {
199 // Remove the value peeked from the buffer list.
200 _bufferList.next();
201 }
202 addChar(value);
203 return true;
204 }
205 }
206
207
145 // Utility class for decoding ascii data delivered as a stream of 208 // Utility class for decoding ascii data delivered as a stream of
146 // bytes. 209 // bytes.
147 class _AsciiDecoder extends _StringDecoderBase { 210 class _AsciiDecoder extends _StringDecoderBase {
148 // Process the next ascii encoded character. 211 // Process the next ascii encoded character.
149 bool _processNext() { 212 bool _processNext() {
150 while (_bufferList.length > 0) { 213 while (_bufferList.length > 0) {
151 int byte = _bufferList.next(); 214 int byte = _bufferList.next();
152 if (byte > 127) { 215 if (byte > 127) {
153 throw new DecoderException("Illegal ASCII character $byte"); 216 throw new DecoderException("Illegal ASCII character $byte");
154 } 217 }
(...skipping 11 matching lines...) Expand all
166 bool _processNext() { 229 bool _processNext() {
167 while (_bufferList.length > 0) { 230 while (_bufferList.length > 0) {
168 int byte = _bufferList.next(); 231 int byte = _bufferList.next();
169 addChar(byte); 232 addChar(byte);
170 } 233 }
171 return true; 234 return true;
172 } 235 }
173 } 236 }
174 237
175 238
176 // Utility class for decoding UTF-8 from data delivered as a stream of 239 // Interface for encoders encoding string data into binary data.
177 // bytes. 240 interface _StringEncoder {
178 class _UTF8Decoder extends _StringDecoderBase { 241 List<int> encodeString(String string);
179 // Process the next UTF-8 encoded character. 242 }
180 bool _processNext() { 243
181 // Peek the next byte to calculate the number of bytes required for 244
182 // the next character. 245 // Utility class for encoding a string into UTF-8 byte stream.
183 int value = _bufferList.peek() & 0xFF; 246 class _UTF8Encoder implements _StringEncoder {
184 if ((value & 0x80) == 0x80) { 247 List<int> encodeString(String string) {
248 int size = _encodingSize(string);
249 ByteArray result = new ByteArray(size);
250 _encodeString(string, result);
251 return result;
252 }
253
254 static int _encodingSize(String string) => _encodeString(string, null);
255
256 static int _encodeString(String string, List<int> buffer) {
257 int pos = 0;
258 int length = string.length;
259 for (int i = 0; i < length; i++) {
185 int additionalBytes; 260 int additionalBytes;
186 if ((value & 0xe0) == 0xc0) { // 110xxxxx 261 int charCode = string.charCodeAt(i);
187 value = value & 0x1F; 262 if (charCode <= 0x007F) {
263 additionalBytes = 0;
264 if (buffer != null) buffer[pos] = charCode;
265 } else if (charCode <= 0x07FF) {
266 // 110xxxxx (xxxxx is top 5 bits).
267 if (buffer != null) buffer[pos] = ((charCode >> 6) & 0x1F) | 0xC0;
188 additionalBytes = 1; 268 additionalBytes = 1;
189 } else if ((value & 0xf0) == 0xe0) { // 1110xxxx 269 } else if (charCode <= 0xFFFF) {
190 value = value & 0x0F; 270 // 1110xxxx (xxxx is top 4 bits)
271 if (buffer != null) buffer[pos] = ((charCode >> 12) & 0x0F)| 0xE0;
191 additionalBytes = 2; 272 additionalBytes = 2;
192 } else { // 11110xxx 273 } else {
193 value = value & 0x07; 274 // 11110xxx (xxx is top 3 bits)
275 if (buffer != null) buffer[pos] = ((charCode >> 18) & 0x07) | 0xF0;
194 additionalBytes = 3; 276 additionalBytes = 3;
195 } 277 }
196 // Check if there are enough bytes to decode the character. Otherwise 278 pos++;
197 // return false. 279 if (buffer != null) {
198 if (_bufferList.length < additionalBytes + 1) { 280 for (int i = additionalBytes; i > 0; i--) {
199 return false; 281 // 10xxxxxx (xxxxxx is next 6 bits from the top).
282 buffer[pos++] = ((charCode >> (6 * (i - 1))) & 0x3F) | 0x80;
283 }
284 } else {
285 pos += additionalBytes;
200 } 286 }
201 // Remove the value peeked from the buffer list.
202 _bufferList.next();
203 for (int i = 0; i < additionalBytes; i++) {
204 int byte = _bufferList.next();
205 value = value << 6 | (byte & 0x3F);
206 }
207 } else {
208 // Remove the value peeked from the buffer list.
209 _bufferList.next();
210 } 287 }
211 addChar(value); 288 return pos;
212 return true;
213 } 289 }
214 } 290 }
215 291
216 292
293 // Utility class for encoding a string into a Latin1 byte stream.
294 class _Latin1Encoder implements _StringEncoder {
295 List<int> encodeString(String string) {
296 ByteArray result = new ByteArray(string.length);
297 for (int i = 0; i < string.length; i++) {
298 int charCode = string.charCodeAt(i);
299 if (charCode > 255) {
300 throw new EncoderException(
301 "No ISO_8859_1 encoding for code point $charCode");
302 }
303 result[i] = charCode;
304 }
305 return result;
306 }
307 }
308
309
310 // Utility class for encoding a string into an ASCII byte stream.
311 class _AsciiEncoder implements _StringEncoder {
312 List<int> encodeString(String string) {
313 ByteArray result = new ByteArray(string.length);
314 for (int i = 0; i < string.length; i++) {
315 int charCode = string.charCodeAt(i);
316 if (charCode > 127) {
317 throw new EncoderException(
318 "No ASCII encoding for code point $charCode");
319 }
320 result[i] = charCode;
321 }
322 return result;
323 }
324 }
325
326
327 class _StringEncoders {
328 static _StringEncoder encoder(Encoding encoding) {
329 if (encoding == Encodings.UTF_8) {
330 return new _UTF8Encoder();
331 } else if (encoding == Encodings.ISO_8859_1) {
332 return new _Latin1Encoder();
333 } else if (encoding == Encodings.ASCII) {
334 return new _AsciiEncoder();
335 } else {
336 throw new StreamException("Unsupported encoding ${encoding.name}");
337 }
338 }
339 }
340
341
342 class EncoderException implements Exception {
343 const EncoderException([String this.message]);
344 String toString() => "EncoderException: $message";
345 final String message;
346 }
347
348
217 class _StringInputStream implements StringInputStream { 349 class _StringInputStream implements StringInputStream {
218 _StringInputStream(InputStream this._input, [String encoding]) 350 _StringInputStream(InputStream this._input,
351 [Encoding encoding = Encodings.UTF_8])
219 : _encoding = encoding { 352 : _encoding = encoding {
220 if (_encoding === null) { 353 _decoder = _StringDecoders.decoder(encoding);
221 _encoding = "UTF-8";
222 }
223 if (_encoding == "UTF-8") {
224 _decoder = new _UTF8Decoder();
225 } else if (_encoding == "ISO-8859-1") {
226 _decoder = new _Latin1Decoder();
227 } else if (_encoding == "ASCII") {
228 _decoder = new _AsciiDecoder();
229 } else {
230 throw new StreamException("Unsupported encoding $_encoding");
231 }
232 _input.onData = _onData; 354 _input.onData = _onData;
233 _input.onClosed = _onClosed; 355 _input.onClosed = _onClosed;
234 } 356 }
235 357
236 String read() { 358 String read() {
237 String result = _decoder.decoded; 359 String result = _decoder.decoded;
238 _checkInstallDataHandler(); 360 _checkInstallDataHandler();
239 return result; 361 return result;
240 } 362 }
241 363
242 String readLine() { 364 String readLine() {
243 String decodedLine = _decoder.decodedLine; 365 String decodedLine = _decoder.decodedLine;
244 if (decodedLine == null) { 366 if (decodedLine == null) {
245 if (_inputClosed) { 367 if (_inputClosed) {
246 // Last line might not have a line separator. 368 // Last line might not have a line separator.
247 decodedLine = _decoder.decoded; 369 decodedLine = _decoder.decoded;
248 if (decodedLine != null && 370 if (decodedLine != null &&
249 decodedLine[decodedLine.length - 1] == '\r') { 371 decodedLine[decodedLine.length - 1] == '\r') {
250 decodedLine = decodedLine.substring(0, decodedLine.length - 1); 372 decodedLine = decodedLine.substring(0, decodedLine.length - 1);
251 } 373 }
252 } 374 }
253 } 375 }
254 _checkInstallDataHandler(); 376 _checkInstallDataHandler();
255 return decodedLine; 377 return decodedLine;
256 } 378 }
257 379
258 int available() => _decoder.available(); 380 int available() => _decoder.available();
259 381
260 String get encoding() => _encoding; 382 Encoding get encoding() => _encoding;
261 383
262 bool get closed() => _inputClosed && _decoder.isEmpty(); 384 bool get closed() => _inputClosed && _decoder.isEmpty();
263 385
264 void set onData(void callback()) { 386 void set onData(void callback()) {
265 _clientDataHandler = callback; 387 _clientDataHandler = callback;
266 _clientLineHandler = null; 388 _clientLineHandler = null;
267 _checkInstallDataHandler(); 389 _checkInstallDataHandler();
268 _checkScheduleCallback(); 390 _checkScheduleCallback();
269 } 391 }
270 392
(...skipping 112 matching lines...) Expand 10 before | Expand all | Expand 10 after
383 // Schedule close callback if no more data and input is closed. 505 // Schedule close callback if no more data and input is closed.
384 if (_decoder.isEmpty() && 506 if (_decoder.isEmpty() &&
385 _inputClosed && 507 _inputClosed &&
386 _scheduledCloseCallback == null) { 508 _scheduledCloseCallback == null) {
387 _scheduledCloseCallback = new Timer(0, issueCloseCallback); 509 _scheduledCloseCallback = new Timer(0, issueCloseCallback);
388 } 510 }
389 } 511 }
390 } 512 }
391 513
392 InputStream _input; 514 InputStream _input;
393 String _encoding; 515 Encoding _encoding;
394 _StringDecoder _decoder; 516 _StringDecoder _decoder;
395 bool _inputClosed = false; // Is the underlying input stream closed? 517 bool _inputClosed = false; // Is the underlying input stream closed?
396 bool _closed = false; // Is this stream closed. 518 bool _closed = false; // Is this stream closed.
397 bool _eof = false; // Has all data been read from the decoder? 519 bool _eof = false; // Has all data been read from the decoder?
398 Timer _scheduledDataCallback; 520 Timer _scheduledDataCallback;
399 Timer _scheduledLineCallback; 521 Timer _scheduledLineCallback;
400 Timer _scheduledCloseCallback; 522 Timer _scheduledCloseCallback;
401 Function _clientDataHandler; 523 Function _clientDataHandler;
402 Function _clientLineHandler; 524 Function _clientLineHandler;
403 Function _clientCloseHandler; 525 Function _clientCloseHandler;
404 } 526 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698