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

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

Issue 11337019: Use patching for dart:io. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Address comments Created 8 years, 1 month 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) 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
3 // BSD-style license that can be found in the LICENSE file.
4
5 // Interface for decoders decoding binary data into string data. The
6 // decoder keeps track of line breaks during decoding.
7 abstract class _StringDecoder {
8 // 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.
10 int write(List<int> buffer);
11
12 // Returns whether any decoded data is available.
13 bool get isEmpty;
14
15 // Returns the number of available decoded characters.
16 int available();
17
18 // Get the number of line breaks present in the current decoded
19 // data.
20 int get lineBreaks;
21
22 // Get up to [len] characters of string data decoded since the last
23 // call to [decode] or [decodeLine]. Returns null if no decoded data
24 // is available. If [len] is not specified all decoded characters
25 // are returned.
26 String decoded([int len]);
27
28 // Get the string data decoded since the last call to [decode] or
29 // [decodeLine] up to the next line break present. Returns null if
30 // no line break is present. The line break character sequence is
31 // discarded.
32 String get decodedLine;
33 }
34
35
36 class _StringDecoders {
37 static _StringDecoder decoder(Encoding encoding) {
38 if (encoding == Encoding.UTF_8) {
39 return new _UTF8Decoder();
40 } else if (encoding == Encoding.ISO_8859_1) {
41 return new _Latin1Decoder();
42 } else if (encoding == Encoding.ASCII) {
43 return new _AsciiDecoder();
44 } else {
45 if (encoding is Encoding) {
46 throw new StreamException("Unsupported encoding ${encoding.name}");
47 } else {
48 throw new StreamException("Unsupported encoding ${encoding}");
49 }
50 }
51 }
52 }
53
54
55 class DecoderException implements Exception {
56 const DecoderException([String this.message]);
57 String toString() => "DecoderException: $message";
58 final String message;
59 }
60
61
62 // Utility class for decoding UTF-8 from data delivered as a stream of
63 // bytes.
64 class _StringDecoderBase implements _StringDecoder {
65 _StringDecoderBase()
66 : _bufferList = new _BufferList(),
67 _result = new List<int>(),
68 _lineBreakEnds = new Queue<int>();
69
70 int write(List<int> buffer) {
71 _bufferList.add(buffer);
72 // Decode as many bytes into characters as possible.
73 while (_bufferList.length > 0) {
74 if (!_processNext()) {
75 break;
76 }
77 }
78 return buffer.length;
79 }
80
81 bool get isEmpty => _result.isEmpty;
82
83 int get lineBreaks => _lineBreaks;
84
85 String decoded([int len]) {
86 if (isEmpty) return null;
87
88 String result;
89 if (len !== null && len < available()) {
90 result = new String.fromCharCodes(_result.getRange(_resultOffset, len));
91 } else {
92 if (_resultOffset == 0) {
93 result = new String.fromCharCodes(_result);
94 } else {
95 result =
96 new String.fromCharCodes(
97 _result.getRange(_resultOffset,
98 _result.length - _resultOffset));
99 }
100 }
101 _resultOffset += result.length;
102 while (!_lineBreakEnds.isEmpty &&
103 _lineBreakEnds.first < _charOffset + _resultOffset) {
104 _lineBreakEnds.removeFirst();
105 _lineBreaks--;
106 }
107 if (_result.length == _resultOffset) _resetResult();
108 return result;
109 }
110
111 String get decodedLine {
112 if (_lineBreakEnds.isEmpty) return null;
113 int lineEnd = _lineBreakEnds.removeFirst();
114 int terminationSequenceLength = 1;
115 if (_result[lineEnd - _charOffset] == LF &&
116 lineEnd > _charOffset &&
117 _resultOffset < lineEnd &&
118 _result[lineEnd - _charOffset - 1] == CR) {
119 terminationSequenceLength = 2;
120 }
121 var lineLength =
122 lineEnd - _charOffset - _resultOffset - terminationSequenceLength + 1;
123 String result =
124 new String.fromCharCodes(_result.getRange(_resultOffset, lineLength));
125 _lineBreaks--;
126 _resultOffset += (lineLength + terminationSequenceLength);
127 if (_result.length == _resultOffset) _resetResult();
128 return result;
129 }
130
131 // Add another decoded character.
132 void addChar(int charCode) {
133 _result.add(charCode);
134 _charCount++;
135 // Check for line ends (\r, \n and \r\n).
136 if (charCode == LF) {
137 _recordLineBreakEnd(_charCount - 1);
138 } else if (_lastCharCode == CR) {
139 _recordLineBreakEnd(_charCount - 2);
140 }
141 _lastCharCode = charCode;
142 }
143
144 int available() => _result.length - _resultOffset;
145
146 void _recordLineBreakEnd(int charPos) {
147 _lineBreakEnds.add(charPos);
148 _lineBreaks++;
149 }
150
151 void _resetResult() {
152 _charOffset += _result.length;
153 _result = new List<int>();
154 _resultOffset = 0;
155 }
156
157 abstract bool _processNext();
158
159 _BufferList _bufferList;
160 int _resultOffset = 0;
161 List<int> _result;
162 int _lineBreaks = 0; // Number of line breaks in the current list.
163 // The positions of the line breaks are tracked in terms of absolute
164 // character positions from the begining of the decoded data.
165 Queue<int> _lineBreakEnds; // Character position of known line breaks.
166 int _charOffset = 0; // Character number of the first character in the list.
167 int _charCount = 0; // Total number of characters decoded.
168 int _lastCharCode = -1;
169
170 final int LF = 10;
171 final int CR = 13;
172 }
173
174
175 // Utility class for decoding UTF-8 from data delivered as a stream of
176 // bytes.
177 class _UTF8Decoder extends _StringDecoderBase {
178 // Process the next UTF-8 encoded character.
179 bool _processNext() {
180 // Peek the next byte to calculate the number of bytes required for
181 // the next character.
182 int value = _bufferList.peek() & 0xFF;
183 if ((value & 0x80) == 0x80) {
184 int additionalBytes;
185 if ((value & 0xe0) == 0xc0) { // 110xxxxx
186 value = value & 0x1F;
187 additionalBytes = 1;
188 } else if ((value & 0xf0) == 0xe0) { // 1110xxxx
189 value = value & 0x0F;
190 additionalBytes = 2;
191 } else { // 11110xxx
192 value = value & 0x07;
193 additionalBytes = 3;
194 }
195 // Check if there are enough bytes to decode the character. Otherwise
196 // return false.
197 if (_bufferList.length < additionalBytes + 1) {
198 return false;
199 }
200 // Remove the value peeked from the buffer list.
201 _bufferList.next();
202 for (int i = 0; i < additionalBytes; i++) {
203 int byte = _bufferList.next();
204 value = value << 6 | (byte & 0x3F);
205 }
206 } else {
207 // Remove the value peeked from the buffer list.
208 _bufferList.next();
209 }
210 addChar(value);
211 return true;
212 }
213 }
214
215
216 // Utility class for decoding ascii data delivered as a stream of
217 // bytes.
218 class _AsciiDecoder extends _StringDecoderBase {
219 // Process the next ascii encoded character.
220 bool _processNext() {
221 while (_bufferList.length > 0) {
222 int byte = _bufferList.next();
223 if (byte > 127) {
224 throw new DecoderException("Illegal ASCII character $byte");
225 }
226 addChar(byte);
227 }
228 return true;
229 }
230 }
231
232
233 // Utility class for decoding Latin-1 data delivered as a stream of
234 // bytes.
235 class _Latin1Decoder extends _StringDecoderBase {
236 // Process the next Latin-1 encoded character.
237 bool _processNext() {
238 while (_bufferList.length > 0) {
239 int byte = _bufferList.next();
240 addChar(byte);
241 }
242 return true;
243 }
244 }
245
246
247 // Interface for encoders encoding string data into binary data.
248 abstract class _StringEncoder {
249 List<int> encodeString(String string);
250 }
251
252
253 // Utility class for encoding a string into UTF-8 byte stream.
254 class _UTF8Encoder implements _StringEncoder {
255 List<int> encodeString(String string) {
256 int size = _encodingSize(string);
257 List<int> result = new Uint8List(size);
258 _encodeString(string, result);
259 return result;
260 }
261
262 static int _encodingSize(String string) => _encodeString(string, null);
263
264 static int _encodeString(String string, List<int> buffer) {
265 int pos = 0;
266 int length = string.length;
267 for (int i = 0; i < length; i++) {
268 int additionalBytes;
269 int charCode = string.charCodeAt(i);
270 if (charCode <= 0x007F) {
271 additionalBytes = 0;
272 if (buffer != null) buffer[pos] = charCode;
273 } else if (charCode <= 0x07FF) {
274 // 110xxxxx (xxxxx is top 5 bits).
275 if (buffer != null) buffer[pos] = ((charCode >> 6) & 0x1F) | 0xC0;
276 additionalBytes = 1;
277 } else if (charCode <= 0xFFFF) {
278 // 1110xxxx (xxxx is top 4 bits)
279 if (buffer != null) buffer[pos] = ((charCode >> 12) & 0x0F)| 0xE0;
280 additionalBytes = 2;
281 } else {
282 // 11110xxx (xxx is top 3 bits)
283 if (buffer != null) buffer[pos] = ((charCode >> 18) & 0x07) | 0xF0;
284 additionalBytes = 3;
285 }
286 pos++;
287 if (buffer != null) {
288 for (int i = additionalBytes; i > 0; i--) {
289 // 10xxxxxx (xxxxxx is next 6 bits from the top).
290 buffer[pos++] = ((charCode >> (6 * (i - 1))) & 0x3F) | 0x80;
291 }
292 } else {
293 pos += additionalBytes;
294 }
295 }
296 return pos;
297 }
298 }
299
300
301 // Utility class for encoding a string into a Latin1 byte stream.
302 class _Latin1Encoder implements _StringEncoder {
303 List<int> encodeString(String string) {
304 List<int> result = new Uint8List(string.length);
305 for (int i = 0; i < string.length; i++) {
306 int charCode = string.charCodeAt(i);
307 if (charCode > 255) {
308 throw new EncoderException(
309 "No ISO_8859_1 encoding for code point $charCode");
310 }
311 result[i] = charCode;
312 }
313 return result;
314 }
315 }
316
317
318 // Utility class for encoding a string into an ASCII byte stream.
319 class _AsciiEncoder implements _StringEncoder {
320 List<int> encodeString(String string) {
321 List<int> result = new Uint8List(string.length);
322 for (int i = 0; i < string.length; i++) {
323 int charCode = string.charCodeAt(i);
324 if (charCode > 127) {
325 throw new EncoderException(
326 "No ASCII encoding for code point $charCode");
327 }
328 result[i] = charCode;
329 }
330 return result;
331 }
332 }
333
334
335 class _StringEncoders {
336 static _StringEncoder encoder(Encoding encoding) {
337 if (encoding == Encoding.UTF_8) {
338 return new _UTF8Encoder();
339 } else if (encoding == Encoding.ISO_8859_1) {
340 return new _Latin1Encoder();
341 } else if (encoding == Encoding.ASCII) {
342 return new _AsciiEncoder();
343 } else {
344 throw new StreamException("Unsupported encoding ${encoding.name}");
345 }
346 }
347 }
348
349
350 class EncoderException implements Exception {
351 const EncoderException([String this.message]);
352 String toString() => "EncoderException: $message";
353 final String message;
354 }
355
356
357 class _StringInputStream implements StringInputStream {
358 _StringInputStream(InputStream this._input, Encoding this._encoding) {
359 _decoder = _StringDecoders.decoder(encoding);
360 _input.onData = _onData;
361 _input.onClosed = _onClosed;
362 }
363
364 String read([int len]) {
365 String result = _decoder.decoded(len);
366 _checkInstallDataHandler();
367 return result;
368 }
369
370 String readLine() {
371 String decodedLine = _decoder.decodedLine;
372 if (decodedLine == null) {
373 if (_inputClosed) {
374 // Last line might not have a line separator.
375 decodedLine = _decoder.decoded();
376 if (decodedLine != null &&
377 decodedLine[decodedLine.length - 1] == '\r') {
378 decodedLine = decodedLine.substring(0, decodedLine.length - 1);
379 }
380 }
381 }
382 _checkInstallDataHandler();
383 return decodedLine;
384 }
385
386 int available() => _decoder.available();
387
388 Encoding get encoding => _encoding;
389
390 bool get closed => _inputClosed && _decoder.isEmpty;
391
392 void set onData(void callback()) {
393 _clientDataHandler = callback;
394 _clientLineHandler = null;
395 _checkInstallDataHandler();
396 _checkScheduleCallback();
397 }
398
399 void set onLine(void callback()) {
400 _clientLineHandler = callback;
401 _clientDataHandler = null;
402 _checkInstallDataHandler();
403 _checkScheduleCallback();
404 }
405
406 void set onClosed(void callback()) {
407 _clientCloseHandler = callback;
408 }
409
410 void set onError(void callback(e)) {
411 _input.onError = callback;
412 }
413
414 void _onData() {
415 _readData();
416 if (!_decoder.isEmpty && _clientDataHandler !== null) {
417 _clientDataHandler();
418 }
419 if (_decoder.lineBreaks > 0 && _clientLineHandler !== null) {
420 _clientLineHandler();
421 }
422 _checkScheduleCallback();
423 _checkInstallDataHandler();
424 }
425
426 void _onClosed() {
427 _inputClosed = true;
428 if (_decoder.isEmpty && _clientCloseHandler != null) {
429 _clientCloseHandler();
430 _closed = true;
431 } else {
432 _checkScheduleCallback();
433 }
434 }
435
436 void _readData() {
437 List<int> data = _input.read();
438 if (data !== null) {
439 _decoder.write(data);
440 }
441 }
442
443 void _checkInstallDataHandler() {
444 if (_inputClosed ||
445 (_clientDataHandler === null && _clientLineHandler === null)) {
446 _input.onData = null;
447 } else if (_clientDataHandler !== null) {
448 if (_decoder.isEmpty) {
449 _input.onData = _onData;
450 } else {
451 _input.onData = null;
452 }
453 } else {
454 assert(_clientLineHandler !== null);
455 if (_decoder.lineBreaks == 0) {
456 _input.onData = _onData;
457 } else {
458 _input.onData = null;
459 }
460 }
461 }
462
463 // TODO(sgjesse): Find a better way of scheduling callbacks from
464 // the event loop.
465 void _checkScheduleCallback() {
466 void issueDataCallback(Timer timer) {
467 _scheduledDataCallback = null;
468 if (_clientDataHandler !== null) {
469 _clientDataHandler();
470 _checkScheduleCallback();
471 }
472 }
473
474 void issueLineCallback(Timer timer) {
475 _scheduledLineCallback = null;
476 if (_clientLineHandler !== null) {
477 _clientLineHandler();
478 _checkScheduleCallback();
479 }
480 }
481
482 void issueCloseCallback(Timer timer) {
483 _scheduledCloseCallback = null;
484 if (!_closed) {
485 if (_clientCloseHandler !== null) _clientCloseHandler();
486 _closed = true;
487 }
488 }
489
490 if (!_closed) {
491 // Schedule data callback if string data available.
492 if (_clientDataHandler != null &&
493 !_decoder.isEmpty &&
494 _scheduledDataCallback == null) {
495 if (_scheduledLineCallback != null) {
496 _scheduledLineCallback.cancel();
497 }
498 _scheduledDataCallback = new Timer(0, issueDataCallback);
499 }
500
501 // Schedule line callback if a line is available.
502 if (_clientLineHandler != null &&
503 (_decoder.lineBreaks > 0 || (!_decoder.isEmpty && _inputClosed)) &&
504 _scheduledLineCallback == null) {
505 if (_scheduledDataCallback != null) {
506 _scheduledDataCallback.cancel();
507 }
508 _scheduledLineCallback = new Timer(0, issueLineCallback);
509 }
510
511 // Schedule close callback if no more data and input is closed.
512 if (_decoder.isEmpty &&
513 _inputClosed &&
514 _scheduledCloseCallback == null) {
515 _scheduledCloseCallback = new Timer(0, issueCloseCallback);
516 }
517 }
518 }
519
520 InputStream _input;
521 Encoding _encoding;
522 _StringDecoder _decoder;
523 bool _inputClosed = false; // Is the underlying input stream closed?
524 bool _closed = false; // Is this stream closed.
525 bool _eof = false; // Has all data been read from the decoder?
526 Timer _scheduledDataCallback;
527 Timer _scheduledLineCallback;
528 Timer _scheduledCloseCallback;
529 Function _clientDataHandler;
530 Function _clientLineHandler;
531 Function _clientCloseHandler;
532 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698