THRIFT-4879 general performance improvements for netstd library
Client: netstd
Patch: Jens Geyer

This closes #1808
diff --git a/lib/netstd/Thrift/Protocol/TCompactProtocol.cs b/lib/netstd/Thrift/Protocol/TCompactProtocol.cs
index e6c5dbd..c26633a 100644
--- a/lib/netstd/Thrift/Protocol/TCompactProtocol.cs
+++ b/lib/netstd/Thrift/Protocol/TCompactProtocol.cs
@@ -17,6 +17,7 @@
 
 using System;
 using System.Collections.Generic;
+using System.Diagnostics;
 using System.Text;
 using System.Threading;
 using System.Threading.Tasks;
@@ -37,10 +38,13 @@
         private const byte TypeBits = 0x07; // 0000 0111
         private const int TypeShiftAmount = 5;
         private static readonly TStruct AnonymousStruct = new TStruct(string.Empty);
-        private static readonly TField Tstop = new TField(string.Empty, TType.Stop, 0);
+        private static readonly TField StopField = new TField(string.Empty, TType.Stop, 0);
+
+        private const byte NoTypeOverride = 0xFF;
 
         // ReSharper disable once InconsistentNaming
         private static readonly byte[] TTypeToCompactType = new byte[16];
+        private static readonly TType[] CompactTypeToTType = new TType[13];
 
         /// <summary>
         ///     Used to keep track of the last field for the current and previous structs, so we can do the delta stuff.
@@ -59,6 +63,26 @@
 
         private short _lastFieldId;
 
+        // minimize memory allocations by means of an preallocated bytes buffer
+        // The value of 128 is arbitrarily chosen, the required minimum size must be sizeof(long)
+        private byte[] PreAllocatedBuffer = new byte[128]; 
+
+        private struct VarInt
+        {
+            public byte[] bytes;
+            public int count;
+        }
+
+        // minimize memory allocations by means of an preallocated VarInt buffer
+        private VarInt PreAllocatedVarInt = new VarInt()
+        {
+            bytes = new byte[10], // see Int64ToVarInt()
+            count = 0
+        };
+
+
+
+
         public TCompactProtocol(TTransport trans)
             : base(trans)
         {
@@ -74,6 +98,20 @@
             TTypeToCompactType[(int) TType.Set] = Types.Set;
             TTypeToCompactType[(int) TType.Map] = Types.Map;
             TTypeToCompactType[(int) TType.Struct] = Types.Struct;
+
+            CompactTypeToTType[Types.Stop] = TType.Stop;
+            CompactTypeToTType[Types.BooleanTrue] = TType.Bool;
+            CompactTypeToTType[Types.BooleanFalse] = TType.Bool;
+            CompactTypeToTType[Types.Byte] = TType.Byte;
+            CompactTypeToTType[Types.I16] = TType.I16;
+            CompactTypeToTType[Types.I32] = TType.I32;
+            CompactTypeToTType[Types.I64] = TType.I64;
+            CompactTypeToTType[Types.Double] = TType.Double;
+            CompactTypeToTType[Types.Binary] = TType.String;
+            CompactTypeToTType[Types.List] = TType.List;
+            CompactTypeToTType[Types.Set] = TType.Set;
+            CompactTypeToTType[Types.Map] = TType.Map;
+            CompactTypeToTType[Types.Struct] = TType.Struct;
         }
 
         public void Reset()
@@ -84,19 +122,12 @@
 
         public override async Task WriteMessageBeginAsync(TMessage message, CancellationToken cancellationToken)
         {
-            if (cancellationToken.IsCancellationRequested)
-            {
-                return;
-            }
+            PreAllocatedBuffer[0] = ProtocolId;
+            PreAllocatedBuffer[1] = (byte)((Version & VersionMask) | (((uint)message.Type << TypeShiftAmount) & TypeMask));
+            await Trans.WriteAsync(PreAllocatedBuffer, 0, 2, cancellationToken);
 
-            await Trans.WriteAsync(new[] {ProtocolId}, cancellationToken);
-            await
-                Trans.WriteAsync(
-                    new[] {(byte) ((Version & VersionMask) | (((uint) message.Type << TypeShiftAmount) & TypeMask))},
-                    cancellationToken);
-
-            var bufferTuple = CreateWriteVarInt32((uint) message.SeqID);
-            await Trans.WriteAsync(bufferTuple.Item1, 0, bufferTuple.Item2, cancellationToken);
+            Int32ToVarInt((uint) message.SeqID, ref PreAllocatedVarInt);
+            await Trans.WriteAsync(PreAllocatedVarInt.bytes, 0, PreAllocatedVarInt.count, cancellationToken);
 
             await WriteStringAsync(message.Name, cancellationToken);
         }
@@ -135,26 +166,31 @@
             _lastFieldId = _lastField.Pop();
         }
 
-        private async Task WriteFieldBeginInternalAsync(TField field, byte typeOverride,
-            CancellationToken cancellationToken)
+        private async Task WriteFieldBeginInternalAsync(TField field, byte fieldType, CancellationToken cancellationToken)
         {
-            // if there's a exType override, use that.
-            var typeToWrite = typeOverride == 0xFF ? GetCompactType(field.Type) : typeOverride;
+            // if there's a exType override passed in, use that. Otherwise ask GetCompactType().
+            if (fieldType == NoTypeOverride)
+                fieldType = GetCompactType(field.Type);
+
 
             // check if we can use delta encoding for the field id
-            if ((field.ID > _lastFieldId) && (field.ID - _lastFieldId <= 15))
+            if (field.ID > _lastFieldId)
             {
-                var b = (byte) (((field.ID - _lastFieldId) << 4) | typeToWrite);
-                // Write them together
-                await Trans.WriteAsync(new[] {b}, cancellationToken);
-            }
-            else
-            {
-                // Write them separate
-                await Trans.WriteAsync(new[] {typeToWrite}, cancellationToken);
-                await WriteI16Async(field.ID, cancellationToken);
+                var delta = field.ID - _lastFieldId;
+                if (delta <= 15)
+                {
+                    // Write them together
+                    PreAllocatedBuffer[0] = (byte)((delta << 4) | fieldType);
+                    await Trans.WriteAsync(PreAllocatedBuffer, 0, 1, cancellationToken);
+                    _lastFieldId = field.ID;
+                    return;
+                }
             }
 
+            // Write them separate
+            PreAllocatedBuffer[0] = fieldType;
+            await Trans.WriteAsync(PreAllocatedBuffer, 0, 1, cancellationToken);
+            await WriteI16Async(field.ID, cancellationToken);
             _lastFieldId = field.ID;
         }
 
@@ -166,7 +202,7 @@
             }
             else
             {
-                await WriteFieldBeginInternalAsync(field, 0xFF, cancellationToken);
+                await WriteFieldBeginInternalAsync(field, NoTypeOverride, cancellationToken);
             }
         }
 
@@ -185,7 +221,8 @@
                 return;
             }
 
-            await Trans.WriteAsync(new[] {Types.Stop}, cancellationToken);
+            PreAllocatedBuffer[0] = Types.Stop;
+            await Trans.WriteAsync(PreAllocatedBuffer, 0, 1, cancellationToken);
         }
 
         protected async Task WriteCollectionBeginAsync(TType elemType, int size, CancellationToken cancellationToken)
@@ -202,14 +239,16 @@
 
             if (size <= 14)
             {
-                await Trans.WriteAsync(new[] {(byte) ((size << 4) | GetCompactType(elemType))}, cancellationToken);
+                PreAllocatedBuffer[0] = (byte)((size << 4) | GetCompactType(elemType));
+                await Trans.WriteAsync(PreAllocatedBuffer, 0, 1, cancellationToken);
             }
             else
             {
-                await Trans.WriteAsync(new[] {(byte) (0xf0 | GetCompactType(elemType))}, cancellationToken);
+                PreAllocatedBuffer[0] = (byte)(0xf0 | GetCompactType(elemType));
+                await Trans.WriteAsync(PreAllocatedBuffer, 0, 1, cancellationToken);
 
-                var bufferTuple = CreateWriteVarInt32((uint) size);
-                await Trans.WriteAsync(bufferTuple.Item1, 0, bufferTuple.Item2, cancellationToken);
+                Int32ToVarInt((uint) size, ref PreAllocatedVarInt);
+                await Trans.WriteAsync(PreAllocatedVarInt.bytes, 0, PreAllocatedVarInt.count, cancellationToken);
             }
         }
 
@@ -261,15 +300,15 @@
             if (_booleanField != null)
             {
                 // we haven't written the field header yet
-                await
-                    WriteFieldBeginInternalAsync(_booleanField.Value, b ? Types.BooleanTrue : Types.BooleanFalse,
-                        cancellationToken);
+                var type = b ? Types.BooleanTrue : Types.BooleanFalse;
+                await WriteFieldBeginInternalAsync(_booleanField.Value, type, cancellationToken);
                 _booleanField = null;
             }
             else
             {
-                // we're not part of a field, so just Write the value.
-                await Trans.WriteAsync(new[] {b ? Types.BooleanTrue : Types.BooleanFalse}, cancellationToken);
+                // we're not part of a field, so just write the value.
+                PreAllocatedBuffer[0] = b ? Types.BooleanTrue : Types.BooleanFalse;
+                await Trans.WriteAsync(PreAllocatedBuffer, 0, 1, cancellationToken);
             }
         }
 
@@ -280,7 +319,8 @@
                 return;
             }
 
-            await Trans.WriteAsync(new[] {(byte) b}, cancellationToken);
+            PreAllocatedBuffer[0] = (byte)b;
+            await Trans.WriteAsync(PreAllocatedBuffer, 0, 1, cancellationToken);
         }
 
         public override async Task WriteI16Async(short i16, CancellationToken cancellationToken)
@@ -290,29 +330,27 @@
                 return;
             }
 
-            var bufferTuple = CreateWriteVarInt32(IntToZigzag(i16));
-            await Trans.WriteAsync(bufferTuple.Item1, 0, bufferTuple.Item2, cancellationToken);
+            Int32ToVarInt(IntToZigzag(i16), ref PreAllocatedVarInt);
+            await Trans.WriteAsync(PreAllocatedVarInt.bytes, 0, PreAllocatedVarInt.count, cancellationToken);
         }
 
-        protected internal Tuple<byte[], int> CreateWriteVarInt32(uint n)
+        private static void Int32ToVarInt(uint n, ref VarInt varint)
         {
-            // Write an i32 as a varint.Results in 1 - 5 bytes on the wire.
-            var i32Buf = new byte[5];
-            var idx = 0;
+            // Write an i32 as a varint. Results in 1 - 5 bytes on the wire.
+            varint.count = 0;
+            Debug.Assert(varint.bytes.Length >= 5);
 
             while (true)
             {
                 if ((n & ~0x7F) == 0)
                 {
-                    i32Buf[idx++] = (byte) n;
+                    varint.bytes[varint.count++] = (byte)n;
                     break;
                 }
 
-                i32Buf[idx++] = (byte) ((n & 0x7F) | 0x80);
+                varint.bytes[varint.count++] = (byte)((n & 0x7F) | 0x80);
                 n >>= 7;
             }
-
-            return new Tuple<byte[], int>(i32Buf, idx);
         }
 
         public override async Task WriteI32Async(int i32, CancellationToken cancellationToken)
@@ -322,28 +360,26 @@
                 return;
             }
 
-            var bufferTuple = CreateWriteVarInt32(IntToZigzag(i32));
-            await Trans.WriteAsync(bufferTuple.Item1, 0, bufferTuple.Item2, cancellationToken);
+            Int32ToVarInt(IntToZigzag(i32), ref PreAllocatedVarInt);
+            await Trans.WriteAsync(PreAllocatedVarInt.bytes, 0, PreAllocatedVarInt.count, cancellationToken);
         }
 
-        protected internal Tuple<byte[], int> CreateWriteVarInt64(ulong n)
+        static private void Int64ToVarInt(ulong n, ref VarInt varint)
         {
             // Write an i64 as a varint. Results in 1-10 bytes on the wire.
-            var buf = new byte[10];
-            var idx = 0;
+            varint.count = 0;
+            Debug.Assert(varint.bytes.Length >= 10);
 
             while (true)
             {
-                if ((n & ~(ulong) 0x7FL) == 0)
+                if ((n & ~(ulong)0x7FL) == 0)
                 {
-                    buf[idx++] = (byte) n;
+                    varint.bytes[varint.count++] = (byte)n;
                     break;
                 }
-                buf[idx++] = (byte) ((n & 0x7F) | 0x80);
+                varint.bytes[varint.count++] = (byte)((n & 0x7F) | 0x80);
                 n >>= 7;
             }
-
-            return new Tuple<byte[], int>(buf, idx);
         }
 
         public override async Task WriteI64Async(long i64, CancellationToken cancellationToken)
@@ -353,8 +389,8 @@
                 return;
             }
 
-            var bufferTuple = CreateWriteVarInt64(LongToZigzag(i64));
-            await Trans.WriteAsync(bufferTuple.Item1, 0, bufferTuple.Item2, cancellationToken);
+            Int64ToVarInt(LongToZigzag(i64), ref PreAllocatedVarInt);
+            await Trans.WriteAsync(PreAllocatedVarInt.bytes, 0, PreAllocatedVarInt.count, cancellationToken);
         }
 
         public override async Task WriteDoubleAsync(double d, CancellationToken cancellationToken)
@@ -364,9 +400,8 @@
                 return;
             }
 
-            var data = new byte[8];
-            FixedLongToBytes(BitConverter.DoubleToInt64Bits(d), data, 0);
-            await Trans.WriteAsync(data, cancellationToken);
+            FixedLongToBytes(BitConverter.DoubleToInt64Bits(d), PreAllocatedBuffer, 0);
+            await Trans.WriteAsync(PreAllocatedBuffer, 0, 8, cancellationToken);
         }
 
         public override async Task WriteStringAsync(string str, CancellationToken cancellationToken)
@@ -378,8 +413,8 @@
 
             var bytes = Encoding.UTF8.GetBytes(str);
 
-            var bufferTuple = CreateWriteVarInt32((uint) bytes.Length);
-            await Trans.WriteAsync(bufferTuple.Item1, 0, bufferTuple.Item2, cancellationToken);
+            Int32ToVarInt((uint) bytes.Length, ref PreAllocatedVarInt);
+            await Trans.WriteAsync(PreAllocatedVarInt.bytes, 0, PreAllocatedVarInt.count, cancellationToken);
             await Trans.WriteAsync(bytes, 0, bytes.Length, cancellationToken);
         }
 
@@ -390,8 +425,8 @@
                 return;
             }
 
-            var bufferTuple = CreateWriteVarInt32((uint) bytes.Length);
-            await Trans.WriteAsync(bufferTuple.Item1, 0, bufferTuple.Item2, cancellationToken);
+            Int32ToVarInt((uint) bytes.Length, ref PreAllocatedVarInt);
+            await Trans.WriteAsync(PreAllocatedVarInt.bytes, 0, PreAllocatedVarInt.count, cancellationToken);
             await Trans.WriteAsync(bytes, 0, bytes.Length, cancellationToken);
         }
 
@@ -401,19 +436,19 @@
             {
                 return;
             }
-
+            
             if (map.Count == 0)
             {
-                await Trans.WriteAsync(new[] {(byte) 0}, cancellationToken);
+                PreAllocatedBuffer[0] = 0;
+                await Trans.WriteAsync( PreAllocatedBuffer, 0, 1, cancellationToken);
             }
             else
             {
-                var bufferTuple = CreateWriteVarInt32((uint) map.Count);
-                await Trans.WriteAsync(bufferTuple.Item1, 0, bufferTuple.Item2, cancellationToken);
-                await
-                    Trans.WriteAsync(
-                        new[] {(byte) ((GetCompactType(map.KeyType) << 4) | GetCompactType(map.ValueType))},
-                        cancellationToken);
+                Int32ToVarInt((uint) map.Count, ref PreAllocatedVarInt);
+                await Trans.WriteAsync(PreAllocatedVarInt.bytes, 0, PreAllocatedVarInt.count, cancellationToken);
+
+                PreAllocatedBuffer[0] = (byte)((GetCompactType(map.KeyType) << 4) | GetCompactType(map.ValueType));
+                await Trans.WriteAsync(PreAllocatedBuffer, 0, 1, cancellationToken);
             }
         }
 
@@ -425,7 +460,7 @@
             }
         }
 
-        public override async Task<TMessage> ReadMessageBeginAsync(CancellationToken cancellationToken)
+        public override async ValueTask<TMessage> ReadMessageBeginAsync(CancellationToken cancellationToken)
         {
             if (cancellationToken.IsCancellationRequested)
             {
@@ -461,7 +496,7 @@
             }
         }
 
-        public override async Task<TStruct> ReadStructBeginAsync(CancellationToken cancellationToken)
+        public override async ValueTask<TStruct> ReadStructBeginAsync(CancellationToken cancellationToken)
         {
             if (cancellationToken.IsCancellationRequested)
             {
@@ -492,19 +527,23 @@
             _lastFieldId = _lastField.Pop();
         }
 
-        public override async Task<TField> ReadFieldBeginAsync(CancellationToken cancellationToken)
+        public override async ValueTask<TField> ReadFieldBeginAsync(CancellationToken cancellationToken)
         {
             // Read a field header off the wire.
             var type = (byte) await ReadByteAsync(cancellationToken);
+
             // if it's a stop, then we can return immediately, as the struct is over.
             if (type == Types.Stop)
             {
-                return Tstop;
+                return StopField;
             }
 
-            short fieldId;
+
             // mask off the 4 MSB of the exType header. it could contain a field id delta.
             var modifier = (short) ((type & 0xf0) >> 4);
+            var compactType = (byte)(type & 0x0f);
+
+            short fieldId;
             if (modifier == 0)
             {
                 fieldId = await ReadI16Async(cancellationToken);
@@ -514,11 +553,13 @@
                 fieldId = (short) (_lastFieldId + modifier);
             }
 
-            var field = new TField(string.Empty, GetTType((byte) (type & 0x0f)), fieldId);
+            var ttype = GetTType(compactType);
+            var field = new TField(string.Empty, ttype, fieldId);
+
             // if this happens to be a boolean field, the value is encoded in the exType
-            if (IsBoolType(type))
+            if( ttype == TType.Bool)
             {
-                _boolValue = (byte) (type & 0x0f) == Types.BooleanTrue;
+                _boolValue = (compactType == Types.BooleanTrue);
             }
 
             // push the new field onto the field stack so we can keep the deltas going.
@@ -534,7 +575,7 @@
             }
         }
 
-        public override async Task<TMap> ReadMapBeginAsync(CancellationToken cancellationToken)
+        public override async ValueTask<TMap> ReadMapBeginAsync(CancellationToken cancellationToken)
         {
             if (cancellationToken.IsCancellationRequested)
             {
@@ -560,7 +601,7 @@
             }
         }
 
-        public override async Task<TSet> ReadSetBeginAsync(CancellationToken cancellationToken)
+        public override async ValueTask<TSet> ReadSetBeginAsync(CancellationToken cancellationToken)
         {
             /*
             Read a set header off the wire. If the set size is 0-14, the size will
@@ -572,13 +613,8 @@
             return new TSet(await ReadListBeginAsync(cancellationToken));
         }
 
-        public override async Task<bool> ReadBoolAsync(CancellationToken cancellationToken)
+        public override ValueTask<bool> ReadBoolAsync(CancellationToken cancellationToken)
         {
-            if (cancellationToken.IsCancellationRequested)
-            {
-                return await Task.FromCanceled<bool>(cancellationToken);
-            }
-
             /*
             Read a boolean off the wire. If this is a boolean field, the value should
             already have been Read during ReadFieldBegin, so we'll just consume the
@@ -589,26 +625,27 @@
             {
                 var result = _boolValue.Value;
                 _boolValue = null;
-                return result;
+                return new ValueTask<bool>(result);
             }
 
-            return await ReadByteAsync(cancellationToken) == Types.BooleanTrue;
-        }
+            return InternalCall();
 
-        public override async Task<sbyte> ReadByteAsync(CancellationToken cancellationToken)
-        {
-            if (cancellationToken.IsCancellationRequested)
+            async ValueTask<bool> InternalCall()
             {
-                return await Task.FromCanceled<sbyte>(cancellationToken);
+                var data = await ReadByteAsync(cancellationToken);
+                return (data == Types.BooleanTrue);
             }
-
-            // Read a single byte off the wire. Nothing interesting here.
-            var buf = new byte[1];
-            await Trans.ReadAllAsync(buf, 0, 1, cancellationToken);
-            return (sbyte) buf[0];
         }
 
-        public override async Task<short> ReadI16Async(CancellationToken cancellationToken)
+
+        public override async ValueTask<sbyte> ReadByteAsync(CancellationToken cancellationToken)
+        {
+            // Read a single byte off the wire. Nothing interesting here.
+            await Trans.ReadAllAsync(PreAllocatedBuffer, 0, 1, cancellationToken);
+            return (sbyte)PreAllocatedBuffer[0];
+        }
+
+        public override async ValueTask<short> ReadI16Async(CancellationToken cancellationToken)
         {
             if (cancellationToken.IsCancellationRequested)
             {
@@ -618,7 +655,7 @@
             return (short) ZigzagToInt(await ReadVarInt32Async(cancellationToken));
         }
 
-        public override async Task<int> ReadI32Async(CancellationToken cancellationToken)
+        public override async ValueTask<int> ReadI32Async(CancellationToken cancellationToken)
         {
             if (cancellationToken.IsCancellationRequested)
             {
@@ -628,7 +665,7 @@
             return ZigzagToInt(await ReadVarInt32Async(cancellationToken));
         }
 
-        public override async Task<long> ReadI64Async(CancellationToken cancellationToken)
+        public override async ValueTask<long> ReadI64Async(CancellationToken cancellationToken)
         {
             if (cancellationToken.IsCancellationRequested)
             {
@@ -638,60 +675,55 @@
             return ZigzagToLong(await ReadVarInt64Async(cancellationToken));
         }
 
-        public override async Task<double> ReadDoubleAsync(CancellationToken cancellationToken)
+        public override async ValueTask<double> ReadDoubleAsync(CancellationToken cancellationToken)
         {
             if (cancellationToken.IsCancellationRequested)
             {
                 return await Task.FromCanceled<double>(cancellationToken);
             }
 
-            var longBits = new byte[8];
-            await Trans.ReadAllAsync(longBits, 0, 8, cancellationToken);
+            await Trans.ReadAllAsync(PreAllocatedBuffer, 0, 8, cancellationToken);
 
-            return BitConverter.Int64BitsToDouble(BytesToLong(longBits));
+            return BitConverter.Int64BitsToDouble(BytesToLong(PreAllocatedBuffer));
         }
 
-        public override async Task<string> ReadStringAsync(CancellationToken cancellationToken)
+        public override async ValueTask<string> ReadStringAsync(CancellationToken cancellationToken)
         {
-            if (cancellationToken.IsCancellationRequested)
-            {
-                await Task.FromCanceled<string>(cancellationToken);
-            }
-
-            // Reads a byte[] (via ReadBinary), and then UTF-8 decodes it.
+            // read length
             var length = (int) await ReadVarInt32Async(cancellationToken);
-
             if (length == 0)
             {
                 return string.Empty;
             }
 
-            var buf = new byte[length];
-            await Trans.ReadAllAsync(buf, 0, length, cancellationToken);
-
-            return Encoding.UTF8.GetString(buf);
-        }
-
-        public override async Task<byte[]> ReadBinaryAsync(CancellationToken cancellationToken)
-        {
-            if (cancellationToken.IsCancellationRequested)
+            // read and decode data
+            if (length < PreAllocatedBuffer.Length)
             {
-                return await Task.FromCanceled<byte[]>(cancellationToken);
+                await Trans.ReadAllAsync(PreAllocatedBuffer, 0, length, cancellationToken);
+                return Encoding.UTF8.GetString(PreAllocatedBuffer, 0, length);
             }
 
-            // Read a byte[] from the wire.
+            var buf = new byte[length];
+            await Trans.ReadAllAsync(buf, 0, length, cancellationToken);
+            return Encoding.UTF8.GetString(buf, 0, length);
+        }
+
+        public override async ValueTask<byte[]> ReadBinaryAsync(CancellationToken cancellationToken)
+        {
+            // read length
             var length = (int) await ReadVarInt32Async(cancellationToken);
             if (length == 0)
             {
                 return new byte[0];
             }
 
+            // read data
             var buf = new byte[length];
             await Trans.ReadAllAsync(buf, 0, length, cancellationToken);
             return buf;
         }
 
-        public override async Task<TList> ReadListBeginAsync(CancellationToken cancellationToken)
+        public override async ValueTask<TList> ReadListBeginAsync(CancellationToken cancellationToken)
         {
             if (cancellationToken.IsCancellationRequested)
             {
@@ -739,7 +771,7 @@
         }
 
 
-        private async Task<uint> ReadVarInt32Async(CancellationToken cancellationToken)
+        private async ValueTask<uint> ReadVarInt32Async(CancellationToken cancellationToken)
         {
             if (cancellationToken.IsCancellationRequested)
             {
@@ -768,7 +800,7 @@
             return result;
         }
 
-        private async Task<ulong> ReadVarInt64Async(CancellationToken cancellationToken)
+        private async ValueTask<ulong> ReadVarInt64Async(CancellationToken cancellationToken)
         {
             if (cancellationToken.IsCancellationRequested)
             {
@@ -825,45 +857,10 @@
                 (bytes[0] & 0xffL);
         }
 
-        private static bool IsBoolType(byte b)
-        {
-            var lowerNibble = b & 0x0f;
-            return (lowerNibble == Types.BooleanTrue) || (lowerNibble == Types.BooleanFalse);
-        }
-
         private static TType GetTType(byte type)
         {
             // Given a TCompactProtocol.Types constant, convert it to its corresponding TType value.
-            switch ((byte) (type & 0x0f))
-            {
-                case Types.Stop:
-                    return TType.Stop;
-                case Types.BooleanFalse:
-                case Types.BooleanTrue:
-                    return TType.Bool;
-                case Types.Byte:
-                    return TType.Byte;
-                case Types.I16:
-                    return TType.I16;
-                case Types.I32:
-                    return TType.I32;
-                case Types.I64:
-                    return TType.I64;
-                case Types.Double:
-                    return TType.Double;
-                case Types.Binary:
-                    return TType.String;
-                case Types.List:
-                    return TType.List;
-                case Types.Set:
-                    return TType.Set;
-                case Types.Map:
-                    return TType.Map;
-                case Types.Struct:
-                    return TType.Struct;
-                default:
-                    throw new TProtocolException($"Don't know what exType: {(byte) (type & 0x0f)}");
-            }
+            return CompactTypeToTType[type & 0x0f];
         }
 
         private static ulong LongToZigzag(long n)