Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file | 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 | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 // Interface for decoders decoding binary data into string data. The | 5 // Interface for decoders decoding binary data into string data. The |
| 6 // decoder keeps track of line breaks during decoding. | 6 // decoder keeps track of line breaks during decoding. |
| 7 interface _StringDecoder { | 7 interface _StringDecoder { |
| 8 // Add more binary data to be decoded. The ownership of the buffer | 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. | 9 // is transfered to the decoder and the caller most not modify it any more. |
| 10 int write(List<int> buffer); | 10 int write(List<int> buffer); |
| (...skipping 214 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 225 } else if (_encoding == "ASCII") { | 225 } else if (_encoding == "ASCII") { |
| 226 _decoder = new _AsciiDecoder(); | 226 _decoder = new _AsciiDecoder(); |
| 227 } else { | 227 } else { |
| 228 throw new StreamException("Unsupported encoding $_encoding"); | 228 throw new StreamException("Unsupported encoding $_encoding"); |
| 229 } | 229 } |
| 230 _input.dataHandler = _dataHandler; | 230 _input.dataHandler = _dataHandler; |
| 231 _input.closeHandler = _closeHandler; | 231 _input.closeHandler = _closeHandler; |
| 232 } | 232 } |
| 233 | 233 |
| 234 String read() { | 234 String read() { |
| 235 // If there is buffered data return that first. | 235 String result = _decoder.decoded; |
| 236 var decodedString = _decoder.decoded; | 236 _checkInstallDataHandler(); |
| 237 if (decodedString !== null) { | 237 return result; |
| 238 if (_inputClosed && _decoder.isEmpty()) { | |
| 239 _streamClosed(); | |
| 240 } | |
| 241 return decodedString; | |
| 242 } else if (_inputClosed) { | |
| 243 _streamClosed(); | |
| 244 return null; | |
| 245 } else { | |
| 246 _readData(); | |
| 247 return _decoder.decoded; | |
| 248 } | |
| 249 } | 238 } |
| 250 | 239 |
| 251 String readLine() { | 240 String readLine() { |
| 252 if (_closed) return null; | 241 String decodedLine = _decoder.decodedLine; |
| 253 | 242 if (decodedLine == null) { |
| 254 if (_decoder.lineBreaks == 0) { | 243 if (_inputClosed) { |
| 255 _readData(); | 244 // Last line might not have a line separator. |
| 245 decodedLine = _decoder.decoded; | |
| 246 if (decodedLine != null && decodedLine[decodedLine.length - 1] == '\r') { | |
|
Mads Ager (google)
2011/12/09 08:47:04
Long line.
Søren Gjesse
2011/12/09 11:44:32
Done.
| |
| 247 decodedLine = decodedLine.substring(0, decodedLine.length - 1); | |
| 248 } | |
| 249 } | |
| 256 } | 250 } |
| 257 var decodedLine = _decoder.decodedLine; | 251 _checkInstallDataHandler(); |
| 258 if (decodedLine !== null) { | 252 return decodedLine; |
| 259 if (_inputClosed && _decoder.isEmpty()) { | |
| 260 _streamClosed(); | |
| 261 } | |
| 262 return decodedLine; | |
| 263 } | |
| 264 if (_inputClosed) { | |
| 265 decodedLine = _decoder.decoded; | |
| 266 if (decodedLine[decodedLine.length - 1] == '\r') { | |
| 267 decodedLine = decodedLine.substring(0, decodedLine.length - 1); | |
| 268 } | |
| 269 _streamClosed(); | |
| 270 return decodedLine; | |
| 271 } | |
| 272 return null; | |
| 273 } | 253 } |
| 274 | 254 |
| 275 String get encoding() => _encoding; | 255 String get encoding() => _encoding; |
| 276 | 256 |
| 277 bool get closed() => _closed; | 257 bool get closed() => _inputClosed && _decoder.isEmpty(); |
| 278 | 258 |
| 279 void set dataHandler(void callback()) { | 259 void set dataHandler(void callback()) { |
| 280 _clientDataHandler = callback; | 260 _clientDataHandler = callback; |
| 281 _clientLineHandler = null; | 261 _clientLineHandler = null; |
| 262 _checkInstallDataHandler(); | |
| 282 } | 263 } |
| 283 | 264 |
| 284 void set lineHandler(void callback()) { | 265 void set lineHandler(void callback()) { |
| 285 _clientLineHandler = callback; | 266 _clientLineHandler = callback; |
| 286 _clientDataHandler = null; | 267 _clientDataHandler = null; |
| 268 _checkInstallDataHandler(); | |
| 287 } | 269 } |
| 288 | 270 |
| 289 void set closeHandler(void callback()) { | 271 void set closeHandler(void callback()) { |
| 290 _clientCloseHandler = callback; | 272 _clientCloseHandler = callback; |
| 291 } | 273 } |
| 292 | 274 |
| 293 void _dataHandler() { | 275 void _dataHandler() { |
| 294 _readData(); | 276 _readData(); |
| 295 if (!_decoder.isEmpty() && _clientDataHandler !== null) { | 277 if (!_decoder.isEmpty() && _clientDataHandler !== null) { |
| 296 _clientDataHandler(); | 278 _clientDataHandler(); |
| 297 } | 279 } |
| 298 if (_decoder.lineBreaks > 0 && _clientLineHandler !== null) { | 280 if (_decoder.lineBreaks > 0 && _clientLineHandler !== null) { |
| 299 _clientLineHandler(); | 281 _clientLineHandler(); |
| 300 } | 282 } |
| 283 _checkScheduleCallback(); | |
| 284 _checkInstallDataHandler(); | |
| 301 } | 285 } |
| 302 | 286 |
| 303 void _closeHandler() { | 287 void _closeHandler() { |
| 304 _inputClosed = true; | 288 _inputClosed = true; |
| 305 if (!_decoder.isEmpty()) { | 289 if (_decoder.isEmpty() && _clientCloseHandler != null) { |
| 306 // If there is still data buffered call the data handler. | 290 _clientCloseHandler(); |
| 307 if (_clientDataHandler !== null) _clientDataHandler(); | 291 _closed = true; |
| 308 if (_clientLineHandler !== null) _clientLineHandler(); | |
| 309 } else { | 292 } else { |
| 310 _closed = true; | 293 _checkScheduleCallback(); |
| 311 if (_clientCloseHandler !== null) _clientCloseHandler(); | |
| 312 } | 294 } |
| 313 } | 295 } |
| 314 | 296 |
| 315 void _readData() { | 297 void _readData() { |
| 316 List<int> data = _input.read(); | 298 List<int> data = _input.read(); |
| 317 if (data !== null) { | 299 if (data !== null) { |
| 318 _decoder.write(data); | 300 _decoder.write(data); |
| 319 } | 301 } |
| 320 } | 302 } |
| 321 | 303 |
| 322 void _streamClosed() { | 304 void _checkInstallDataHandler() { |
| 323 _closed = true; | 305 if (_inputClosed || |
| 306 (_clientDataHandler === null && _clientLineHandler === null)) { | |
| 307 _input.dataHandler = null; | |
| 308 } else if (_clientDataHandler !== null) { | |
| 309 if (_decoder.isEmpty()) { | |
| 310 _input.dataHandler = _dataHandler; | |
| 311 } else { | |
| 312 _input.dataHandler = null; | |
| 313 } | |
| 314 } else { | |
| 315 assert(_clientLineHandler !== null); | |
| 316 if (_decoder.lineBreaks == 0) { | |
| 317 _input.dataHandler = _dataHandler; | |
| 318 } else { | |
| 319 _input.dataHandler = null; | |
| 320 } | |
| 321 } | |
| 322 } | |
| 324 | 323 |
| 325 // TODO(sgjesse): Find a better way of scheduling callbacks from | 324 // TODO(sgjesse): Find a better way of scheduling callbacks from |
| 326 // the event loop. | 325 // the event loop. |
| 326 void _checkScheduleCallback() { | |
| 327 void issueDataCallback(Timer timer) { | |
| 328 _scheduledDataCallback = null; | |
| 329 if (_clientDataHandler !== null) { | |
| 330 _clientDataHandler(); | |
| 331 _checkScheduleCallback(); | |
| 332 } | |
| 333 } | |
| 334 | |
| 335 void issueLineCallback(Timer timer) { | |
| 336 _scheduledLineCallback = null; | |
| 337 if (_clientLineHandler !== null) { | |
| 338 _clientLineHandler(); | |
| 339 _checkScheduleCallback(); | |
| 340 } | |
| 341 } | |
| 342 | |
| 327 void issueCloseCallback(Timer timer) { | 343 void issueCloseCallback(Timer timer) { |
| 328 if (_clientCloseHandler !== null) _clientCloseHandler(); | 344 _scheduledCloseCallback = null; |
| 345 if (!_closed) { | |
| 346 if (_clientCloseHandler !== null) _clientCloseHandler(); | |
| 347 _closed = true; | |
| 348 } | |
| 329 } | 349 } |
| 330 new Timer(issueCloseCallback, 0, false); | 350 |
| 351 if (!_closed) { | |
| 352 // Schedule data callback if string data available. | |
| 353 if (_clientDataHandler != null && | |
| 354 !_decoder.isEmpty() && | |
| 355 _scheduledDataCallback == null) { | |
| 356 if (_scheduledLineCallback != null) _scheduledLineCallback.cancel(); | |
| 357 _scheduledDataCallback = new Timer(issueDataCallback, 0, false); | |
| 358 } | |
| 359 | |
| 360 // Schedule line callback if a line is available. | |
| 361 if (_clientLineHandler != null && | |
| 362 (_decoder.lineBreaks > 0 || (!_decoder.isEmpty() && _inputClosed)) && | |
| 363 _scheduledLineCallback == null) { | |
| 364 if (_scheduledDataCallback != null) _scheduledDataCallback.cancel(); | |
| 365 _scheduledLineCallback = new Timer(issueLineCallback, 0, false); | |
| 366 } | |
| 367 | |
| 368 // Schedule close callback if no more data and input is closed. | |
| 369 if (_decoder.isEmpty() && | |
| 370 _inputClosed && | |
| 371 _scheduledCloseCallback == null) { | |
| 372 _scheduledCloseCallback = new Timer(issueCloseCallback, 0, false); | |
| 373 } | |
| 374 } | |
| 331 } | 375 } |
| 332 | 376 |
| 333 InputStream _input; | 377 InputStream _input; |
| 334 String _encoding; | 378 String _encoding; |
| 335 _StringDecoder _decoder; | 379 _StringDecoder _decoder; |
| 336 bool _inputClosed = false; // Is the underlying input stream closed? | 380 bool _inputClosed = false; // Is the underlying input stream closed? |
| 337 bool _closed = false; // Is this stream closed. | 381 bool _closed = false; // Is this stream closed. |
| 338 bool _eof = false; // Has all data been read from the decoder? | 382 bool _eof = false; // Has all data been read from the decoder? |
| 339 var _clientDataHandler; | 383 Timer _scheduledDataCallback; |
| 340 var _clientLineHandler; | 384 Timer _scheduledLineCallback; |
| 341 var _clientCloseHandler; | 385 Timer _scheduledCloseCallback; |
| 386 Function _clientDataHandler; | |
| 387 Function _clientLineHandler; | |
| 388 Function _clientCloseHandler; | |
| 342 } | 389 } |
| OLD | NEW |