|
1 | 1 | import 'dart:async'; |
2 | | -import 'dart:collection'; |
3 | 2 | import 'dart:typed_data'; |
4 | 3 |
|
5 | 4 | import 'package:buffer/buffer.dart'; |
@@ -35,76 +34,95 @@ Map<int, _ServerMessageFn> _messageTypeMap = { |
35 | 34 | $N: NoticeMessage.parse, |
36 | 35 | }; |
37 | 36 |
|
38 | | -class MessageFramer { |
39 | | - final CodecContext _codecContext; |
40 | | - late final _reader = PgByteDataReader(codecContext: _codecContext); |
41 | | - final messageQueue = Queue<ServerMessage>(); |
| 37 | +class _BytesFrame { |
| 38 | + final int type; |
| 39 | + final int length; |
| 40 | + final Uint8List bytes; |
42 | 41 |
|
43 | | - MessageFramer(this._codecContext); |
| 42 | + _BytesFrame(this.type, this.length, this.bytes); |
| 43 | +} |
44 | 44 |
|
45 | | - int? _type; |
46 | | - int _expectedLength = 0; |
| 45 | +StreamTransformer<Uint8List, ServerMessage> bytesToMessageParser() { |
| 46 | + return StreamTransformer<Uint8List, ServerMessage>.fromHandlers( |
| 47 | + handleData: (data, sink) {}, |
| 48 | + ); |
| 49 | +} |
47 | 50 |
|
48 | | - bool get _hasReadHeader => _type != null; |
49 | | - bool get _canReadHeader => _reader.remainingLength >= _headerByteSize; |
| 51 | +final _emptyData = Uint8List(0); |
50 | 52 |
|
51 | | - bool get _isComplete => |
52 | | - _expectedLength == 0 || _expectedLength <= _reader.remainingLength; |
| 53 | +class _BytesToFrameParser |
| 54 | + extends StreamTransformerBase<Uint8List, _BytesFrame> { |
| 55 | + final CodecContext _codecContext; |
53 | 56 |
|
54 | | - Future<void> addBytes(Uint8List bytes) async { |
55 | | - _reader.add(bytes); |
| 57 | + _BytesToFrameParser(this._codecContext); |
56 | 58 |
|
57 | | - while (true) { |
58 | | - if (!_hasReadHeader && _canReadHeader) { |
59 | | - _type = _reader.readUint8(); |
60 | | - _expectedLength = _reader.readUint32() - 4; |
61 | | - } |
| 59 | + @override |
| 60 | + Stream<_BytesFrame> bind(Stream<Uint8List> stream) async* { |
| 61 | + final reader = PgByteDataReader(codecContext: _codecContext); |
62 | 62 |
|
63 | | - // special case |
64 | | - if (_type == SharedMessageId.copyDone) { |
65 | | - // unlike other messages, CopyDoneMessage only takes the length as an |
66 | | - // argument (must be the full length including the length bytes) |
67 | | - final msg = CopyDoneMessage(_expectedLength + 4); |
68 | | - _addMsg(msg); |
69 | | - continue; |
70 | | - } |
| 63 | + int? type; |
| 64 | + int expectedLength = 0; |
71 | 65 |
|
72 | | - if (_hasReadHeader && _isComplete) { |
73 | | - final msgMaker = _messageTypeMap[_type]; |
74 | | - if (msgMaker == null) { |
75 | | - _addMsg(UnknownMessage(_type!, _reader.read(_expectedLength))); |
76 | | - continue; |
| 66 | + await for (final bytes in stream) { |
| 67 | + reader.add(bytes); |
| 68 | + |
| 69 | + while (true) { |
| 70 | + if (type == null && reader.remainingLength >= _headerByteSize) { |
| 71 | + type = reader.readUint8(); |
| 72 | + expectedLength = reader.readUint32() - 4; |
77 | 73 | } |
78 | 74 |
|
79 | | - final targetRemainingLength = _reader.remainingLength - _expectedLength; |
80 | | - final msg = await msgMaker(_reader, _expectedLength); |
81 | | - if (_reader.remainingLength > targetRemainingLength) { |
82 | | - throw StateError( |
83 | | - 'Message parser consumed more bytes than expected. type=$_type expectedLength=$_expectedLength'); |
| 75 | + // special case |
| 76 | + if (type == SharedMessageId.copyDone) { |
| 77 | + // unlike other messages, CopyDoneMessage only takes the length as an |
| 78 | + // argument (must be the full length including the length bytes) |
| 79 | + yield _BytesFrame(type!, expectedLength, _emptyData); |
| 80 | + type = null; |
| 81 | + expectedLength = 0; |
| 82 | + continue; |
84 | 83 | } |
85 | | - // consume the rest of the message |
86 | | - if (_reader.remainingLength < targetRemainingLength) { |
87 | | - _reader.read(targetRemainingLength - _reader.remainingLength); |
| 84 | + |
| 85 | + if (type != null && expectedLength <= reader.remainingLength) { |
| 86 | + final data = reader.read(expectedLength); |
| 87 | + yield _BytesFrame(type, expectedLength, data); |
| 88 | + type = null; |
| 89 | + expectedLength = 0; |
| 90 | + continue; |
88 | 91 | } |
89 | 92 |
|
90 | | - _addMsg(msg); |
91 | | - continue; |
| 93 | + break; |
92 | 94 | } |
93 | | - |
94 | | - break; |
95 | 95 | } |
96 | 96 | } |
| 97 | +} |
97 | 98 |
|
98 | | - void _addMsg(ServerMessage msg) { |
99 | | - messageQueue.add(msg); |
100 | | - _type = null; |
101 | | - _expectedLength = 0; |
102 | | - } |
| 99 | +class BytesToMessageParser |
| 100 | + extends StreamTransformerBase<Uint8List, ServerMessage> { |
| 101 | + final CodecContext _codecContext; |
| 102 | + |
| 103 | + BytesToMessageParser(this._codecContext); |
103 | 104 |
|
104 | | - bool get hasMessage => messageQueue.isNotEmpty; |
| 105 | + @override |
| 106 | + Stream<ServerMessage> bind(Stream<Uint8List> stream) { |
| 107 | + return stream |
| 108 | + .transform(_BytesToFrameParser(_codecContext)) |
| 109 | + .asyncMap((frame) async { |
| 110 | + // special case |
| 111 | + if (frame.type == SharedMessageId.copyDone) { |
| 112 | + // unlike other messages, CopyDoneMessage only takes the length as an |
| 113 | + // argument (must be the full length including the length bytes) |
| 114 | + return CopyDoneMessage(frame.length + 4); |
| 115 | + } |
| 116 | + |
| 117 | + final msgMaker = _messageTypeMap[frame.type]; |
| 118 | + if (msgMaker == null) { |
| 119 | + return UnknownMessage(frame.type, frame.bytes); |
| 120 | + } |
105 | 121 |
|
106 | | - ServerMessage popMessage() { |
107 | | - return messageQueue.removeFirst(); |
| 122 | + return await msgMaker( |
| 123 | + PgByteDataReader(codecContext: _codecContext)..add(frame.bytes), |
| 124 | + frame.bytes.length); |
| 125 | + }); |
108 | 126 | } |
109 | 127 | } |
110 | 128 |
|
|
0 commit comments