OLD | NEW |
---|---|
(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. | |
Mads Ager (google)
2011/10/26 10:56:07
Ownership of the buffer is transferred to the Deco
Søren Gjesse
2011/10/26 12:03:31
Done.
| |
40 int write(List<int> buffer); | |
41 | |
42 // Returns whether any decoded data is available. | |
43 bool isEmpty(); | |
44 | |
45 // Get the decoded data. | |
Mads Ager (google)
2011/10/26 10:56:07
Get the data decoded since the last call to decode
Søren Gjesse
2011/10/26 12:03:31
Done.
| |
46 T get decoded(); | |
47 } | |
48 | |
49 | |
50 class DecoderException implements Exception { | |
51 const DecoderException([String this.message]); | |
52 String toString() => "DecoderException: $message"; | |
53 final String message; | |
54 } | |
55 | |
56 | |
57 // Utility class for decoding UTF-8 from data delivered as a stream of | |
58 // bytes. | |
59 class _StringDecoderBase implements _Decoder<String> { | |
60 _StringDecoderBase() | |
61 : _bufferList = new _BufferList(), | |
62 _result = new StringBuffer(); | |
63 | |
64 // Add UTF-8 encoded data. | |
65 int write(List<int> buffer) { | |
66 _bufferList.add(buffer); | |
67 // Decode as many bytes into characters as possible. | |
68 while (_bufferList.length > 0) { | |
69 if (!_processNext()) { | |
70 break; | |
71 } | |
72 } | |
73 return buffer.length; | |
74 } | |
75 | |
76 // Check if any characters have been decoded so far. | |
77 bool isEmpty() { | |
78 return _result.isEmpty(); | |
79 } | |
80 | |
81 // Return the string decoded so far. | |
Mads Ager (google)
2011/10/26 10:56:07
so far -> since the last call to decoded.
Søren Gjesse
2011/10/26 12:03:31
Done.
| |
82 String get decoded() { | |
83 if (isEmpty()) { | |
84 return null; | |
85 } else { | |
86 String result = _result.toString(); | |
87 _result = new StringBuffer(); | |
88 return result; | |
89 } | |
90 } | |
91 | |
92 abstract bool _processNext(); | |
93 | |
94 _BufferList _bufferList; | |
95 StringBuffer _result; | |
96 } | |
97 | |
98 | |
99 // Utility class for decoding ascii data delivered as a stream of | |
100 // bytes. | |
101 class _AsciiDecoder extends _StringDecoderBase { | |
102 // Process the next ascii encoded character. | |
103 bool _processNext() { | |
104 while (_bufferList.length > 0) { | |
105 int byte = _bufferList.next(); | |
106 if (byte > 127) { | |
107 throw new DecoderException("Illegal ASCII character $byte"); | |
108 } | |
109 _result.addCharCode(byte); | |
110 } | |
111 return true; | |
112 } | |
113 } | |
114 | |
115 | |
116 // Utility class for decoding Latin-1 data delivered as a stream of | |
117 // bytes. | |
118 class _Latin1Decoder extends _StringDecoderBase { | |
119 // Process the next Latin-1 encoded character. | |
120 bool _processNext() { | |
121 while (_bufferList.length > 0) { | |
122 int byte = _bufferList.next(); | |
123 _result.addCharCode(byte); | |
124 } | |
125 return true; | |
126 } | |
127 } | |
128 | |
129 | |
130 // Utility class for decoding UTF-8 from data delivered as a stream of | |
131 // bytes. | |
132 class _UTF8Decoder extends _StringDecoderBase { | |
133 // Process the next UTF-8 encoded character. | |
134 bool _processNext() { | |
135 // Peek the next byte to calculate the number of bytes required | |
136 // for the next character. | |
137 int value = _bufferList.peek() & 0xFF; | |
138 if ((value & 0x80) == 0x80) { | |
139 int additionalBytes; | |
140 if ((value & 0xe0) == 0xc0) { // 110xxxxx | |
141 value = value & 0x1F; | |
142 additionalBytes = 1; | |
143 } else if ((value & 0xf0) == 0xe0) { // 1110xxxx | |
144 value = value & 0x0F; | |
145 additionalBytes = 2; | |
146 } else { // 11110xxx | |
147 value = value & 0x07; | |
148 additionalBytes = 3; | |
149 } | |
150 // Check if there are not enough bytes to decode the character | |
Mads Ager (google)
2011/10/26 10:56:07
Check if there are enough bytes to decode the char
Søren Gjesse
2011/10/26 12:03:31
Done.
| |
151 // return false. | |
152 if (_bufferList.length < additionalBytes + 1) { | |
153 return false; | |
154 } | |
155 // Remove the value peeked from the buffer list. | |
156 _bufferList.next(); | |
157 for (int i = 0; i < additionalBytes; i++) { | |
158 int byte = _bufferList.next(); | |
159 value = value << 6 | (byte & 0x3F); | |
160 } | |
161 } else { | |
162 // Remove the value peeked from the buffer list. | |
163 _bufferList.next(); | |
164 } | |
165 _result.addCharCode(value); | |
166 return true; | |
167 } | |
168 } | |
169 | |
170 | |
171 class _StringInputStream implements StringInputStream { | |
172 _StringInputStream(InputStream this._input, [String this._encoding]) { | |
173 if (_encoding == null) { | |
174 _encoding = "UTF-8"; | |
175 } | |
176 if (_encoding == "UTF-8") { | |
177 _decoder = new _UTF8Decoder(); | |
178 } else if (_encoding == "ISO-8859-1") { | |
179 _decoder = new _Latin1Decoder(); | |
180 } else if (_encoding == "ASCII") { | |
181 _decoder = new _AsciiDecoder(); | |
182 } else { | |
183 throw new StreamException("Unsupported encoding $_encoding"); | |
184 } | |
185 _input.dataHandler = _dataHandler; | |
186 _input.closeHandler = _closeHandler; | |
187 } | |
188 | |
189 String read() { | |
190 // If there is buffered data return that first. | |
191 var decodedString = _decoder.decoded; | |
192 if (_buffer != null) { | |
193 var result = _buffer; | |
194 _resetBuffer(); | |
195 if (decodedString != null) result += decodedString; | |
196 return result; | |
197 } else { | |
198 if (decodedString != null) { | |
199 return decodedString; | |
200 } else { | |
201 _readData(); | |
202 return _decoder.decoded; | |
203 } | |
204 } | |
205 } | |
206 | |
207 String readLine() { | |
208 // Get line from the buffer if possible. | |
209 if (_buffer != null) { | |
210 var result = _readLineFromBuffer(); | |
211 if (result != null) return result; | |
212 } | |
213 // Try to fill more data into the buffer and read a line. | |
214 if (_fillBuffer()) { | |
215 if (_eof && _buffer == null) return null; | |
216 return _readLineFromBuffer(); | |
217 } | |
218 return null; | |
219 } | |
220 | |
221 String get encoding() => _encoding; | |
222 | |
223 bool get closed() => _closed; | |
224 | |
225 void set dataHandler(void callback()) { | |
226 _clientDataHandler = callback; | |
227 } | |
228 | |
229 void set closeHandler(void callback()) { | |
230 _clientCloseHandler = callback; | |
231 } | |
232 | |
233 void _dataHandler() { | |
234 _readData(); | |
235 if (!_decoder.isEmpty() && _clientDataHandler != null) { | |
236 _clientDataHandler(); | |
237 } | |
238 } | |
239 | |
240 void _closeHandler() { | |
241 print("close handler"); | |
Mads Ager (google)
2011/10/26 10:56:07
Remove
Søren Gjesse
2011/10/26 12:03:31
Done.
| |
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 | |
Mads Ager (google)
2011/10/26 10:56:07
Period after 'buffer'.
Søren Gjesse
2011/10/26 12:03:31
Done.
| |
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 } | |
OLD | NEW |