| Index: pkg/analyzer_cli/lib/src/message_grouper.dart
|
| diff --git a/pkg/analyzer_cli/lib/src/message_grouper.dart b/pkg/analyzer_cli/lib/src/message_grouper.dart
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..8696796bc19673334137d9931ed3dcd5a2880e4c
|
| --- /dev/null
|
| +++ b/pkg/analyzer_cli/lib/src/message_grouper.dart
|
| @@ -0,0 +1,112 @@
|
| +// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
|
| +// for details. All rights reserved. Use of this source code is governed by a
|
| +// BSD-style license that can be found in the LICENSE file.
|
| +
|
| +import 'dart:io';
|
| +import 'dart:typed_data';
|
| +
|
| +/// Groups stdin input into messages by interpreting it as
|
| +/// base-128 encoded lengths interleaved with raw data.
|
| +///
|
| +/// The base-128 encoding is in little-endian order, with the high bit set on
|
| +/// all bytes but the last. This was chosen since it's the same as the
|
| +/// base-128 encoding used by protobufs, so it allows a modest amount of code
|
| +/// reuse at the other end of the protocol.
|
| +///
|
| +/// Possible future improvements to consider (should a debugging need arise):
|
| +/// - Put a magic number at the beginning of the stream.
|
| +/// - Use a guard byte between messages to sanity check that the encoder and
|
| +/// decoder agree on the encoding of lengths.
|
| +class MessageGrouper {
|
| + final _state = new _MessageGrouperState();
|
| + final Stdin _stdin;
|
| +
|
| + MessageGrouper(this._stdin);
|
| +
|
| + /// Blocks until the next full message is received, and then returns it.
|
| + ///
|
| + /// Returns null at end of file.
|
| + List<int> get next {
|
| + var message;
|
| + while (message == null) {
|
| + var nextByte = _stdin.readByteSync();
|
| + if (nextByte == -1) return null;
|
| + message = _state.handleInput(nextByte);
|
| + }
|
| + return message;
|
| + }
|
| +}
|
| +
|
| +/// State held by the [MessageGrouper] while waiting for additional data to
|
| +/// arrive.
|
| +class _MessageGrouperState {
|
| + /// `true` means we are waiting to receive bytes of base-128 encoded length.
|
| + /// Some bytes of length may have been received already.
|
| + ///
|
| + /// `false` means we are waiting to receive more bytes of message data. Some
|
| + /// bytes of message data may have been received already.
|
| + bool waitingForLength = true;
|
| +
|
| + /// If [waitingForLength] is `true`, the decoded value of the length bytes
|
| + /// received so far (if any). If [waitingForLength] is `false`, the decoded
|
| + /// length that was most recently received.
|
| + int length = 0;
|
| +
|
| + /// If [waitingForLength] is `true`, the amount by which the next received
|
| + /// length byte must be left-shifted; otherwise undefined.
|
| + int lengthShift = 0;
|
| +
|
| + /// If [waitingForLength] is `false`, a [Uint8List] which is ready to receive
|
| + /// message data. Otherwise null.
|
| + Uint8List message;
|
| +
|
| + /// If [waitingForLength] is `false`, the number of message bytes that have
|
| + /// been received so far. Otherwise zero.
|
| + int numMessageBytesReceived;
|
| +
|
| + _MessageGrouperState() {
|
| + reset();
|
| + }
|
| +
|
| + /// Handle one byte at a time.
|
| + ///
|
| + /// Returns a [List<int>] of message bytes if [byte] was the last byte in a
|
| + /// message, otherwise returns [null].
|
| + List<int> handleInput(int byte) {
|
| + if (waitingForLength) {
|
| + length |= (byte & 0x7f) << lengthShift;
|
| + if ((byte & 0x80) == 0) {
|
| + waitingForLength = false;
|
| + message = new Uint8List(length);
|
| + if (length == 0) {
|
| + // There is no message data to wait for, so just go ahead and deliver the
|
| + // empty message.
|
| + var messageToReturn = message;
|
| + reset();
|
| + return messageToReturn;
|
| + }
|
| + } else {
|
| + lengthShift += 7;
|
| + }
|
| + } else {
|
| + message[numMessageBytesReceived] = byte;
|
| + numMessageBytesReceived++;
|
| + if (numMessageBytesReceived == length) {
|
| + var messageToReturn = message;
|
| + reset();
|
| + return messageToReturn;
|
| + }
|
| + }
|
| + return null;
|
| + }
|
| +
|
| + /// Reset the state so that we are ready to receive the next base-128 encoded
|
| + /// length.
|
| + void reset() {
|
| + waitingForLength = true;
|
| + length = 0;
|
| + lengthShift = 0;
|
| + message = null;
|
| + numMessageBytesReceived = 0;
|
| + }
|
| +}
|
|
|