1 /// Licensed to the Apache Software Foundation (ASF) under one
2 /// or more contributor license agreements. See the NOTICE file
3 /// distributed with this work for additional information
4 /// regarding copyright ownership. The ASF licenses this file
5 /// to you under the Apache License, Version 2.0 (the
6 /// 'License'); you may not use this file except in compliance
7 /// with the License. You may obtain a copy of the License at
9 /// http://www.apache.org/licenses/LICENSE-2.0
11 /// Unless required by applicable law or agreed to in writing,
12 /// software distributed under the License is distributed on an
13 /// 'AS IS' BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14 /// KIND, either express or implied. See the License for the
15 /// specific language governing permissions and limitations
16 /// under the License.
20 class TCompactProtocolFactory implements TProtocolFactory<TCompactProtocol> {
21 TCompactProtocolFactory();
23 TCompactProtocol getProtocol(TTransport transport) {
24 return new TCompactProtocol(transport);
28 /// Compact protocol implementation for Thrift.
30 /// Use of fixnum library is required due to bugs like
31 /// https://github.com/dart-lang/sdk/issues/15361
33 /// Adapted from the Java version.
34 class TCompactProtocol extends TProtocol {
35 static const int PROTOCOL_ID = 0x82;
36 static const int VERSION = 1;
37 static const int VERSION_MASK = 0x1f;
38 static const int TYPE_MASK = 0xE0;
39 static const int TYPE_BITS = 0x07;
40 static const int TYPE_SHIFT_AMOUNT = 5;
41 static final TField TSTOP = new TField("", TType.STOP, 0);
43 static const int TYPE_BOOLEAN_TRUE = 0x01;
44 static const int TYPE_BOOLEAN_FALSE = 0x02;
45 static const int TYPE_BYTE = 0x03;
46 static const int TYPE_I16 = 0x04;
47 static const int TYPE_I32 = 0x05;
48 static const int TYPE_I64 = 0x06;
49 static const int TYPE_DOUBLE = 0x07;
50 static const int TYPE_BINARY = 0x08;
51 static const int TYPE_LIST = 0x09;
52 static const int TYPE_SET = 0x0A;
53 static const int TYPE_MAP = 0x0B;
54 static const int TYPE_STRUCT = 0x0C;
56 static final List<int> _typeMap = new List.unmodifiable(new List(16)
57 ..[TType.STOP] = TType.STOP
58 ..[TType.BOOL] = TYPE_BOOLEAN_TRUE
59 ..[TType.BYTE] = TYPE_BYTE
60 ..[TType.I16] = TYPE_I16
61 ..[TType.I32] = TYPE_I32
62 ..[TType.I64] = TYPE_I64
63 ..[TType.DOUBLE] = TYPE_DOUBLE
64 ..[TType.STRING] = TYPE_BINARY
65 ..[TType.LIST] = TYPE_LIST
66 ..[TType.SET] = TYPE_SET
67 ..[TType.MAP] = TYPE_MAP
68 ..[TType.STRUCT] = TYPE_STRUCT);
70 static const Utf8Codec _utf8Codec = const Utf8Codec();
72 // Pretend this is a stack
73 DoubleLinkedQueue<int> _lastField = new DoubleLinkedQueue<int>();
76 TField _booleanField = null;
77 bool _boolValue = null;
79 final Uint8List tempList = new Uint8List(10);
80 final ByteData tempBD = new ByteData(10);
82 TCompactProtocol(TTransport transport) : super(transport);
85 void writeMessageBegin(TMessage message) {
86 writeByte(PROTOCOL_ID);
87 writeByte((VERSION & VERSION_MASK) |
88 ((message.type << TYPE_SHIFT_AMOUNT) & TYPE_MASK));
89 _writeVarInt32(new Int32(message.seqid));
90 writeString(message.name);
93 void writeMessageEnd() {}
95 void writeStructBegin(TStruct struct) {
96 _lastField.addLast(_lastFieldId);
100 void writeStructEnd() {
101 _lastFieldId = _lastField.removeLast();
104 void writeFieldBegin(TField field) {
105 if (field.type == TType.BOOL) {
106 _booleanField = field;
108 _writeFieldBegin(field, -1);
112 void _writeFieldBegin(TField field, int typeOverride) {
114 typeOverride == -1 ? _getCompactType(field.type) : typeOverride;
116 if (field.id > _lastFieldId && field.id - _lastFieldId <= 15) {
117 writeByte((field.id - _lastFieldId) << 4 | typeToWrite);
119 writeByte(typeToWrite);
123 _lastFieldId = field.id;
126 void writeFieldEnd() {}
128 void writeFieldStop() {
129 writeByte(TType.STOP);
132 void writeMapBegin(TMap map) {
133 if (map.length == 0) {
136 _writeVarInt32(new Int32(map.length));
138 _getCompactType(map.keyType) << 4 | _getCompactType(map.valueType));
142 void writeMapEnd() {}
144 void writeListBegin(TList list) {
145 _writeCollectionBegin(list.elementType, list.length);
148 void writeListEnd() {}
150 void writeSetBegin(TSet set) {
151 _writeCollectionBegin(set.elementType, set.length);
154 void writeSetEnd() {}
156 void writeBool(bool b) {
157 if (b == null) b = false;
158 if (_booleanField != null) {
160 _booleanField, b ? TYPE_BOOLEAN_TRUE : TYPE_BOOLEAN_FALSE);
161 _booleanField = null;
163 writeByte(b ? TYPE_BOOLEAN_TRUE : TYPE_BOOLEAN_FALSE);
167 void writeByte(int b) {
168 if (b == null) b = 0;
170 transport.write(tempList, 0, 1);
173 void writeI16(int i16) {
174 if (i16 == null) i16 = 0;
175 _writeVarInt32(_int32ToZigZag(new Int32(i16)));
178 void writeI32(int i32) {
179 if (i32 == null) i32 = 0;
180 _writeVarInt32(_int32ToZigZag(new Int32(i32)));
183 void writeI64(int i64) {
184 if (i64 == null) i64 = 0;
185 _writeVarInt64(_int64ToZigZag(new Int64(i64)));
188 void writeDouble(double d) {
189 if (d == null) d = 0.0;
190 tempBD.setFloat64(0, d, Endianness.little);
191 transport.write(tempBD.buffer.asUint8List(), 0, 8);
194 void writeString(String str) {
196 str != null ? _utf8Codec.encode(str) : new Uint8List.fromList([]);
200 void writeBinary(Uint8List bytes) {
201 _writeVarInt32(new Int32(bytes.length));
202 transport.write(bytes, 0, bytes.length);
205 void _writeVarInt32(Int32 n) {
208 if ((n & ~0x7F) == 0) {
209 tempList[idx++] = (n & 0xFF).toInt();
212 tempList[idx++] = (((n & 0x7F) | 0x80) & 0xFF).toInt();
213 n = n.shiftRightUnsigned(7);
216 transport.write(tempList, 0, idx);
219 void _writeVarInt64(Int64 n) {
222 if ((n & ~0x7F) == 0) {
223 tempList[idx++] = (n & 0xFF).toInt();
226 tempList[idx++] = (((n & 0x7F) | 0x80) & 0xFF).toInt();
227 n = n.shiftRightUnsigned(7);
230 transport.write(tempList, 0, idx);
233 void _writeCollectionBegin(int elemType, int length) {
235 writeByte(length << 4 | _getCompactType(elemType));
237 writeByte(0xF0 | _getCompactType(elemType));
238 _writeVarInt32(new Int32(length));
242 Int32 _int32ToZigZag(Int32 n) {
243 return (n << 1) ^ (n >> 31);
246 Int64 _int64ToZigZag(Int64 n) {
247 return (n << 1) ^ (n >> 63);
251 TMessage readMessageBegin() {
252 int protocolId = readByte();
253 if (protocolId != PROTOCOL_ID) {
254 throw new TProtocolError(TProtocolErrorType.BAD_VERSION,
255 'Expected protocol id $PROTOCOL_ID but got $protocolId');
257 int versionAndType = readByte();
258 int version = versionAndType & VERSION_MASK;
259 if (version != VERSION) {
260 throw new TProtocolError(TProtocolErrorType.BAD_VERSION,
261 'Expected version $VERSION but got $version');
263 int type = (versionAndType >> TYPE_SHIFT_AMOUNT) & TYPE_BITS;
264 int seqId = _readVarInt32().toInt();
265 String messageName = readString();
266 return new TMessage(messageName, type, seqId);
269 void readMessageEnd() {}
271 TStruct readStructBegin() {
272 _lastField.addLast(_lastFieldId);
274 // TODO make this a constant?
275 return new TStruct();
278 void readStructEnd() {
279 _lastFieldId = _lastField.removeLast();
282 TField readFieldBegin() {
283 int type = readByte();
284 if (type == TType.STOP) {
289 int modifier = (type & 0xF0) >> 4;
293 fieldId = _lastFieldId + modifier;
296 TField field = new TField('', _getTType(type & 0x0F), fieldId);
297 if (_isBoolType(type)) {
298 _boolValue = (type & 0x0F) == TYPE_BOOLEAN_TRUE;
301 _lastFieldId = field.id;
305 void readFieldEnd() {}
307 TMap readMapBegin() {
308 int length = _readVarInt32().toInt();
309 _checkNegReadLength(length);
311 int keyAndValueType = length == 0 ? 0 : readByte();
312 int keyType = _getTType(keyAndValueType >> 4);
313 int valueType = _getTType(keyAndValueType & 0x0F);
314 return new TMap(keyType, valueType, length);
319 TList readListBegin() {
320 int lengthAndType = readByte();
321 int length = (lengthAndType >> 4) & 0x0F;
323 length = _readVarInt32().toInt();
325 _checkNegReadLength(length);
326 int type = _getTType(lengthAndType);
327 return new TList(type, length);
330 void readListEnd() {}
332 TSet readSetBegin() {
333 TList tlist = readListBegin();
334 return new TSet(tlist.elementType, tlist.length);
340 if (_boolValue != null) {
341 bool result = _boolValue;
345 return readByte() == TYPE_BOOLEAN_TRUE;
349 transport.readAll(tempList, 0, 1);
350 return tempList.buffer.asByteData().getUint8(0);
354 return _zigzagToInt32(_readVarInt32()).toInt();
358 return _zigzagToInt32(_readVarInt32()).toInt();
362 return _zigzagToInt64(_readVarInt64()).toInt();
365 double readDouble() {
366 transport.readAll(tempList, 0, 8);
367 return tempList.buffer.asByteData().getFloat64(0, Endianness.little);
370 String readString() {
371 int length = _readVarInt32().toInt();
372 _checkNegReadLength(length);
374 // TODO look at using temp for small strings?
375 Uint8List buff = new Uint8List(length);
376 transport.readAll(buff, 0, length);
377 return _utf8Codec.decode(buff);
380 Uint8List readBinary() {
381 int length = _readVarInt32().toInt();
382 _checkNegReadLength(length);
384 Uint8List buff = new Uint8List(length);
385 transport.readAll(buff, 0, length);
389 Int32 _readVarInt32() {
390 Int32 result = Int32.ZERO;
393 Int32 b = new Int32(readByte());
394 result |= (b & 0x7f) << shift;
395 if ((b & 0x80) != 0x80) break;
401 Int64 _readVarInt64() {
402 Int64 result = Int64.ZERO;
405 Int64 b = new Int64(readByte());
406 result |= (b & 0x7f) << shift;
407 if ((b & 0x80) != 0x80) break;
413 Int32 _zigzagToInt32(Int32 n) {
414 return (n.shiftRightUnsigned(1)) ^ -(n & 1);
417 Int64 _zigzagToInt64(Int64 n) {
418 return (n.shiftRightUnsigned(1)) ^ -(n & 1);
421 void _checkNegReadLength(int length) {
423 throw new TProtocolError(
424 TProtocolErrorType.NEGATIVE_SIZE, 'Negative length: $length');
428 int _getCompactType(int ttype) {
429 return _typeMap[ttype];
432 int _getTType(int type) {
433 switch (type & 0x0F) {
436 case TYPE_BOOLEAN_FALSE:
437 case TYPE_BOOLEAN_TRUE:
460 throw new TProtocolError(
461 TProtocolErrorType.INVALID_DATA, "Unknown type: ${type & 0x0F}");
465 bool _isBoolType(int b) {
466 int lowerNibble = b & 0x0F;
467 return lowerNibble == TYPE_BOOLEAN_TRUE ||
468 lowerNibble == TYPE_BOOLEAN_FALSE;