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. 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 } |
OLD | NEW |