blob: 066b327227c87d9752dc204a83df82c6966e240f [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
29namespace Thrift.Protocol
30{
31 //TODO: implementation of TProtocol
32
33 // ReSharper disable once InconsistentNaming
34 public class TCompactProtocol : TProtocol
35 {
36 private const byte ProtocolId = 0x82;
37 private const byte Version = 1;
38 private const byte VersionMask = 0x1f; // 0001 1111
39 private const byte TypeMask = 0xE0; // 1110 0000
40 private const byte TypeBits = 0x07; // 0000 0111
41 private const int TypeShiftAmount = 5;
Jens Geyer5a17b132019-05-26 15:53:37 +020042
43 private const byte NoTypeOverride = 0xFF;
Jens Geyeraa0c8b32019-01-28 23:27:45 +010044
45 // ReSharper disable once InconsistentNaming
46 private static readonly byte[] TTypeToCompactType = new byte[16];
Jens Geyer5a17b132019-05-26 15:53:37 +020047 private static readonly TType[] CompactTypeToTType = new TType[13];
Jens Geyeraa0c8b32019-01-28 23:27:45 +010048
49 /// <summary>
50 /// Used to keep track of the last field for the current and previous structs, so we can do the delta stuff.
51 /// </summary>
52 private readonly Stack<short> _lastField = new Stack<short>(15);
53
54 /// <summary>
55 /// If we encounter a boolean field begin, save the TField here so it can have the value incorporated.
56 /// </summary>
57 private TField? _booleanField;
58
59 /// <summary>
60 /// If we Read a field header, and it's a boolean field, save the boolean value here so that ReadBool can use it.
61 /// </summary>
62 private bool? _boolValue;
63
64 private short _lastFieldId;
65
Jens Geyer5a17b132019-05-26 15:53:37 +020066 // minimize memory allocations by means of an preallocated bytes buffer
67 // The value of 128 is arbitrarily chosen, the required minimum size must be sizeof(long)
Mikel Blanchard4b66a9d2020-03-05 00:46:21 +010068 private readonly byte[] PreAllocatedBuffer = new byte[128];
Jens Geyer5a17b132019-05-26 15:53:37 +020069
70 private struct VarInt
71 {
72 public byte[] bytes;
73 public int count;
74 }
75
76 // minimize memory allocations by means of an preallocated VarInt buffer
77 private VarInt PreAllocatedVarInt = new VarInt()
78 {
79 bytes = new byte[10], // see Int64ToVarInt()
80 count = 0
81 };
82
83
84
85
Jens Geyeraa0c8b32019-01-28 23:27:45 +010086 public TCompactProtocol(TTransport trans)
87 : base(trans)
88 {
89 TTypeToCompactType[(int) TType.Stop] = Types.Stop;
90 TTypeToCompactType[(int) TType.Bool] = Types.BooleanTrue;
91 TTypeToCompactType[(int) TType.Byte] = Types.Byte;
92 TTypeToCompactType[(int) TType.I16] = Types.I16;
93 TTypeToCompactType[(int) TType.I32] = Types.I32;
94 TTypeToCompactType[(int) TType.I64] = Types.I64;
95 TTypeToCompactType[(int) TType.Double] = Types.Double;
96 TTypeToCompactType[(int) TType.String] = Types.Binary;
97 TTypeToCompactType[(int) TType.List] = Types.List;
98 TTypeToCompactType[(int) TType.Set] = Types.Set;
99 TTypeToCompactType[(int) TType.Map] = Types.Map;
100 TTypeToCompactType[(int) TType.Struct] = Types.Struct;
Jens Geyer5a17b132019-05-26 15:53:37 +0200101
102 CompactTypeToTType[Types.Stop] = TType.Stop;
103 CompactTypeToTType[Types.BooleanTrue] = TType.Bool;
104 CompactTypeToTType[Types.BooleanFalse] = TType.Bool;
105 CompactTypeToTType[Types.Byte] = TType.Byte;
106 CompactTypeToTType[Types.I16] = TType.I16;
107 CompactTypeToTType[Types.I32] = TType.I32;
108 CompactTypeToTType[Types.I64] = TType.I64;
109 CompactTypeToTType[Types.Double] = TType.Double;
110 CompactTypeToTType[Types.Binary] = TType.String;
111 CompactTypeToTType[Types.List] = TType.List;
112 CompactTypeToTType[Types.Set] = TType.Set;
113 CompactTypeToTType[Types.Map] = TType.Map;
114 CompactTypeToTType[Types.Struct] = TType.Struct;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100115 }
116
117 public void Reset()
118 {
119 _lastField.Clear();
120 _lastFieldId = 0;
121 }
122
123 public override async Task WriteMessageBeginAsync(TMessage message, CancellationToken cancellationToken)
124 {
Jens Geyer5a17b132019-05-26 15:53:37 +0200125 PreAllocatedBuffer[0] = ProtocolId;
126 PreAllocatedBuffer[1] = (byte)((Version & VersionMask) | (((uint)message.Type << TypeShiftAmount) & TypeMask));
127 await Trans.WriteAsync(PreAllocatedBuffer, 0, 2, cancellationToken);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100128
Jens Geyer5a17b132019-05-26 15:53:37 +0200129 Int32ToVarInt((uint) message.SeqID, ref PreAllocatedVarInt);
130 await Trans.WriteAsync(PreAllocatedVarInt.bytes, 0, PreAllocatedVarInt.count, cancellationToken);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100131
132 await WriteStringAsync(message.Name, cancellationToken);
133 }
134
Jens Geyerdce22992020-05-16 23:02:27 +0200135 public override Task WriteMessageEndAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100136 {
Jens Geyerdce22992020-05-16 23:02:27 +0200137 cancellationToken.ThrowIfCancellationRequested();
138 return Task.CompletedTask;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100139 }
140
141 /// <summary>
142 /// Write a struct begin. This doesn't actually put anything on the wire. We
143 /// use it as an opportunity to put special placeholder markers on the field
144 /// stack so we can get the field id deltas correct.
145 /// </summary>
Jens Geyerdce22992020-05-16 23:02:27 +0200146 public override Task WriteStructBeginAsync(TStruct @struct, CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100147 {
Jens Geyerdce22992020-05-16 23:02:27 +0200148 cancellationToken.ThrowIfCancellationRequested();
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100149
150 _lastField.Push(_lastFieldId);
151 _lastFieldId = 0;
Jens Geyerdce22992020-05-16 23:02:27 +0200152
153 return Task.CompletedTask;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100154 }
155
Jens Geyerdce22992020-05-16 23:02:27 +0200156 public override Task WriteStructEndAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100157 {
Jens Geyerdce22992020-05-16 23:02:27 +0200158 cancellationToken.ThrowIfCancellationRequested();
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100159
160 _lastFieldId = _lastField.Pop();
Jens Geyerdce22992020-05-16 23:02:27 +0200161
162 return Task.CompletedTask;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100163 }
164
Jens Geyer5a17b132019-05-26 15:53:37 +0200165 private async Task WriteFieldBeginInternalAsync(TField field, byte fieldType, CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100166 {
Jens Geyer5a17b132019-05-26 15:53:37 +0200167 // if there's a exType override passed in, use that. Otherwise ask GetCompactType().
168 if (fieldType == NoTypeOverride)
169 fieldType = GetCompactType(field.Type);
170
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100171
172 // check if we can use delta encoding for the field id
Jens Geyer5a17b132019-05-26 15:53:37 +0200173 if (field.ID > _lastFieldId)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100174 {
Jens Geyer5a17b132019-05-26 15:53:37 +0200175 var delta = field.ID - _lastFieldId;
176 if (delta <= 15)
177 {
178 // Write them together
179 PreAllocatedBuffer[0] = (byte)((delta << 4) | fieldType);
180 await Trans.WriteAsync(PreAllocatedBuffer, 0, 1, cancellationToken);
181 _lastFieldId = field.ID;
182 return;
183 }
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100184 }
185
Jens Geyer5a17b132019-05-26 15:53:37 +0200186 // Write them separate
187 PreAllocatedBuffer[0] = fieldType;
188 await Trans.WriteAsync(PreAllocatedBuffer, 0, 1, cancellationToken);
189 await WriteI16Async(field.ID, cancellationToken);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100190 _lastFieldId = field.ID;
191 }
192
193 public override async Task WriteFieldBeginAsync(TField field, CancellationToken cancellationToken)
194 {
195 if (field.Type == TType.Bool)
196 {
197 _booleanField = field;
198 }
199 else
200 {
Jens Geyer5a17b132019-05-26 15:53:37 +0200201 await WriteFieldBeginInternalAsync(field, NoTypeOverride, cancellationToken);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100202 }
203 }
204
Jens Geyerdce22992020-05-16 23:02:27 +0200205 public override Task WriteFieldEndAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100206 {
Jens Geyerdce22992020-05-16 23:02:27 +0200207 cancellationToken.ThrowIfCancellationRequested();
208 return Task.CompletedTask;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100209 }
210
211 public override async Task WriteFieldStopAsync(CancellationToken cancellationToken)
212 {
Jens Geyerdce22992020-05-16 23:02:27 +0200213 cancellationToken.ThrowIfCancellationRequested();
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100214
Jens Geyer5a17b132019-05-26 15:53:37 +0200215 PreAllocatedBuffer[0] = Types.Stop;
216 await Trans.WriteAsync(PreAllocatedBuffer, 0, 1, cancellationToken);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100217 }
218
219 protected async Task WriteCollectionBeginAsync(TType elemType, int size, CancellationToken cancellationToken)
220 {
Jens Geyerdce22992020-05-16 23:02:27 +0200221 cancellationToken.ThrowIfCancellationRequested();
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100222
223 /*
224 Abstract method for writing the start of lists and sets. List and sets on
225 the wire differ only by the exType indicator.
226 */
227
228 if (size <= 14)
229 {
Jens Geyer5a17b132019-05-26 15:53:37 +0200230 PreAllocatedBuffer[0] = (byte)((size << 4) | GetCompactType(elemType));
231 await Trans.WriteAsync(PreAllocatedBuffer, 0, 1, cancellationToken);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100232 }
233 else
234 {
Jens Geyer5a17b132019-05-26 15:53:37 +0200235 PreAllocatedBuffer[0] = (byte)(0xf0 | GetCompactType(elemType));
236 await Trans.WriteAsync(PreAllocatedBuffer, 0, 1, cancellationToken);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100237
Jens Geyer5a17b132019-05-26 15:53:37 +0200238 Int32ToVarInt((uint) size, ref PreAllocatedVarInt);
239 await Trans.WriteAsync(PreAllocatedVarInt.bytes, 0, PreAllocatedVarInt.count, cancellationToken);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100240 }
241 }
242
243 public override async Task WriteListBeginAsync(TList list, CancellationToken cancellationToken)
244 {
245 await WriteCollectionBeginAsync(list.ElementType, list.Count, cancellationToken);
246 }
247
Jens Geyerdce22992020-05-16 23:02:27 +0200248 public override Task WriteListEndAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100249 {
Jens Geyerdce22992020-05-16 23:02:27 +0200250 cancellationToken.ThrowIfCancellationRequested();
251 return Task.CompletedTask;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100252 }
253
254 public override async Task WriteSetBeginAsync(TSet set, CancellationToken cancellationToken)
255 {
Jens Geyerdce22992020-05-16 23:02:27 +0200256 cancellationToken.ThrowIfCancellationRequested();
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100257
258 await WriteCollectionBeginAsync(set.ElementType, set.Count, cancellationToken);
259 }
260
Jens Geyerdce22992020-05-16 23:02:27 +0200261 public override Task WriteSetEndAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100262 {
Jens Geyerdce22992020-05-16 23:02:27 +0200263 cancellationToken.ThrowIfCancellationRequested();
264 return Task.CompletedTask;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100265 }
266
267 public override async Task WriteBoolAsync(bool b, CancellationToken cancellationToken)
268 {
Jens Geyerdce22992020-05-16 23:02:27 +0200269 cancellationToken.ThrowIfCancellationRequested();
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100270
271 /*
272 Write a boolean value. Potentially, this could be a boolean field, in
273 which case the field header info isn't written yet. If so, decide what the
274 right exType header is for the value and then Write the field header.
275 Otherwise, Write a single byte.
276 */
277
278 if (_booleanField != null)
279 {
280 // we haven't written the field header yet
Jens Geyer5a17b132019-05-26 15:53:37 +0200281 var type = b ? Types.BooleanTrue : Types.BooleanFalse;
282 await WriteFieldBeginInternalAsync(_booleanField.Value, type, cancellationToken);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100283 _booleanField = null;
284 }
285 else
286 {
Jens Geyer5a17b132019-05-26 15:53:37 +0200287 // we're not part of a field, so just write the value.
288 PreAllocatedBuffer[0] = b ? Types.BooleanTrue : Types.BooleanFalse;
289 await Trans.WriteAsync(PreAllocatedBuffer, 0, 1, cancellationToken);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100290 }
291 }
292
293 public override async Task WriteByteAsync(sbyte b, CancellationToken cancellationToken)
294 {
Jens Geyerdce22992020-05-16 23:02:27 +0200295 cancellationToken.ThrowIfCancellationRequested();
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100296
Jens Geyer5a17b132019-05-26 15:53:37 +0200297 PreAllocatedBuffer[0] = (byte)b;
298 await Trans.WriteAsync(PreAllocatedBuffer, 0, 1, cancellationToken);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100299 }
300
301 public override async Task WriteI16Async(short i16, CancellationToken cancellationToken)
302 {
Jens Geyerdce22992020-05-16 23:02:27 +0200303 cancellationToken.ThrowIfCancellationRequested();
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100304
Jens Geyer5a17b132019-05-26 15:53:37 +0200305 Int32ToVarInt(IntToZigzag(i16), ref PreAllocatedVarInt);
306 await Trans.WriteAsync(PreAllocatedVarInt.bytes, 0, PreAllocatedVarInt.count, cancellationToken);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100307 }
308
Jens Geyer5a17b132019-05-26 15:53:37 +0200309 private static void Int32ToVarInt(uint n, ref VarInt varint)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100310 {
Jens Geyer5a17b132019-05-26 15:53:37 +0200311 // Write an i32 as a varint. Results in 1 - 5 bytes on the wire.
312 varint.count = 0;
313 Debug.Assert(varint.bytes.Length >= 5);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100314
315 while (true)
316 {
317 if ((n & ~0x7F) == 0)
318 {
Jens Geyer5a17b132019-05-26 15:53:37 +0200319 varint.bytes[varint.count++] = (byte)n;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100320 break;
321 }
322
Jens Geyer5a17b132019-05-26 15:53:37 +0200323 varint.bytes[varint.count++] = (byte)((n & 0x7F) | 0x80);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100324 n >>= 7;
325 }
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100326 }
327
328 public override async Task WriteI32Async(int i32, CancellationToken cancellationToken)
329 {
Jens Geyerdce22992020-05-16 23:02:27 +0200330 cancellationToken.ThrowIfCancellationRequested();
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100331
Jens Geyer5a17b132019-05-26 15:53:37 +0200332 Int32ToVarInt(IntToZigzag(i32), ref PreAllocatedVarInt);
333 await Trans.WriteAsync(PreAllocatedVarInt.bytes, 0, PreAllocatedVarInt.count, cancellationToken);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100334 }
335
Jens Geyer5a17b132019-05-26 15:53:37 +0200336 static private void Int64ToVarInt(ulong n, ref VarInt varint)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100337 {
338 // Write an i64 as a varint. Results in 1-10 bytes on the wire.
Jens Geyer5a17b132019-05-26 15:53:37 +0200339 varint.count = 0;
340 Debug.Assert(varint.bytes.Length >= 10);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100341
342 while (true)
343 {
Jens Geyer5a17b132019-05-26 15:53:37 +0200344 if ((n & ~(ulong)0x7FL) == 0)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100345 {
Jens Geyer5a17b132019-05-26 15:53:37 +0200346 varint.bytes[varint.count++] = (byte)n;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100347 break;
348 }
Jens Geyer5a17b132019-05-26 15:53:37 +0200349 varint.bytes[varint.count++] = (byte)((n & 0x7F) | 0x80);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100350 n >>= 7;
351 }
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100352 }
353
354 public override async Task WriteI64Async(long i64, CancellationToken cancellationToken)
355 {
Jens Geyerdce22992020-05-16 23:02:27 +0200356 cancellationToken.ThrowIfCancellationRequested();
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100357
Jens Geyer5a17b132019-05-26 15:53:37 +0200358 Int64ToVarInt(LongToZigzag(i64), ref PreAllocatedVarInt);
359 await Trans.WriteAsync(PreAllocatedVarInt.bytes, 0, PreAllocatedVarInt.count, cancellationToken);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100360 }
361
362 public override async Task WriteDoubleAsync(double d, CancellationToken cancellationToken)
363 {
Jens Geyerdce22992020-05-16 23:02:27 +0200364 cancellationToken.ThrowIfCancellationRequested();
365
zembord9d958a32019-11-21 13:11:44 +0300366 BinaryPrimitives.WriteInt64LittleEndian(PreAllocatedBuffer, BitConverter.DoubleToInt64Bits(d));
Jens Geyer5a17b132019-05-26 15:53:37 +0200367 await Trans.WriteAsync(PreAllocatedBuffer, 0, 8, cancellationToken);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100368 }
369
370 public override async Task WriteStringAsync(string str, CancellationToken cancellationToken)
371 {
Jens Geyerdce22992020-05-16 23:02:27 +0200372 cancellationToken.ThrowIfCancellationRequested();
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100373
Mikel Blanchard4b66a9d2020-03-05 00:46:21 +0100374 var buf = ArrayPool<byte>.Shared.Rent(Encoding.UTF8.GetByteCount(str));
375 try
376 {
377 var numberOfBytes = Encoding.UTF8.GetBytes(str, 0, str.Length, buf, 0);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100378
Mikel Blanchard4b66a9d2020-03-05 00:46:21 +0100379 Int32ToVarInt((uint)numberOfBytes, ref PreAllocatedVarInt);
380 await Trans.WriteAsync(PreAllocatedVarInt.bytes, 0, PreAllocatedVarInt.count, cancellationToken);
381 await Trans.WriteAsync(buf, 0, numberOfBytes, cancellationToken);
382 }
383 finally
384 {
385 ArrayPool<byte>.Shared.Return(buf);
386 }
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100387 }
388
389 public override async Task WriteBinaryAsync(byte[] bytes, CancellationToken cancellationToken)
390 {
Jens Geyerdce22992020-05-16 23:02:27 +0200391 cancellationToken.ThrowIfCancellationRequested();
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100392
Jens Geyer5a17b132019-05-26 15:53:37 +0200393 Int32ToVarInt((uint) bytes.Length, ref PreAllocatedVarInt);
394 await Trans.WriteAsync(PreAllocatedVarInt.bytes, 0, PreAllocatedVarInt.count, cancellationToken);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100395 await Trans.WriteAsync(bytes, 0, bytes.Length, cancellationToken);
396 }
397
398 public override async Task WriteMapBeginAsync(TMap map, CancellationToken cancellationToken)
399 {
Jens Geyerdce22992020-05-16 23:02:27 +0200400 cancellationToken.ThrowIfCancellationRequested();
401
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100402 if (map.Count == 0)
403 {
Jens Geyer5a17b132019-05-26 15:53:37 +0200404 PreAllocatedBuffer[0] = 0;
405 await Trans.WriteAsync( PreAllocatedBuffer, 0, 1, cancellationToken);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100406 }
407 else
408 {
Jens Geyer5a17b132019-05-26 15:53:37 +0200409 Int32ToVarInt((uint) map.Count, ref PreAllocatedVarInt);
410 await Trans.WriteAsync(PreAllocatedVarInt.bytes, 0, PreAllocatedVarInt.count, cancellationToken);
411
412 PreAllocatedBuffer[0] = (byte)((GetCompactType(map.KeyType) << 4) | GetCompactType(map.ValueType));
413 await Trans.WriteAsync(PreAllocatedBuffer, 0, 1, cancellationToken);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100414 }
415 }
416
Jens Geyerdce22992020-05-16 23:02:27 +0200417 public override Task WriteMapEndAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100418 {
Jens Geyerdce22992020-05-16 23:02:27 +0200419 cancellationToken.ThrowIfCancellationRequested();
420 return Task.CompletedTask;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100421 }
422
Jens Geyer5a17b132019-05-26 15:53:37 +0200423 public override async ValueTask<TMessage> ReadMessageBeginAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100424 {
Jens Geyerdce22992020-05-16 23:02:27 +0200425 cancellationToken.ThrowIfCancellationRequested();
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100426
427 var protocolId = (byte) await ReadByteAsync(cancellationToken);
428 if (protocolId != ProtocolId)
429 {
430 throw new TProtocolException($"Expected protocol id {ProtocolId:X} but got {protocolId:X}");
431 }
432
433 var versionAndType = (byte) await ReadByteAsync(cancellationToken);
434 var version = (byte) (versionAndType & VersionMask);
435
436 if (version != Version)
437 {
438 throw new TProtocolException($"Expected version {Version} but got {version}");
439 }
440
441 var type = (byte) ((versionAndType >> TypeShiftAmount) & TypeBits);
442 var seqid = (int) await ReadVarInt32Async(cancellationToken);
443 var messageName = await ReadStringAsync(cancellationToken);
444
445 return new TMessage(messageName, (TMessageType) type, seqid);
446 }
447
Jens Geyerdce22992020-05-16 23:02:27 +0200448 public override Task ReadMessageEndAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100449 {
Jens Geyerdce22992020-05-16 23:02:27 +0200450 cancellationToken.ThrowIfCancellationRequested();
451 return Task.CompletedTask;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100452 }
453
Jens Geyerdce22992020-05-16 23:02:27 +0200454 public override ValueTask<TStruct> ReadStructBeginAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100455 {
Jens Geyerdce22992020-05-16 23:02:27 +0200456 cancellationToken.ThrowIfCancellationRequested();
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100457
458 _lastField.Push(_lastFieldId);
459 _lastFieldId = 0;
460
Jens Geyerdce22992020-05-16 23:02:27 +0200461 return new ValueTask<TStruct>(AnonymousStruct);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100462 }
463
Jens Geyerdce22992020-05-16 23:02:27 +0200464 public override Task ReadStructEndAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100465 {
Jens Geyerdce22992020-05-16 23:02:27 +0200466 cancellationToken.ThrowIfCancellationRequested();
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100467
468 /*
469 Doesn't actually consume any wire data, just removes the last field for
470 this struct from the field stack.
471 */
472
473 // consume the last field we Read off the wire.
474 _lastFieldId = _lastField.Pop();
Jens Geyerdce22992020-05-16 23:02:27 +0200475
476 return Task.CompletedTask;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100477 }
478
Jens Geyer5a17b132019-05-26 15:53:37 +0200479 public override async ValueTask<TField> ReadFieldBeginAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100480 {
481 // Read a field header off the wire.
482 var type = (byte) await ReadByteAsync(cancellationToken);
Jens Geyer5a17b132019-05-26 15:53:37 +0200483
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100484 // if it's a stop, then we can return immediately, as the struct is over.
485 if (type == Types.Stop)
486 {
Jens Geyer5a17b132019-05-26 15:53:37 +0200487 return StopField;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100488 }
489
Jens Geyer5a17b132019-05-26 15:53:37 +0200490
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100491 // mask off the 4 MSB of the exType header. it could contain a field id delta.
492 var modifier = (short) ((type & 0xf0) >> 4);
Jens Geyer5a17b132019-05-26 15:53:37 +0200493 var compactType = (byte)(type & 0x0f);
494
495 short fieldId;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100496 if (modifier == 0)
497 {
498 fieldId = await ReadI16Async(cancellationToken);
499 }
500 else
501 {
502 fieldId = (short) (_lastFieldId + modifier);
503 }
504
Jens Geyer5a17b132019-05-26 15:53:37 +0200505 var ttype = GetTType(compactType);
506 var field = new TField(string.Empty, ttype, fieldId);
507
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100508 // if this happens to be a boolean field, the value is encoded in the exType
Jens Geyer5a17b132019-05-26 15:53:37 +0200509 if( ttype == TType.Bool)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100510 {
Jens Geyer5a17b132019-05-26 15:53:37 +0200511 _boolValue = (compactType == Types.BooleanTrue);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100512 }
513
514 // push the new field onto the field stack so we can keep the deltas going.
515 _lastFieldId = field.ID;
516 return field;
517 }
518
Jens Geyerdce22992020-05-16 23:02:27 +0200519 public override Task ReadFieldEndAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100520 {
Jens Geyerdce22992020-05-16 23:02:27 +0200521 cancellationToken.ThrowIfCancellationRequested();
522 return Task.CompletedTask;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100523 }
524
Jens Geyer5a17b132019-05-26 15:53:37 +0200525 public override async ValueTask<TMap> ReadMapBeginAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100526 {
Jens Geyerdce22992020-05-16 23:02:27 +0200527 cancellationToken.ThrowIfCancellationRequested();
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100528
529 /*
530 Read a map header off the wire. If the size is zero, skip Reading the key
531 and value exType. This means that 0-length maps will yield TMaps without the
532 "correct" types.
533 */
534
535 var size = (int) await ReadVarInt32Async(cancellationToken);
536 var keyAndValueType = size == 0 ? (byte) 0 : (byte) await ReadByteAsync(cancellationToken);
Jens Geyer50806452019-11-23 01:55:58 +0100537 var map = new TMap(GetTType((byte) (keyAndValueType >> 4)), GetTType((byte) (keyAndValueType & 0xf)), size);
538 CheckReadBytesAvailable(map);
539 return map;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100540 }
541
Jens Geyerdce22992020-05-16 23:02:27 +0200542 public override Task ReadMapEndAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100543 {
Jens Geyerdce22992020-05-16 23:02:27 +0200544 cancellationToken.ThrowIfCancellationRequested();
545 return Task.CompletedTask;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100546 }
547
Jens Geyer5a17b132019-05-26 15:53:37 +0200548 public override async ValueTask<TSet> ReadSetBeginAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100549 {
550 /*
551 Read a set header off the wire. If the set size is 0-14, the size will
552 be packed into the element exType header. If it's a longer set, the 4 MSB
553 of the element exType header will be 0xF, and a varint will follow with the
554 true size.
555 */
556
557 return new TSet(await ReadListBeginAsync(cancellationToken));
558 }
559
Jens Geyer5a17b132019-05-26 15:53:37 +0200560 public override ValueTask<bool> ReadBoolAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100561 {
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100562 /*
563 Read a boolean off the wire. If this is a boolean field, the value should
564 already have been Read during ReadFieldBegin, so we'll just consume the
565 pre-stored value. Otherwise, Read a byte.
566 */
567
568 if (_boolValue != null)
569 {
570 var result = _boolValue.Value;
571 _boolValue = null;
Jens Geyer5a17b132019-05-26 15:53:37 +0200572 return new ValueTask<bool>(result);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100573 }
574
Jens Geyer5a17b132019-05-26 15:53:37 +0200575 return InternalCall();
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100576
Jens Geyer5a17b132019-05-26 15:53:37 +0200577 async ValueTask<bool> InternalCall()
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100578 {
Jens Geyer5a17b132019-05-26 15:53:37 +0200579 var data = await ReadByteAsync(cancellationToken);
580 return (data == Types.BooleanTrue);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100581 }
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100582 }
583
Jens Geyer5a17b132019-05-26 15:53:37 +0200584
585 public override async ValueTask<sbyte> ReadByteAsync(CancellationToken cancellationToken)
586 {
587 // Read a single byte off the wire. Nothing interesting here.
588 await Trans.ReadAllAsync(PreAllocatedBuffer, 0, 1, cancellationToken);
589 return (sbyte)PreAllocatedBuffer[0];
590 }
591
592 public override async ValueTask<short> ReadI16Async(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100593 {
Jens Geyerdce22992020-05-16 23:02:27 +0200594 cancellationToken.ThrowIfCancellationRequested();
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100595
596 return (short) ZigzagToInt(await ReadVarInt32Async(cancellationToken));
597 }
598
Jens Geyer5a17b132019-05-26 15:53:37 +0200599 public override async ValueTask<int> ReadI32Async(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100600 {
Jens Geyerdce22992020-05-16 23:02:27 +0200601 cancellationToken.ThrowIfCancellationRequested();
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100602
603 return ZigzagToInt(await ReadVarInt32Async(cancellationToken));
604 }
605
Jens Geyer5a17b132019-05-26 15:53:37 +0200606 public override async ValueTask<long> ReadI64Async(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100607 {
Jens Geyerdce22992020-05-16 23:02:27 +0200608 cancellationToken.ThrowIfCancellationRequested();
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100609
610 return ZigzagToLong(await ReadVarInt64Async(cancellationToken));
611 }
612
Jens Geyer5a17b132019-05-26 15:53:37 +0200613 public override async ValueTask<double> ReadDoubleAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100614 {
Jens Geyerdce22992020-05-16 23:02:27 +0200615 cancellationToken.ThrowIfCancellationRequested();
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100616
Jens Geyer5a17b132019-05-26 15:53:37 +0200617 await Trans.ReadAllAsync(PreAllocatedBuffer, 0, 8, cancellationToken);
zembord9d958a32019-11-21 13:11:44 +0300618
619 return BitConverter.Int64BitsToDouble(BinaryPrimitives.ReadInt64LittleEndian(PreAllocatedBuffer));
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100620 }
621
Jens Geyer5a17b132019-05-26 15:53:37 +0200622 public override async ValueTask<string> ReadStringAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100623 {
Jens Geyer5a17b132019-05-26 15:53:37 +0200624 // read length
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100625 var length = (int) await ReadVarInt32Async(cancellationToken);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100626 if (length == 0)
627 {
628 return string.Empty;
629 }
630
Jens Geyer5a17b132019-05-26 15:53:37 +0200631 // read and decode data
632 if (length < PreAllocatedBuffer.Length)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100633 {
Jens Geyer5a17b132019-05-26 15:53:37 +0200634 await Trans.ReadAllAsync(PreAllocatedBuffer, 0, length, cancellationToken);
635 return Encoding.UTF8.GetString(PreAllocatedBuffer, 0, length);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100636 }
637
Jens Geyer50806452019-11-23 01:55:58 +0100638 Transport.CheckReadBytesAvailable(length);
Mikel Blanchard4b66a9d2020-03-05 00:46:21 +0100639
640 var buf = ArrayPool<byte>.Shared.Rent(length);
641 try
642 {
643 await Trans.ReadAllAsync(buf, 0, length, cancellationToken);
644 return Encoding.UTF8.GetString(buf, 0, length);
645 }
646 finally
647 {
648 ArrayPool<byte>.Shared.Return(buf);
649 }
Jens Geyer5a17b132019-05-26 15:53:37 +0200650 }
651
652 public override async ValueTask<byte[]> ReadBinaryAsync(CancellationToken cancellationToken)
653 {
654 // read length
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100655 var length = (int) await ReadVarInt32Async(cancellationToken);
656 if (length == 0)
657 {
Mikel Blanchard4b66a9d2020-03-05 00:46:21 +0100658 return Array.Empty<byte>();
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100659 }
660
Jens Geyer5a17b132019-05-26 15:53:37 +0200661 // read data
Jens Geyer50806452019-11-23 01:55:58 +0100662 Transport.CheckReadBytesAvailable(length);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100663 var buf = new byte[length];
664 await Trans.ReadAllAsync(buf, 0, length, cancellationToken);
665 return buf;
666 }
667
Jens Geyer5a17b132019-05-26 15:53:37 +0200668 public override async ValueTask<TList> ReadListBeginAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100669 {
Jens Geyerdce22992020-05-16 23:02:27 +0200670 cancellationToken.ThrowIfCancellationRequested();
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100671
672 /*
673 Read a list header off the wire. If the list size is 0-14, the size will
674 be packed into the element exType header. If it's a longer list, the 4 MSB
675 of the element exType header will be 0xF, and a varint will follow with the
676 true size.
677 */
678
679 var sizeAndType = (byte) await ReadByteAsync(cancellationToken);
680 var size = (sizeAndType >> 4) & 0x0f;
681 if (size == 15)
682 {
683 size = (int) await ReadVarInt32Async(cancellationToken);
684 }
685
686 var type = GetTType(sizeAndType);
Jens Geyer50806452019-11-23 01:55:58 +0100687 var list = new TList(type, size);
688 CheckReadBytesAvailable(list);
689 return list;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100690 }
691
Jens Geyerdce22992020-05-16 23:02:27 +0200692 public override Task ReadListEndAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100693 {
Jens Geyerdce22992020-05-16 23:02:27 +0200694 cancellationToken.ThrowIfCancellationRequested();
695 return Task.CompletedTask;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100696 }
697
Jens Geyerdce22992020-05-16 23:02:27 +0200698 public override Task ReadSetEndAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100699 {
Jens Geyerdce22992020-05-16 23:02:27 +0200700 cancellationToken.ThrowIfCancellationRequested();
701 return Task.CompletedTask;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100702 }
703
704 private static byte GetCompactType(TType ttype)
705 {
706 // Given a TType value, find the appropriate TCompactProtocol.Types constant.
707 return TTypeToCompactType[(int) ttype];
708 }
709
710
Jens Geyer5a17b132019-05-26 15:53:37 +0200711 private async ValueTask<uint> ReadVarInt32Async(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100712 {
Jens Geyerdce22992020-05-16 23:02:27 +0200713 cancellationToken.ThrowIfCancellationRequested();
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100714
715 /*
716 Read an i32 from the wire as a varint. The MSB of each byte is set
717 if there is another byte to follow. This can Read up to 5 bytes.
718 */
719
720 uint result = 0;
721 var shift = 0;
722
723 while (true)
724 {
725 var b = (byte) await ReadByteAsync(cancellationToken);
726 result |= (uint) (b & 0x7f) << shift;
727 if ((b & 0x80) != 0x80)
728 {
729 break;
730 }
731 shift += 7;
732 }
733
734 return result;
735 }
736
Jens Geyer5a17b132019-05-26 15:53:37 +0200737 private async ValueTask<ulong> ReadVarInt64Async(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100738 {
Jens Geyerdce22992020-05-16 23:02:27 +0200739 cancellationToken.ThrowIfCancellationRequested();
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100740
741 /*
742 Read an i64 from the wire as a proper varint. The MSB of each byte is set
743 if there is another byte to follow. This can Read up to 10 bytes.
744 */
745
746 var shift = 0;
747 ulong result = 0;
748 while (true)
749 {
750 var b = (byte) await ReadByteAsync(cancellationToken);
751 result |= (ulong) (b & 0x7f) << shift;
752 if ((b & 0x80) != 0x80)
753 {
754 break;
755 }
756 shift += 7;
757 }
758
759 return result;
760 }
761
762 private static int ZigzagToInt(uint n)
763 {
764 return (int) (n >> 1) ^ -(int) (n & 1);
765 }
766
767 private static long ZigzagToLong(ulong n)
768 {
769 return (long) (n >> 1) ^ -(long) (n & 1);
770 }
771
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100772 private static TType GetTType(byte type)
773 {
774 // Given a TCompactProtocol.Types constant, convert it to its corresponding TType value.
Jens Geyer5a17b132019-05-26 15:53:37 +0200775 return CompactTypeToTType[type & 0x0f];
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100776 }
777
778 private static ulong LongToZigzag(long n)
779 {
780 // Convert l into a zigzag long. This allows negative numbers to be represented compactly as a varint
781 return (ulong) (n << 1) ^ (ulong) (n >> 63);
782 }
783
784 private static uint IntToZigzag(int n)
785 {
786 // Convert n into a zigzag int. This allows negative numbers to be represented compactly as a varint
787 return (uint) (n << 1) ^ (uint) (n >> 31);
788 }
789
Jens Geyer50806452019-11-23 01:55:58 +0100790 // Return the minimum number of bytes a type will consume on the wire
791 public override int GetMinSerializedSize(TType type)
792 {
793 switch (type)
794 {
795 case TType.Stop: return 0;
796 case TType.Void: return 0;
797 case TType.Bool: return sizeof(byte);
798 case TType.Double: return 8; // uses fixedLongToBytes() which always writes 8 bytes
799 case TType.Byte: return sizeof(byte);
800 case TType.I16: return sizeof(byte); // zigzag
801 case TType.I32: return sizeof(byte); // zigzag
802 case TType.I64: return sizeof(byte); // zigzag
803 case TType.String: return sizeof(byte); // string length
804 case TType.Struct: return 0; // empty struct
805 case TType.Map: return sizeof(byte); // element count
806 case TType.Set: return sizeof(byte); // element count
807 case TType.List: return sizeof(byte); // element count
808 default: throw new TTransportException(TTransportException.ExceptionType.Unknown, "unrecognized type code");
809 }
810 }
811
Jens Geyer421444f2019-03-20 22:13:25 +0100812 public class Factory : TProtocolFactory
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100813 {
Jens Geyer421444f2019-03-20 22:13:25 +0100814 public override TProtocol GetProtocol(TTransport trans)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100815 {
816 return new TCompactProtocol(trans);
817 }
818 }
819
820 /// <summary>
821 /// All of the on-wire exType codes.
822 /// </summary>
823 private static class Types
824 {
825 public const byte Stop = 0x00;
826 public const byte BooleanTrue = 0x01;
827 public const byte BooleanFalse = 0x02;
828 public const byte Byte = 0x03;
829 public const byte I16 = 0x04;
830 public const byte I32 = 0x05;
831 public const byte I64 = 0x06;
832 public const byte Double = 0x07;
833 public const byte Binary = 0x08;
834 public const byte List = 0x09;
835 public const byte Set = 0x0A;
836 public const byte Map = 0x0B;
837 public const byte Struct = 0x0C;
838 }
839 }
Jens Geyer421444f2019-03-20 22:13:25 +0100840}