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

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

Issue 8318009: Update the streams interfaces (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Addressed review comments from ager@ Created 9 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
« no previous file with comments | « runtime/bin/socket_stream.dart ('k') | tests/standalone/src/EchoServerStreamReadUntilTest.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2011, 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 // Utility class which can deliver bytes one by one from a number of
6 // buffers added.
7 class _BufferList {
8 _BufferList() : _index = 0, _length = 0, _buffers = new Queue();
9
10 void add(List<int> buffer) {
11 _buffers.addLast(buffer);
12 _length += buffer.length;
13 }
14
15 int peek() {
16 return _buffers.first()[_index];
17 }
18
19 int next() {
20 int value = _buffers.first()[_index++];
21 _length--;
22 if (_index == _buffers.first().length) {
23 _buffers.removeFirst();
24 _index = 0;
25 }
26 return value;
27 }
28
29 int get length() => _length;
30
31 int _length;
32 Queue<List<int>> _buffers;
33 int _index;
34 }
35
36
37 // Interface for decoders decoding binary data into objects of type T.
38 interface _Decoder<T> {
39 // Add more binary data to be decoded. The ownership of the buffer
40 // is transfered to the decoder and the caller most not modify it any more.
41 int write(List<int> buffer);
42
43 // Returns whether any decoded data is available.
44 bool isEmpty();
45
46 // Get the data decoded since the last call to decode.
47 T get decoded();
48 }
49
50
51 class DecoderException implements Exception {
52 const DecoderException([String this.message]);
53 String toString() => "DecoderException: $message";
54 final String message;
55 }
56
57
58 // Utility class for decoding UTF-8 from data delivered as a stream of
59 // bytes.
60 class _StringDecoderBase implements _Decoder<String> {
61 _StringDecoderBase()
62 : _bufferList = new _BufferList(),
63 _result = new StringBuffer();
64
65 // Add UTF-8 encoded data.
66 int write(List<int> buffer) {
67 _bufferList.add(buffer);
68 // Decode as many bytes into characters as possible.
69 while (_bufferList.length > 0) {
70 if (!_processNext()) {
71 break;
72 }
73 }
74 return buffer.length;
75 }
76
77 // Check if any characters have been decoded since the last call to decode.
78 bool isEmpty() {
79 return _result.isEmpty();
80 }
81
82 // Return the string decoded since the last call to decode.
83 String get decoded() {
84 if (isEmpty()) {
85 return null;
86 } else {
87 String result = _result.toString();
88 _result = new StringBuffer();
89 return result;
90 }
91 }
92
93 abstract bool _processNext();
94
95 _BufferList _bufferList;
96 StringBuffer _result;
97 }
98
99
100 // Utility class for decoding ascii data delivered as a stream of
101 // bytes.
102 class _AsciiDecoder extends _StringDecoderBase {
103 // Process the next ascii encoded character.
104 bool _processNext() {
105 while (_bufferList.length > 0) {
106 int byte = _bufferList.next();
107 if (byte > 127) {
108 throw new DecoderException("Illegal ASCII character $byte");
109 }
110 _result.addCharCode(byte);
111 }
112 return true;
113 }
114 }
115
116
117 // Utility class for decoding Latin-1 data delivered as a stream of
118 // bytes.
119 class _Latin1Decoder extends _StringDecoderBase {
120 // Process the next Latin-1 encoded character.
121 bool _processNext() {
122 while (_bufferList.length > 0) {
123 int byte = _bufferList.next();
124 _result.addCharCode(byte);
125 }
126 return true;
127 }
128 }
129
130
131 // Utility class for decoding UTF-8 from data delivered as a stream of
132 // bytes.
133 class _UTF8Decoder extends _StringDecoderBase {
134 // Process the next UTF-8 encoded character.
135 bool _processNext() {
136 // Peek the next byte to calculate the number of bytes required for
137 // the next character.
138 int value = _bufferList.peek() & 0xFF;
139 if ((value & 0x80) == 0x80) {
140 int additionalBytes;
141 if ((value & 0xe0) == 0xc0) { // 110xxxxx
142 value = value & 0x1F;
143 additionalBytes = 1;
144 } else if ((value & 0xf0) == 0xe0) { // 1110xxxx
145 value = value & 0x0F;
146 additionalBytes = 2;
147 } else { // 11110xxx
148 value = value & 0x07;
149 additionalBytes = 3;
150 }
151 // Check if there are enough bytes to decode the character. Otherwise
152 // return false.
153 if (_bufferList.length < additionalBytes + 1) {
154 return false;
155 }
156 // Remove the value peeked from the buffer list.
157 _bufferList.next();
158 for (int i = 0; i < additionalBytes; i++) {
159 int byte = _bufferList.next();
160 value = value << 6 | (byte & 0x3F);
161 }
162 } else {
163 // Remove the value peeked from the buffer list.
164 _bufferList.next();
165 }
166 _result.addCharCode(value);
167 return true;
168 }
169 }
170
171
172 class _StringInputStream implements StringInputStream {
173 _StringInputStream(InputStream this._input, [String this._encoding]) {
174 if (_encoding == null) {
175 _encoding = "UTF-8";
176 }
177 if (_encoding == "UTF-8") {
178 _decoder = new _UTF8Decoder();
179 } else if (_encoding == "ISO-8859-1") {
180 _decoder = new _Latin1Decoder();
181 } else if (_encoding == "ASCII") {
182 _decoder = new _AsciiDecoder();
183 } else {
184 throw new StreamException("Unsupported encoding $_encoding");
185 }
186 _input.dataHandler = _dataHandler;
187 _input.closeHandler = _closeHandler;
188 }
189
190 String read() {
191 // If there is buffered data return that first.
192 var decodedString = _decoder.decoded;
193 if (_buffer != null) {
194 var result = _buffer;
195 _resetBuffer();
196 if (decodedString != null) result += decodedString;
197 return result;
198 } else {
199 if (decodedString != null) {
200 return decodedString;
201 } else {
202 _readData();
203 return _decoder.decoded;
204 }
205 }
206 }
207
208 String readLine() {
209 // Get line from the buffer if possible.
210 if (_buffer != null) {
211 var result = _readLineFromBuffer();
212 if (result != null) return result;
213 }
214 // Try to fill more data into the buffer and read a line.
215 if (_fillBuffer()) {
216 if (_eof && _buffer == null) return null;
217 return _readLineFromBuffer();
218 }
219 return null;
220 }
221
222 String get encoding() => _encoding;
223
224 bool get closed() => _closed;
225
226 void set dataHandler(void callback()) {
227 _clientDataHandler = callback;
228 }
229
230 void set closeHandler(void callback()) {
231 _clientCloseHandler = callback;
232 }
233
234 void _dataHandler() {
235 _readData();
236 if (!_decoder.isEmpty() && _clientDataHandler != null) {
237 _clientDataHandler();
238 }
239 }
240
241 void _closeHandler() {
242 _closed = true;
243 if (_clientDataHandler != null) _clientDataHandler();
244 if (_clientCloseHandler != null) _clientCloseHandler();
245 }
246
247 void _readData() {
248 List<int> data = _input.read();
249 if (data != null) {
250 _decoder.write(data);
251 }
252 }
253
254 String _readLineFromBuffer() {
255 // Both \n or \r indicates a new line. If \r is followed by \n the
256 // \n is part of the line breaking character.
257 for (int i = _bufferLineStart; i < _buffer.length; i++) {
258 String char = _buffer[i];
259 if (char == '\r') {
260 if (i == _buffer.length - 1) {
261 if (_eof) {
262 var result = _buffer.substring(_bufferLineStart, i);
263 _resetBuffer();
264 return result;
265 } else {
266 return null;
267 }
268 }
269 var result = _buffer.substring(_bufferLineStart, i);
270 _bufferLineStart = i + 1;
271 if (_buffer[_bufferLineStart] == '\n') _bufferLineStart++;
272 return result;
273 } else if (char == '\n') {
274 var result = _buffer.substring(_bufferLineStart, i);
275 _bufferLineStart = i + 1;
276 return result;
277 }
278 }
279 if (_eof) {
280 var result = _buffer;
281 _resetBuffer();
282 return result;
283 }
284 return null;
285 }
286
287 void _resetBuffer() {
288 _buffer = null;
289 _bufferLineStart = null;
290 }
291
292 // Fill decoded data into the buffer. Returns true if more data was
293 // added or end of file was reached.
294 bool _fillBuffer() {
295 if (_eof) return false;
296 if (_buffer != null && _bufferLineStart == _buffer.length) {
297 _buffer = null;
298 _bufferLineStart = null;
299 }
300 _readData();
301 var decodedString = _decoder.decoded;
302 if (decodedString == null && _closed) {
303 _eof = true;
304 return true;
305 }
306 if (_buffer == null) {
307 _buffer = decodedString;
308 if (_buffer != null) {
309 _bufferLineStart = 0;
310 return true;
311 }
312 } else if (decodedString != null) {
313 _buffer = _buffer.substring(_bufferLineStart) + decodedString;
314 _bufferLineStart = 0;
315 return true;
316 }
317 return false;
318 }
319
320 InputStream _input;
321 String _encoding;
322 _Decoder _decoder;
323 String _buffer; // String can be buffered here if readLine is used.
324 int _bufferLineStart; // Current offset into _buffer if any.
325 bool _closed = false;
326 bool _eof = false; // Has all data been read from the decoder?
327 var _clientDataHandler;
328 var _clientCloseHandler;
329 }
OLDNEW
« no previous file with comments | « runtime/bin/socket_stream.dart ('k') | tests/standalone/src/EchoServerStreamReadUntilTest.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698