blob: 3758174cc358083f8570e5f8ac60b538476c950f [file] [log] [blame]
Jens Geyer421444f2019-03-20 22:13:25 +01001// Licensed to the Apache Software Foundation(ASF) under one
Jens Geyeraa0c8b32019-01-28 23:27:45 +01002// 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
8//
9// http://www.apache.org/licenses/LICENSE-2.0
10//
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.
17
18using System;
Mikel Blanchard4b66a9d2020-03-05 00:46:21 +010019using System.Buffers;
zembord9d958a32019-11-21 13:11:44 +030020using System.Buffers.Binary;
Jens Geyeraa0c8b32019-01-28 23:27:45 +010021using System.Collections.Generic;
Jens Geyer5a17b132019-05-26 15:53:37 +020022using System.Diagnostics;
Jens Geyeraa0c8b32019-01-28 23:27:45 +010023using System.Text;
24using System.Threading;
25using System.Threading.Tasks;
26using Thrift.Protocol.Entities;
27using Thrift.Transport;
28
Jens Geyer0d128322021-02-25 09:42:52 +010029#pragma warning disable IDE0079 // unnecessary suppression
30#pragma warning disable IDE0066 // use switch expression
31
Jens Geyeraa0c8b32019-01-28 23:27:45 +010032namespace Thrift.Protocol
33{
Jens Geyeraa0c8b32019-01-28 23:27:45 +010034
35 // ReSharper disable once InconsistentNaming
36 public class TCompactProtocol : TProtocol
37 {
38 private const byte ProtocolId = 0x82;
39 private const byte Version = 1;
40 private const byte VersionMask = 0x1f; // 0001 1111
41 private const byte TypeMask = 0xE0; // 1110 0000
42 private const byte TypeBits = 0x07; // 0000 0111
43 private const int TypeShiftAmount = 5;
Jens Geyer5a17b132019-05-26 15:53:37 +020044
45 private const byte NoTypeOverride = 0xFF;
Jens Geyeraa0c8b32019-01-28 23:27:45 +010046
47 // ReSharper disable once InconsistentNaming
48 private static readonly byte[] TTypeToCompactType = new byte[16];
Jens Geyer5a17b132019-05-26 15:53:37 +020049 private static readonly TType[] CompactTypeToTType = new TType[13];
Jens Geyeraa0c8b32019-01-28 23:27:45 +010050
51 /// <summary>
52 /// Used to keep track of the last field for the current and previous structs, so we can do the delta stuff.
53 /// </summary>
54 private readonly Stack<short> _lastField = new Stack<short>(15);
55
56 /// <summary>
57 /// If we encounter a boolean field begin, save the TField here so it can have the value incorporated.
58 /// </summary>
59 private TField? _booleanField;
60
61 /// <summary>
62 /// If we Read a field header, and it's a boolean field, save the boolean value here so that ReadBool can use it.
63 /// </summary>
64 private bool? _boolValue;
65
66 private short _lastFieldId;
67
Jens Geyer5a17b132019-05-26 15:53:37 +020068 // minimize memory allocations by means of an preallocated bytes buffer
69 // The value of 128 is arbitrarily chosen, the required minimum size must be sizeof(long)
Mikel Blanchard4b66a9d2020-03-05 00:46:21 +010070 private readonly byte[] PreAllocatedBuffer = new byte[128];
Jens Geyer5a17b132019-05-26 15:53:37 +020071
72 private struct VarInt
73 {
74 public byte[] bytes;
75 public int count;
76 }
77
78 // minimize memory allocations by means of an preallocated VarInt buffer
79 private VarInt PreAllocatedVarInt = new VarInt()
80 {
81 bytes = new byte[10], // see Int64ToVarInt()
82 count = 0
83 };
84
85
86
87
Jens Geyeraa0c8b32019-01-28 23:27:45 +010088 public TCompactProtocol(TTransport trans)
89 : base(trans)
90 {
91 TTypeToCompactType[(int) TType.Stop] = Types.Stop;
92 TTypeToCompactType[(int) TType.Bool] = Types.BooleanTrue;
93 TTypeToCompactType[(int) TType.Byte] = Types.Byte;
94 TTypeToCompactType[(int) TType.I16] = Types.I16;
95 TTypeToCompactType[(int) TType.I32] = Types.I32;
96 TTypeToCompactType[(int) TType.I64] = Types.I64;
97 TTypeToCompactType[(int) TType.Double] = Types.Double;
98 TTypeToCompactType[(int) TType.String] = Types.Binary;
99 TTypeToCompactType[(int) TType.List] = Types.List;
100 TTypeToCompactType[(int) TType.Set] = Types.Set;
101 TTypeToCompactType[(int) TType.Map] = Types.Map;
102 TTypeToCompactType[(int) TType.Struct] = Types.Struct;
Jens Geyer5a17b132019-05-26 15:53:37 +0200103
104 CompactTypeToTType[Types.Stop] = TType.Stop;
105 CompactTypeToTType[Types.BooleanTrue] = TType.Bool;
106 CompactTypeToTType[Types.BooleanFalse] = TType.Bool;
107 CompactTypeToTType[Types.Byte] = TType.Byte;
108 CompactTypeToTType[Types.I16] = TType.I16;
109 CompactTypeToTType[Types.I32] = TType.I32;
110 CompactTypeToTType[Types.I64] = TType.I64;
111 CompactTypeToTType[Types.Double] = TType.Double;
112 CompactTypeToTType[Types.Binary] = TType.String;
113 CompactTypeToTType[Types.List] = TType.List;
114 CompactTypeToTType[Types.Set] = TType.Set;
115 CompactTypeToTType[Types.Map] = TType.Map;
116 CompactTypeToTType[Types.Struct] = TType.Struct;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100117 }
118
119 public void Reset()
120 {
121 _lastField.Clear();
122 _lastFieldId = 0;
123 }
124
125 public override async Task WriteMessageBeginAsync(TMessage message, CancellationToken cancellationToken)
126 {
Jens Geyer5a17b132019-05-26 15:53:37 +0200127 PreAllocatedBuffer[0] = ProtocolId;
128 PreAllocatedBuffer[1] = (byte)((Version & VersionMask) | (((uint)message.Type << TypeShiftAmount) & TypeMask));
129 await Trans.WriteAsync(PreAllocatedBuffer, 0, 2, cancellationToken);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100130
Jens Geyer5a17b132019-05-26 15:53:37 +0200131 Int32ToVarInt((uint) message.SeqID, ref PreAllocatedVarInt);
132 await Trans.WriteAsync(PreAllocatedVarInt.bytes, 0, PreAllocatedVarInt.count, cancellationToken);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100133
134 await WriteStringAsync(message.Name, cancellationToken);
135 }
136
Jens Geyerdce22992020-05-16 23:02:27 +0200137 public override Task WriteMessageEndAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100138 {
Jens Geyerdce22992020-05-16 23:02:27 +0200139 cancellationToken.ThrowIfCancellationRequested();
140 return Task.CompletedTask;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100141 }
142
143 /// <summary>
144 /// Write a struct begin. This doesn't actually put anything on the wire. We
145 /// use it as an opportunity to put special placeholder markers on the field
146 /// stack so we can get the field id deltas correct.
147 /// </summary>
Jens Geyerdce22992020-05-16 23:02:27 +0200148 public override Task WriteStructBeginAsync(TStruct @struct, CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100149 {
Jens Geyerdce22992020-05-16 23:02:27 +0200150 cancellationToken.ThrowIfCancellationRequested();
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100151
152 _lastField.Push(_lastFieldId);
153 _lastFieldId = 0;
Jens Geyerdce22992020-05-16 23:02:27 +0200154
155 return Task.CompletedTask;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100156 }
157
Jens Geyerdce22992020-05-16 23:02:27 +0200158 public override Task WriteStructEndAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100159 {
Jens Geyerdce22992020-05-16 23:02:27 +0200160 cancellationToken.ThrowIfCancellationRequested();
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100161
162 _lastFieldId = _lastField.Pop();
Jens Geyerdce22992020-05-16 23:02:27 +0200163
164 return Task.CompletedTask;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100165 }
166
Jens Geyer5a17b132019-05-26 15:53:37 +0200167 private async Task WriteFieldBeginInternalAsync(TField field, byte fieldType, CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100168 {
Jens Geyer5a17b132019-05-26 15:53:37 +0200169 // if there's a exType override passed in, use that. Otherwise ask GetCompactType().
170 if (fieldType == NoTypeOverride)
171 fieldType = GetCompactType(field.Type);
172
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100173
174 // check if we can use delta encoding for the field id
Jens Geyer5a17b132019-05-26 15:53:37 +0200175 if (field.ID > _lastFieldId)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100176 {
Jens Geyer5a17b132019-05-26 15:53:37 +0200177 var delta = field.ID - _lastFieldId;
178 if (delta <= 15)
179 {
180 // Write them together
181 PreAllocatedBuffer[0] = (byte)((delta << 4) | fieldType);
182 await Trans.WriteAsync(PreAllocatedBuffer, 0, 1, cancellationToken);
183 _lastFieldId = field.ID;
184 return;
185 }
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100186 }
187
Jens Geyer5a17b132019-05-26 15:53:37 +0200188 // Write them separate
189 PreAllocatedBuffer[0] = fieldType;
190 await Trans.WriteAsync(PreAllocatedBuffer, 0, 1, cancellationToken);
191 await WriteI16Async(field.ID, cancellationToken);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100192 _lastFieldId = field.ID;
193 }
194
195 public override async Task WriteFieldBeginAsync(TField field, CancellationToken cancellationToken)
196 {
197 if (field.Type == TType.Bool)
198 {
199 _booleanField = field;
200 }
201 else
202 {
Jens Geyer5a17b132019-05-26 15:53:37 +0200203 await WriteFieldBeginInternalAsync(field, NoTypeOverride, cancellationToken);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100204 }
205 }
206
Jens Geyerdce22992020-05-16 23:02:27 +0200207 public override Task WriteFieldEndAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100208 {
Jens Geyerdce22992020-05-16 23:02:27 +0200209 cancellationToken.ThrowIfCancellationRequested();
210 return Task.CompletedTask;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100211 }
212
213 public override async Task WriteFieldStopAsync(CancellationToken cancellationToken)
214 {
Jens Geyerdce22992020-05-16 23:02:27 +0200215 cancellationToken.ThrowIfCancellationRequested();
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100216
Jens Geyer5a17b132019-05-26 15:53:37 +0200217 PreAllocatedBuffer[0] = Types.Stop;
218 await Trans.WriteAsync(PreAllocatedBuffer, 0, 1, cancellationToken);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100219 }
220
221 protected async Task WriteCollectionBeginAsync(TType elemType, int size, CancellationToken cancellationToken)
222 {
Jens Geyerdce22992020-05-16 23:02:27 +0200223 cancellationToken.ThrowIfCancellationRequested();
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100224
225 /*
226 Abstract method for writing the start of lists and sets. List and sets on
227 the wire differ only by the exType indicator.
228 */
229
230 if (size <= 14)
231 {
Jens Geyer5a17b132019-05-26 15:53:37 +0200232 PreAllocatedBuffer[0] = (byte)((size << 4) | GetCompactType(elemType));
233 await Trans.WriteAsync(PreAllocatedBuffer, 0, 1, cancellationToken);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100234 }
235 else
236 {
Jens Geyer5a17b132019-05-26 15:53:37 +0200237 PreAllocatedBuffer[0] = (byte)(0xf0 | GetCompactType(elemType));
238 await Trans.WriteAsync(PreAllocatedBuffer, 0, 1, cancellationToken);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100239
Jens Geyer5a17b132019-05-26 15:53:37 +0200240 Int32ToVarInt((uint) size, ref PreAllocatedVarInt);
241 await Trans.WriteAsync(PreAllocatedVarInt.bytes, 0, PreAllocatedVarInt.count, cancellationToken);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100242 }
243 }
244
245 public override async Task WriteListBeginAsync(TList list, CancellationToken cancellationToken)
246 {
247 await WriteCollectionBeginAsync(list.ElementType, list.Count, cancellationToken);
248 }
249
Jens Geyerdce22992020-05-16 23:02:27 +0200250 public override Task WriteListEndAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100251 {
Jens Geyerdce22992020-05-16 23:02:27 +0200252 cancellationToken.ThrowIfCancellationRequested();
253 return Task.CompletedTask;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100254 }
255
256 public override async Task WriteSetBeginAsync(TSet set, CancellationToken cancellationToken)
257 {
Jens Geyerdce22992020-05-16 23:02:27 +0200258 cancellationToken.ThrowIfCancellationRequested();
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100259
260 await WriteCollectionBeginAsync(set.ElementType, set.Count, cancellationToken);
261 }
262
Jens Geyerdce22992020-05-16 23:02:27 +0200263 public override Task WriteSetEndAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100264 {
Jens Geyerdce22992020-05-16 23:02:27 +0200265 cancellationToken.ThrowIfCancellationRequested();
266 return Task.CompletedTask;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100267 }
268
269 public override async Task WriteBoolAsync(bool b, CancellationToken cancellationToken)
270 {
Jens Geyerdce22992020-05-16 23:02:27 +0200271 cancellationToken.ThrowIfCancellationRequested();
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100272
273 /*
274 Write a boolean value. Potentially, this could be a boolean field, in
275 which case the field header info isn't written yet. If so, decide what the
276 right exType header is for the value and then Write the field header.
277 Otherwise, Write a single byte.
278 */
279
280 if (_booleanField != null)
281 {
282 // we haven't written the field header yet
Jens Geyer5a17b132019-05-26 15:53:37 +0200283 var type = b ? Types.BooleanTrue : Types.BooleanFalse;
284 await WriteFieldBeginInternalAsync(_booleanField.Value, type, cancellationToken);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100285 _booleanField = null;
286 }
287 else
288 {
Jens Geyer5a17b132019-05-26 15:53:37 +0200289 // we're not part of a field, so just write the value.
290 PreAllocatedBuffer[0] = b ? Types.BooleanTrue : Types.BooleanFalse;
291 await Trans.WriteAsync(PreAllocatedBuffer, 0, 1, cancellationToken);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100292 }
293 }
294
295 public override async Task WriteByteAsync(sbyte b, CancellationToken cancellationToken)
296 {
Jens Geyerdce22992020-05-16 23:02:27 +0200297 cancellationToken.ThrowIfCancellationRequested();
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100298
Jens Geyer5a17b132019-05-26 15:53:37 +0200299 PreAllocatedBuffer[0] = (byte)b;
300 await Trans.WriteAsync(PreAllocatedBuffer, 0, 1, cancellationToken);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100301 }
302
303 public override async Task WriteI16Async(short i16, CancellationToken cancellationToken)
304 {
Jens Geyerdce22992020-05-16 23:02:27 +0200305 cancellationToken.ThrowIfCancellationRequested();
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100306
Jens Geyer5a17b132019-05-26 15:53:37 +0200307 Int32ToVarInt(IntToZigzag(i16), ref PreAllocatedVarInt);
308 await Trans.WriteAsync(PreAllocatedVarInt.bytes, 0, PreAllocatedVarInt.count, cancellationToken);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100309 }
310
Jens Geyer5a17b132019-05-26 15:53:37 +0200311 private static void Int32ToVarInt(uint n, ref VarInt varint)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100312 {
Jens Geyer5a17b132019-05-26 15:53:37 +0200313 // Write an i32 as a varint. Results in 1 - 5 bytes on the wire.
314 varint.count = 0;
315 Debug.Assert(varint.bytes.Length >= 5);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100316
317 while (true)
318 {
319 if ((n & ~0x7F) == 0)
320 {
Jens Geyer5a17b132019-05-26 15:53:37 +0200321 varint.bytes[varint.count++] = (byte)n;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100322 break;
323 }
324
Jens Geyer5a17b132019-05-26 15:53:37 +0200325 varint.bytes[varint.count++] = (byte)((n & 0x7F) | 0x80);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100326 n >>= 7;
327 }
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100328 }
329
330 public override async Task WriteI32Async(int i32, CancellationToken cancellationToken)
331 {
Jens Geyerdce22992020-05-16 23:02:27 +0200332 cancellationToken.ThrowIfCancellationRequested();
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100333
Jens Geyer5a17b132019-05-26 15:53:37 +0200334 Int32ToVarInt(IntToZigzag(i32), ref PreAllocatedVarInt);
335 await Trans.WriteAsync(PreAllocatedVarInt.bytes, 0, PreAllocatedVarInt.count, cancellationToken);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100336 }
337
Jens Geyer5a17b132019-05-26 15:53:37 +0200338 static private void Int64ToVarInt(ulong n, ref VarInt varint)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100339 {
340 // Write an i64 as a varint. Results in 1-10 bytes on the wire.
Jens Geyer5a17b132019-05-26 15:53:37 +0200341 varint.count = 0;
342 Debug.Assert(varint.bytes.Length >= 10);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100343
344 while (true)
345 {
Jens Geyer5a17b132019-05-26 15:53:37 +0200346 if ((n & ~(ulong)0x7FL) == 0)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100347 {
Jens Geyer5a17b132019-05-26 15:53:37 +0200348 varint.bytes[varint.count++] = (byte)n;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100349 break;
350 }
Jens Geyer5a17b132019-05-26 15:53:37 +0200351 varint.bytes[varint.count++] = (byte)((n & 0x7F) | 0x80);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100352 n >>= 7;
353 }
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100354 }
355
356 public override async Task WriteI64Async(long i64, CancellationToken cancellationToken)
357 {
Jens Geyerdce22992020-05-16 23:02:27 +0200358 cancellationToken.ThrowIfCancellationRequested();
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100359
Jens Geyer5a17b132019-05-26 15:53:37 +0200360 Int64ToVarInt(LongToZigzag(i64), ref PreAllocatedVarInt);
361 await Trans.WriteAsync(PreAllocatedVarInt.bytes, 0, PreAllocatedVarInt.count, cancellationToken);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100362 }
363
364 public override async Task WriteDoubleAsync(double d, CancellationToken cancellationToken)
365 {
Jens Geyerdce22992020-05-16 23:02:27 +0200366 cancellationToken.ThrowIfCancellationRequested();
367
zembord9d958a32019-11-21 13:11:44 +0300368 BinaryPrimitives.WriteInt64LittleEndian(PreAllocatedBuffer, BitConverter.DoubleToInt64Bits(d));
Jens Geyer5a17b132019-05-26 15:53:37 +0200369 await Trans.WriteAsync(PreAllocatedBuffer, 0, 8, cancellationToken);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100370 }
371
372 public override async Task WriteStringAsync(string str, CancellationToken cancellationToken)
373 {
Jens Geyerdce22992020-05-16 23:02:27 +0200374 cancellationToken.ThrowIfCancellationRequested();
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100375
Mikel Blanchard4b66a9d2020-03-05 00:46:21 +0100376 var buf = ArrayPool<byte>.Shared.Rent(Encoding.UTF8.GetByteCount(str));
377 try
378 {
379 var numberOfBytes = Encoding.UTF8.GetBytes(str, 0, str.Length, buf, 0);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100380
Mikel Blanchard4b66a9d2020-03-05 00:46:21 +0100381 Int32ToVarInt((uint)numberOfBytes, ref PreAllocatedVarInt);
382 await Trans.WriteAsync(PreAllocatedVarInt.bytes, 0, PreAllocatedVarInt.count, cancellationToken);
383 await Trans.WriteAsync(buf, 0, numberOfBytes, cancellationToken);
384 }
385 finally
386 {
387 ArrayPool<byte>.Shared.Return(buf);
388 }
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100389 }
390
391 public override async Task WriteBinaryAsync(byte[] bytes, CancellationToken cancellationToken)
392 {
Jens Geyerdce22992020-05-16 23:02:27 +0200393 cancellationToken.ThrowIfCancellationRequested();
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100394
Jens Geyer5a17b132019-05-26 15:53:37 +0200395 Int32ToVarInt((uint) bytes.Length, ref PreAllocatedVarInt);
396 await Trans.WriteAsync(PreAllocatedVarInt.bytes, 0, PreAllocatedVarInt.count, cancellationToken);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100397 await Trans.WriteAsync(bytes, 0, bytes.Length, cancellationToken);
398 }
399
400 public override async Task WriteMapBeginAsync(TMap map, CancellationToken cancellationToken)
401 {
Jens Geyerdce22992020-05-16 23:02:27 +0200402 cancellationToken.ThrowIfCancellationRequested();
403
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100404 if (map.Count == 0)
405 {
Jens Geyer5a17b132019-05-26 15:53:37 +0200406 PreAllocatedBuffer[0] = 0;
407 await Trans.WriteAsync( PreAllocatedBuffer, 0, 1, cancellationToken);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100408 }
409 else
410 {
Jens Geyer5a17b132019-05-26 15:53:37 +0200411 Int32ToVarInt((uint) map.Count, ref PreAllocatedVarInt);
412 await Trans.WriteAsync(PreAllocatedVarInt.bytes, 0, PreAllocatedVarInt.count, cancellationToken);
413
414 PreAllocatedBuffer[0] = (byte)((GetCompactType(map.KeyType) << 4) | GetCompactType(map.ValueType));
415 await Trans.WriteAsync(PreAllocatedBuffer, 0, 1, cancellationToken);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100416 }
417 }
418
Jens Geyerdce22992020-05-16 23:02:27 +0200419 public override Task WriteMapEndAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100420 {
Jens Geyerdce22992020-05-16 23:02:27 +0200421 cancellationToken.ThrowIfCancellationRequested();
422 return Task.CompletedTask;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100423 }
424
Jens Geyer5a17b132019-05-26 15:53:37 +0200425 public override async ValueTask<TMessage> ReadMessageBeginAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100426 {
Jens Geyerdce22992020-05-16 23:02:27 +0200427 cancellationToken.ThrowIfCancellationRequested();
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100428
429 var protocolId = (byte) await ReadByteAsync(cancellationToken);
430 if (protocolId != ProtocolId)
431 {
432 throw new TProtocolException($"Expected protocol id {ProtocolId:X} but got {protocolId:X}");
433 }
434
435 var versionAndType = (byte) await ReadByteAsync(cancellationToken);
436 var version = (byte) (versionAndType & VersionMask);
437
438 if (version != Version)
439 {
440 throw new TProtocolException($"Expected version {Version} but got {version}");
441 }
442
443 var type = (byte) ((versionAndType >> TypeShiftAmount) & TypeBits);
444 var seqid = (int) await ReadVarInt32Async(cancellationToken);
445 var messageName = await ReadStringAsync(cancellationToken);
446
447 return new TMessage(messageName, (TMessageType) type, seqid);
448 }
449
Jens Geyerdce22992020-05-16 23:02:27 +0200450 public override Task ReadMessageEndAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100451 {
Jens Geyerdce22992020-05-16 23:02:27 +0200452 cancellationToken.ThrowIfCancellationRequested();
453 return Task.CompletedTask;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100454 }
455
Jens Geyerdce22992020-05-16 23:02:27 +0200456 public override ValueTask<TStruct> ReadStructBeginAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100457 {
Jens Geyerdce22992020-05-16 23:02:27 +0200458 cancellationToken.ThrowIfCancellationRequested();
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100459
460 _lastField.Push(_lastFieldId);
461 _lastFieldId = 0;
462
Jens Geyerdce22992020-05-16 23:02:27 +0200463 return new ValueTask<TStruct>(AnonymousStruct);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100464 }
465
Jens Geyerdce22992020-05-16 23:02:27 +0200466 public override Task ReadStructEndAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100467 {
Jens Geyerdce22992020-05-16 23:02:27 +0200468 cancellationToken.ThrowIfCancellationRequested();
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100469
470 /*
471 Doesn't actually consume any wire data, just removes the last field for
472 this struct from the field stack.
473 */
474
475 // consume the last field we Read off the wire.
476 _lastFieldId = _lastField.Pop();
Jens Geyerdce22992020-05-16 23:02:27 +0200477
478 return Task.CompletedTask;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100479 }
480
Jens Geyer5a17b132019-05-26 15:53:37 +0200481 public override async ValueTask<TField> ReadFieldBeginAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100482 {
483 // Read a field header off the wire.
484 var type = (byte) await ReadByteAsync(cancellationToken);
Jens Geyer5a17b132019-05-26 15:53:37 +0200485
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100486 // if it's a stop, then we can return immediately, as the struct is over.
487 if (type == Types.Stop)
488 {
Jens Geyer5a17b132019-05-26 15:53:37 +0200489 return StopField;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100490 }
491
Jens Geyer5a17b132019-05-26 15:53:37 +0200492
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100493 // mask off the 4 MSB of the exType header. it could contain a field id delta.
494 var modifier = (short) ((type & 0xf0) >> 4);
Jens Geyer5a17b132019-05-26 15:53:37 +0200495 var compactType = (byte)(type & 0x0f);
496
497 short fieldId;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100498 if (modifier == 0)
499 {
500 fieldId = await ReadI16Async(cancellationToken);
501 }
502 else
503 {
504 fieldId = (short) (_lastFieldId + modifier);
505 }
506
Jens Geyer5a17b132019-05-26 15:53:37 +0200507 var ttype = GetTType(compactType);
508 var field = new TField(string.Empty, ttype, fieldId);
509
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100510 // if this happens to be a boolean field, the value is encoded in the exType
Jens Geyer5a17b132019-05-26 15:53:37 +0200511 if( ttype == TType.Bool)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100512 {
Jens Geyer5a17b132019-05-26 15:53:37 +0200513 _boolValue = (compactType == Types.BooleanTrue);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100514 }
515
516 // push the new field onto the field stack so we can keep the deltas going.
517 _lastFieldId = field.ID;
518 return field;
519 }
520
Jens Geyerdce22992020-05-16 23:02:27 +0200521 public override Task ReadFieldEndAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100522 {
Jens Geyerdce22992020-05-16 23:02:27 +0200523 cancellationToken.ThrowIfCancellationRequested();
524 return Task.CompletedTask;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100525 }
526
Jens Geyer5a17b132019-05-26 15:53:37 +0200527 public override async ValueTask<TMap> ReadMapBeginAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100528 {
Jens Geyerdce22992020-05-16 23:02:27 +0200529 cancellationToken.ThrowIfCancellationRequested();
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100530
531 /*
532 Read a map header off the wire. If the size is zero, skip Reading the key
533 and value exType. This means that 0-length maps will yield TMaps without the
534 "correct" types.
535 */
536
537 var size = (int) await ReadVarInt32Async(cancellationToken);
538 var keyAndValueType = size == 0 ? (byte) 0 : (byte) await ReadByteAsync(cancellationToken);
Jens Geyer50806452019-11-23 01:55:58 +0100539 var map = new TMap(GetTType((byte) (keyAndValueType >> 4)), GetTType((byte) (keyAndValueType & 0xf)), size);
540 CheckReadBytesAvailable(map);
541 return map;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100542 }
543
Jens Geyerdce22992020-05-16 23:02:27 +0200544 public override Task ReadMapEndAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100545 {
Jens Geyerdce22992020-05-16 23:02:27 +0200546 cancellationToken.ThrowIfCancellationRequested();
547 return Task.CompletedTask;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100548 }
549
Jens Geyer5a17b132019-05-26 15:53:37 +0200550 public override async ValueTask<TSet> ReadSetBeginAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100551 {
552 /*
553 Read a set header off the wire. If the set size is 0-14, the size will
554 be packed into the element exType header. If it's a longer set, the 4 MSB
555 of the element exType header will be 0xF, and a varint will follow with the
556 true size.
557 */
558
559 return new TSet(await ReadListBeginAsync(cancellationToken));
560 }
561
Jens Geyer5a17b132019-05-26 15:53:37 +0200562 public override ValueTask<bool> ReadBoolAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100563 {
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100564 /*
565 Read a boolean off the wire. If this is a boolean field, the value should
566 already have been Read during ReadFieldBegin, so we'll just consume the
567 pre-stored value. Otherwise, Read a byte.
568 */
569
570 if (_boolValue != null)
571 {
572 var result = _boolValue.Value;
573 _boolValue = null;
Jens Geyer5a17b132019-05-26 15:53:37 +0200574 return new ValueTask<bool>(result);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100575 }
576
Jens Geyer5a17b132019-05-26 15:53:37 +0200577 return InternalCall();
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100578
Jens Geyer5a17b132019-05-26 15:53:37 +0200579 async ValueTask<bool> InternalCall()
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100580 {
Jens Geyer5a17b132019-05-26 15:53:37 +0200581 var data = await ReadByteAsync(cancellationToken);
582 return (data == Types.BooleanTrue);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100583 }
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100584 }
585
Jens Geyer5a17b132019-05-26 15:53:37 +0200586
587 public override async ValueTask<sbyte> ReadByteAsync(CancellationToken cancellationToken)
588 {
589 // Read a single byte off the wire. Nothing interesting here.
590 await Trans.ReadAllAsync(PreAllocatedBuffer, 0, 1, cancellationToken);
591 return (sbyte)PreAllocatedBuffer[0];
592 }
593
594 public override async ValueTask<short> ReadI16Async(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100595 {
Jens Geyerdce22992020-05-16 23:02:27 +0200596 cancellationToken.ThrowIfCancellationRequested();
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100597
598 return (short) ZigzagToInt(await ReadVarInt32Async(cancellationToken));
599 }
600
Jens Geyer5a17b132019-05-26 15:53:37 +0200601 public override async ValueTask<int> ReadI32Async(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100602 {
Jens Geyerdce22992020-05-16 23:02:27 +0200603 cancellationToken.ThrowIfCancellationRequested();
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100604
605 return ZigzagToInt(await ReadVarInt32Async(cancellationToken));
606 }
607
Jens Geyer5a17b132019-05-26 15:53:37 +0200608 public override async ValueTask<long> ReadI64Async(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100609 {
Jens Geyerdce22992020-05-16 23:02:27 +0200610 cancellationToken.ThrowIfCancellationRequested();
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100611
612 return ZigzagToLong(await ReadVarInt64Async(cancellationToken));
613 }
614
Jens Geyer5a17b132019-05-26 15:53:37 +0200615 public override async ValueTask<double> ReadDoubleAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100616 {
Jens Geyerdce22992020-05-16 23:02:27 +0200617 cancellationToken.ThrowIfCancellationRequested();
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100618
Jens Geyer5a17b132019-05-26 15:53:37 +0200619 await Trans.ReadAllAsync(PreAllocatedBuffer, 0, 8, cancellationToken);
zembord9d958a32019-11-21 13:11:44 +0300620
621 return BitConverter.Int64BitsToDouble(BinaryPrimitives.ReadInt64LittleEndian(PreAllocatedBuffer));
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100622 }
623
Jens Geyer5a17b132019-05-26 15:53:37 +0200624 public override async ValueTask<string> ReadStringAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100625 {
Jens Geyer5a17b132019-05-26 15:53:37 +0200626 // read length
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100627 var length = (int) await ReadVarInt32Async(cancellationToken);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100628 if (length == 0)
629 {
630 return string.Empty;
631 }
632
Jens Geyer5a17b132019-05-26 15:53:37 +0200633 // read and decode data
634 if (length < PreAllocatedBuffer.Length)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100635 {
Jens Geyer5a17b132019-05-26 15:53:37 +0200636 await Trans.ReadAllAsync(PreAllocatedBuffer, 0, length, cancellationToken);
637 return Encoding.UTF8.GetString(PreAllocatedBuffer, 0, length);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100638 }
639
Jens Geyer50806452019-11-23 01:55:58 +0100640 Transport.CheckReadBytesAvailable(length);
Mikel Blanchard4b66a9d2020-03-05 00:46:21 +0100641
642 var buf = ArrayPool<byte>.Shared.Rent(length);
643 try
644 {
645 await Trans.ReadAllAsync(buf, 0, length, cancellationToken);
646 return Encoding.UTF8.GetString(buf, 0, length);
647 }
648 finally
649 {
650 ArrayPool<byte>.Shared.Return(buf);
651 }
Jens Geyer5a17b132019-05-26 15:53:37 +0200652 }
653
654 public override async ValueTask<byte[]> ReadBinaryAsync(CancellationToken cancellationToken)
655 {
656 // read length
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100657 var length = (int) await ReadVarInt32Async(cancellationToken);
658 if (length == 0)
659 {
Mikel Blanchard4b66a9d2020-03-05 00:46:21 +0100660 return Array.Empty<byte>();
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100661 }
662
Jens Geyer5a17b132019-05-26 15:53:37 +0200663 // read data
Jens Geyer50806452019-11-23 01:55:58 +0100664 Transport.CheckReadBytesAvailable(length);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100665 var buf = new byte[length];
666 await Trans.ReadAllAsync(buf, 0, length, cancellationToken);
667 return buf;
668 }
669
Jens Geyer5a17b132019-05-26 15:53:37 +0200670 public override async ValueTask<TList> ReadListBeginAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100671 {
Jens Geyerdce22992020-05-16 23:02:27 +0200672 cancellationToken.ThrowIfCancellationRequested();
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100673
674 /*
675 Read a list header off the wire. If the list size is 0-14, the size will
676 be packed into the element exType header. If it's a longer list, the 4 MSB
677 of the element exType header will be 0xF, and a varint will follow with the
678 true size.
679 */
680
681 var sizeAndType = (byte) await ReadByteAsync(cancellationToken);
682 var size = (sizeAndType >> 4) & 0x0f;
683 if (size == 15)
684 {
685 size = (int) await ReadVarInt32Async(cancellationToken);
686 }
687
688 var type = GetTType(sizeAndType);
Jens Geyer50806452019-11-23 01:55:58 +0100689 var list = new TList(type, size);
690 CheckReadBytesAvailable(list);
691 return list;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100692 }
693
Jens Geyerdce22992020-05-16 23:02:27 +0200694 public override Task ReadListEndAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100695 {
Jens Geyerdce22992020-05-16 23:02:27 +0200696 cancellationToken.ThrowIfCancellationRequested();
697 return Task.CompletedTask;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100698 }
699
Jens Geyerdce22992020-05-16 23:02:27 +0200700 public override Task ReadSetEndAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100701 {
Jens Geyerdce22992020-05-16 23:02:27 +0200702 cancellationToken.ThrowIfCancellationRequested();
703 return Task.CompletedTask;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100704 }
705
706 private static byte GetCompactType(TType ttype)
707 {
708 // Given a TType value, find the appropriate TCompactProtocol.Types constant.
709 return TTypeToCompactType[(int) ttype];
710 }
711
712
Jens Geyer5a17b132019-05-26 15:53:37 +0200713 private async ValueTask<uint> ReadVarInt32Async(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100714 {
Jens Geyerdce22992020-05-16 23:02:27 +0200715 cancellationToken.ThrowIfCancellationRequested();
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100716
717 /*
718 Read an i32 from the wire as a varint. The MSB of each byte is set
719 if there is another byte to follow. This can Read up to 5 bytes.
720 */
721
722 uint result = 0;
723 var shift = 0;
724
725 while (true)
726 {
727 var b = (byte) await ReadByteAsync(cancellationToken);
728 result |= (uint) (b & 0x7f) << shift;
729 if ((b & 0x80) != 0x80)
730 {
731 break;
732 }
733 shift += 7;
734 }
735
736 return result;
737 }
738
Jens Geyer5a17b132019-05-26 15:53:37 +0200739 private async ValueTask<ulong> ReadVarInt64Async(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100740 {
Jens Geyerdce22992020-05-16 23:02:27 +0200741 cancellationToken.ThrowIfCancellationRequested();
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100742
743 /*
744 Read an i64 from the wire as a proper varint. The MSB of each byte is set
745 if there is another byte to follow. This can Read up to 10 bytes.
746 */
747
748 var shift = 0;
749 ulong result = 0;
750 while (true)
751 {
752 var b = (byte) await ReadByteAsync(cancellationToken);
753 result |= (ulong) (b & 0x7f) << shift;
754 if ((b & 0x80) != 0x80)
755 {
756 break;
757 }
758 shift += 7;
759 }
760
761 return result;
762 }
763
764 private static int ZigzagToInt(uint n)
765 {
766 return (int) (n >> 1) ^ -(int) (n & 1);
767 }
768
769 private static long ZigzagToLong(ulong n)
770 {
771 return (long) (n >> 1) ^ -(long) (n & 1);
772 }
773
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100774 private static TType GetTType(byte type)
775 {
776 // Given a TCompactProtocol.Types constant, convert it to its corresponding TType value.
Jens Geyer5a17b132019-05-26 15:53:37 +0200777 return CompactTypeToTType[type & 0x0f];
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100778 }
779
780 private static ulong LongToZigzag(long n)
781 {
782 // Convert l into a zigzag long. This allows negative numbers to be represented compactly as a varint
783 return (ulong) (n << 1) ^ (ulong) (n >> 63);
784 }
785
786 private static uint IntToZigzag(int n)
787 {
788 // Convert n into a zigzag int. This allows negative numbers to be represented compactly as a varint
789 return (uint) (n << 1) ^ (uint) (n >> 31);
790 }
791
Jens Geyer50806452019-11-23 01:55:58 +0100792 // Return the minimum number of bytes a type will consume on the wire
793 public override int GetMinSerializedSize(TType type)
794 {
795 switch (type)
796 {
797 case TType.Stop: return 0;
798 case TType.Void: return 0;
799 case TType.Bool: return sizeof(byte);
800 case TType.Double: return 8; // uses fixedLongToBytes() which always writes 8 bytes
801 case TType.Byte: return sizeof(byte);
802 case TType.I16: return sizeof(byte); // zigzag
803 case TType.I32: return sizeof(byte); // zigzag
804 case TType.I64: return sizeof(byte); // zigzag
805 case TType.String: return sizeof(byte); // string length
806 case TType.Struct: return 0; // empty struct
807 case TType.Map: return sizeof(byte); // element count
808 case TType.Set: return sizeof(byte); // element count
809 case TType.List: return sizeof(byte); // element count
Jens Geyer0d128322021-02-25 09:42:52 +0100810 default: throw new TProtocolException(TProtocolException.NOT_IMPLEMENTED, "unrecognized type code");
Jens Geyer50806452019-11-23 01:55:58 +0100811 }
812 }
813
Jens Geyer421444f2019-03-20 22:13:25 +0100814 public class Factory : TProtocolFactory
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100815 {
Jens Geyer421444f2019-03-20 22:13:25 +0100816 public override TProtocol GetProtocol(TTransport trans)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100817 {
818 return new TCompactProtocol(trans);
819 }
820 }
821
822 /// <summary>
823 /// All of the on-wire exType codes.
824 /// </summary>
825 private static class Types
826 {
827 public const byte Stop = 0x00;
828 public const byte BooleanTrue = 0x01;
829 public const byte BooleanFalse = 0x02;
830 public const byte Byte = 0x03;
831 public const byte I16 = 0x04;
832 public const byte I32 = 0x05;
833 public const byte I64 = 0x06;
834 public const byte Double = 0x07;
835 public const byte Binary = 0x08;
836 public const byte List = 0x09;
837 public const byte Set = 0x0A;
838 public const byte Map = 0x0B;
839 public const byte Struct = 0x0C;
840 }
841 }
Jens Geyer421444f2019-03-20 22:13:25 +0100842}