Chromium Code Reviews| 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 |