From 0c111c5b3b6262928274643516f83e6eef111a56 Mon Sep 17 00:00:00 2001 From: bollhals Date: Sun, 20 Dec 2020 23:11:19 +0100 Subject: [PATCH] some wire formatting improvements --- .../WireFormatting/DataTypeSerialization.cs | 127 +++++- .../WireFormatting/MethodSerialization.cs | 6 + .../WireFormatting/PrimitivesSerialization.cs | 28 +- .../client/impl/WireFormatting.cs | 408 ++++++++++-------- 4 files changed, 366 insertions(+), 203 deletions(-) diff --git a/projects/Benchmarks/WireFormatting/DataTypeSerialization.cs b/projects/Benchmarks/WireFormatting/DataTypeSerialization.cs index b9b7ac982d..5755dcde1b 100644 --- a/projects/Benchmarks/WireFormatting/DataTypeSerialization.cs +++ b/projects/Benchmarks/WireFormatting/DataTypeSerialization.cs @@ -21,12 +21,83 @@ public class DataTypeSerialization public virtual void SetUp() { } } - public class DateTypeArraySerialization : DataTypeSerialization + public class DataTypeFieldSerialization : DataTypeSerialization { - readonly List _emptyArray = new List(); - Memory _emptyArrayBuffer; - Memory _populatedArrayBuffer; - List _array; + private readonly object _intObject = 5; + private readonly string _shortString = new string('x', 100); + private readonly byte[] _byteArray = new byte[0]; + private readonly Dictionary _emptyDictionary = new Dictionary(); + private readonly BinaryTableValue _binaryTableValue = new BinaryTableValue(new byte[0]); + + private Memory _fieldStringBuffer; + private Memory _fieldIntBuffer; + private Memory _fieldNullBuffer; + private Memory _fieldArrayBuffer; + private Memory _fieldDictBuffer; + private Memory _fieldBinaryTableValueBuffer; + + public override void SetUp() + { + _fieldNullBuffer = new byte[WireFormatting.GetFieldValueByteCount(null)]; + WireFormatting.WriteFieldValue(_fieldNullBuffer.Span, null); + _fieldIntBuffer = new byte[WireFormatting.GetFieldValueByteCount(_intObject)]; + WireFormatting.WriteFieldValue(_fieldIntBuffer.Span, _intObject); + _fieldStringBuffer = new byte[WireFormatting.GetFieldValueByteCount(_shortString)]; + WireFormatting.WriteFieldValue(_fieldStringBuffer.Span, _shortString); + _fieldArrayBuffer = new byte[WireFormatting.GetFieldValueByteCount(_byteArray)]; + WireFormatting.WriteFieldValue(_fieldArrayBuffer.Span, _byteArray); + _fieldDictBuffer = new byte[WireFormatting.GetFieldValueByteCount(_emptyDictionary)]; + WireFormatting.WriteFieldValue(_fieldDictBuffer.Span, _emptyDictionary); + _fieldBinaryTableValueBuffer = new byte[WireFormatting.GetFieldValueByteCount(_binaryTableValue)]; + WireFormatting.WriteFieldValue(_fieldBinaryTableValueBuffer.Span, _binaryTableValue); + } + + [Benchmark] + public object NullRead() => WireFormatting.ReadFieldValue(_fieldNullBuffer.Span, out int _); + [Benchmark] + public object IntRead() => WireFormatting.ReadFieldValue(_fieldIntBuffer.Span, out int _); + [Benchmark] + public object StringRead() => WireFormatting.ReadFieldValue(_fieldStringBuffer.Span, out int _); + [Benchmark] + public object ArrayRead() => WireFormatting.ReadFieldValue(_fieldArrayBuffer.Span, out int _); + [Benchmark] + public object DictRead() => WireFormatting.ReadFieldValue(_fieldDictBuffer.Span, out int _); + [Benchmark] + public object BinaryTableValueRead() => WireFormatting.ReadFieldValue(_fieldBinaryTableValueBuffer.Span, out int _); + + [Benchmark] + public int NullWrite() => WireFormatting.WriteFieldValue(_buffer.Span,null); + [Benchmark] + public int IntWrite() => WireFormatting.WriteFieldValue(_buffer.Span, _intObject); + [Benchmark] + public int StringWrite() => WireFormatting.WriteFieldValue(_buffer.Span, _shortString); + [Benchmark] + public int ArrayWrite() => WireFormatting.WriteFieldValue(_buffer.Span, _byteArray); + [Benchmark] + public int DictWrite() => WireFormatting.WriteFieldValue(_buffer.Span, _emptyDictionary); + [Benchmark] + public int BinaryTableValueWrite() => WireFormatting.WriteFieldValue(_buffer.Span, _binaryTableValue); + + [Benchmark] + public int NullGetSize() => WireFormatting.GetFieldValueByteCount(null); + [Benchmark] + public int IntGetSize() => WireFormatting.GetFieldValueByteCount(_intObject); + [Benchmark] + public int StringGetSize() => WireFormatting.GetFieldValueByteCount(_shortString); + [Benchmark] + public int ArrayGetSize() => WireFormatting.GetFieldValueByteCount(_byteArray); + [Benchmark] + public int DictGetSize() => WireFormatting.GetFieldValueByteCount(_emptyDictionary); + [Benchmark] + public int BinaryTableValueGetSize() => WireFormatting.GetFieldValueByteCount(_binaryTableValue); + } + + public class DataTypeArraySerialization : DataTypeSerialization + { + private readonly List _emptyArray = new List(); + private Memory _emptyArrayBuffer; + private Memory _populatedArrayBuffer; + private List _array; public override void SetUp() { @@ -49,14 +120,20 @@ public override void SetUp() [Benchmark] public int ArrayWritePopulated() => WireFormatting.WriteArray(_buffer.Span, _array); + + [Benchmark] + public int ArrayGetSizeEmpty() => WireFormatting.GetArrayByteCount(_emptyArray); + + [Benchmark] + public int ArrayGetSizePopulated() => WireFormatting.GetArrayByteCount(_array); } public class DataTypeTableSerialization : DataTypeSerialization { - IDictionary _emptyDict = new Dictionary(); - IDictionary _populatedDict; - Memory _emptyDictionaryBuffer; - Memory _populatedDictionaryBuffer; + private IDictionary _emptyDict = new Dictionary(); + private IDictionary _populatedDict; + private Memory _emptyDictionaryBuffer; + private Memory _populatedDictionaryBuffer; public override void SetUp() { @@ -67,7 +144,7 @@ public override void SetUp() { "uint", 1234u }, { "decimal", 12.34m }, { "timestamp", _timestamp }, - { "fieldtable", new Dictionary(){ { "test", "test" } } }, + { "fieldtable", new Dictionary{ { "test", "test" } } }, { "fieldarray", new List { "longstring", 1234, 12.34m, _timestamp } } }; @@ -89,13 +166,19 @@ public override void SetUp() [Benchmark] public int TableWritePopulated() => WireFormatting.WriteTable(_buffer.Span, _populatedDict); + + [Benchmark] + public int TableGetSizeEmpty() => WireFormatting.GetTableByteCount(_emptyDict); + + [Benchmark] + public int TableGetSizePopulated() => WireFormatting.GetTableByteCount(_populatedDict); } public class DataTypeLongStringSerialization : DataTypeSerialization { - readonly string _longString = new string('X', 4096); - readonly Memory _emptyLongStringBuffer = GenerateLongStringBuffer(string.Empty); - readonly Memory _populatedLongStringBuffer = GenerateLongStringBuffer(new string('X', 4096)); + private readonly string _longString = new string('X', 4096); + private readonly Memory _emptyLongStringBuffer = GenerateLongStringBuffer(string.Empty); + private readonly Memory _populatedLongStringBuffer = GenerateLongStringBuffer(new string('X', 4096)); [Benchmark] public int LongstrReadEmpty() => WireFormatting.ReadLongstr(_emptyLongStringBuffer.Span, out _); @@ -109,6 +192,12 @@ public class DataTypeLongStringSerialization : DataTypeSerialization [Benchmark] public int LongstrWritePopulated() => WireFormatting.WriteLongstr(_buffer.Span, _longString); + [Benchmark] + public int LongstrGetSizeEmpty() => WireFormatting.GetFieldValueByteCount(string.Empty); + + [Benchmark] + public int LongstrGetSizePopulated() => WireFormatting.GetFieldValueByteCount(_longString); + private static byte[] GenerateLongStringBuffer(string val) { byte[] _buffer = new byte[5 + Encoding.UTF8.GetByteCount(val)]; @@ -119,9 +208,9 @@ private static byte[] GenerateLongStringBuffer(string val) public class DataTypeShortStringSerialization : DataTypeSerialization { - readonly string _shortString = new string('X', 255); - readonly Memory _emptyShortStringBuffer = GenerateStringBuffer(string.Empty); - readonly Memory _populatedShortStringBuffer = GenerateStringBuffer(new string('X', 255)); + private readonly string _shortString = new string('X', 255); + private readonly Memory _emptyShortStringBuffer = GenerateStringBuffer(string.Empty); + private readonly Memory _populatedShortStringBuffer = GenerateStringBuffer(new string('X', 255)); [Benchmark] public int ShortstrReadEmpty() => WireFormatting.ReadShortstr(_emptyShortStringBuffer.Span, out _); @@ -135,6 +224,12 @@ public class DataTypeShortStringSerialization : DataTypeSerialization [Benchmark] public int ShortstrWritePopulated() => WireFormatting.WriteShortstr(_buffer.Span, _shortString); + [Benchmark] + public int ShortstrGetSizeEmpty() => WireFormatting.GetByteCount(string.Empty); + + [Benchmark] + public int ShortstrGetSizePopulated() => WireFormatting.GetByteCount(_shortString); + private static byte[] GenerateStringBuffer(string val) { byte[] _buffer = new byte[2 + Encoding.UTF8.GetByteCount(val)]; diff --git a/projects/Benchmarks/WireFormatting/MethodSerialization.cs b/projects/Benchmarks/WireFormatting/MethodSerialization.cs index 5354e8ca5e..dda1042f9b 100644 --- a/projects/Benchmarks/WireFormatting/MethodSerialization.cs +++ b/projects/Benchmarks/WireFormatting/MethodSerialization.cs @@ -39,6 +39,9 @@ public class MethodBasicDeliver : MethodSerializationBase [Benchmark] public int BasicDeliverWrite() => _basicDeliver.WriteArgumentsTo(_buffer.Span); + + [Benchmark] + public int BasicDeliverSize() => _basicDeliver.GetRequiredBufferSize(); } public class MethodChannelClose : MethodSerializationBase @@ -64,5 +67,8 @@ public class MethodBasicProperties : MethodSerializationBase [Benchmark] public int BasicPropertiesWrite() => _basicProperties.WritePropertiesTo(_buffer.Span); + + [Benchmark] + public int BasicDeliverSize() => _basicProperties.GetRequiredPayloadBufferSize(); } } diff --git a/projects/Benchmarks/WireFormatting/PrimitivesSerialization.cs b/projects/Benchmarks/WireFormatting/PrimitivesSerialization.cs index e13489aa84..00334e5350 100644 --- a/projects/Benchmarks/WireFormatting/PrimitivesSerialization.cs +++ b/projects/Benchmarks/WireFormatting/PrimitivesSerialization.cs @@ -17,6 +17,25 @@ public class PrimitivesBase public virtual void Setup() { } } + public class PrimitivesBool : PrimitivesBase + { + public override void Setup() => WireFormatting.WriteBits(_buffer.Span, true, false, true, false, true); + + [Benchmark] + public int BoolRead2() => WireFormatting.ReadBits(_buffer.Span, out bool _, out bool _); + + [Benchmark] + public int BoolRead5() => WireFormatting.ReadBits(_buffer.Span, out bool _, out bool _, out bool _, out bool _, out bool _); + + [Benchmark] + [Arguments(true, false)] + public int BoolWrite2(bool param1, bool param2) => WireFormatting.WriteBits(_buffer.Span, param1, param2); + + [Benchmark] + [Arguments(true, false)] + public int BoolWrite5(bool param1, bool param2) => WireFormatting.WriteBits(_buffer.Span, param1, param2, param1, param2, param1); + } + public class PrimitivesDecimal : PrimitivesBase { public override void Setup() => WireFormatting.WriteDecimal(_buffer.Span, 123.45m); @@ -36,7 +55,8 @@ public class PrimitivesLong : PrimitivesBase public int LongRead() => WireFormatting.ReadLong(_buffer.Span, out _); [Benchmark] - public int LongWrite() => WireFormatting.WriteLong(_buffer.Span, 12345u); + [Arguments(12345u)] + public int LongWrite(uint value) => WireFormatting.WriteLong(_buffer.Span, value); } public class PrimitivesLonglong : PrimitivesBase @@ -47,7 +67,8 @@ public class PrimitivesLonglong : PrimitivesBase public int LonglongRead() => WireFormatting.ReadLonglong(_buffer.Span, out _); [Benchmark] - public int LonglongWrite() => WireFormatting.WriteLonglong(_buffer.Span, 12345ul); + [Arguments(12345ul)] + public int LonglongWrite(ulong value) => WireFormatting.WriteLonglong(_buffer.Span, value); } public class PrimitivesShort : PrimitivesBase @@ -58,7 +79,8 @@ public class PrimitivesShort : PrimitivesBase public int ShortRead() => WireFormatting.ReadShort(_buffer.Span, out _); [Benchmark] - public int ShortWrite() => WireFormatting.WriteShort(_buffer.Span, 12345); + [Arguments(12345)] + public int ShortWrite(ushort value) => WireFormatting.WriteShort(_buffer.Span, value); } public class PrimitivesTimestamp : PrimitivesBase diff --git a/projects/RabbitMQ.Client/client/impl/WireFormatting.cs b/projects/RabbitMQ.Client/client/impl/WireFormatting.cs index b1bc77ef3d..412436c0e7 100644 --- a/projects/RabbitMQ.Client/client/impl/WireFormatting.cs +++ b/projects/RabbitMQ.Client/client/impl/WireFormatting.cs @@ -68,7 +68,7 @@ internal DecimalData(uint flags, uint hi, uint lo, uint mid) } } - private static UTF8Encoding UTF8 = new UTF8Encoding(); + private static readonly UTF8Encoding UTF8 = new UTF8Encoding(); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static decimal ReadDecimal(ReadOnlySpan span) @@ -86,14 +86,17 @@ public static decimal ReadDecimal(ReadOnlySpan span) public static IList ReadArray(ReadOnlySpan span, out int bytesRead) { - List array = new List(); - long arrayLength = NetworkOrderDeserializer.ReadUInt32(span); bytesRead = 4; + long arrayLength = NetworkOrderDeserializer.ReadUInt32(span); + if (arrayLength == 0) + { + return null; + } + List array = new List(); while (bytesRead - 4 < arrayLength) { - object value = ReadFieldValue(span.Slice(bytesRead), out int fieldValueBytesRead); + array.Add(ReadFieldValue(span.Slice(bytesRead), out int fieldValueBytesRead)); bytesRead += fieldValueBytesRead; - array.Add(value); } return array; @@ -101,59 +104,71 @@ public static IList ReadArray(ReadOnlySpan span, out int bytesRead) public static object ReadFieldValue(ReadOnlySpan span, out int bytesRead) { - bytesRead = 1; switch ((char)span[0]) { case 'S': - bytesRead += ReadLongstr(span.Slice(1), out var bytes); + bytesRead = 1 + ReadLongstr(span.Slice(1), out var bytes); return bytes; - case 'I': - bytesRead += 4; - return NetworkOrderDeserializer.ReadInt32(span.Slice(1)); - case 'i': - bytesRead += 4; - return NetworkOrderDeserializer.ReadUInt32(span.Slice(1)); - case 'D': - bytesRead += 5; - return ReadDecimal(span.Slice(1)); - case 'T': - bytesRead += ReadTimestamp(span.Slice(1), out var timestamp); - return timestamp; - case 'F': - bytesRead += ReadDictionary(span.Slice(1), out var dictionary); - return dictionary; - case 'A': - IList arrayResult = ReadArray(span.Slice(1), out int arrayBytesRead); - bytesRead += arrayBytesRead; - return arrayResult; - case 'B': - bytesRead += 1; - return span[1]; - case 'b': - bytesRead += 1; - return (sbyte)span[1]; - case 'd': - bytesRead += 8; - return NetworkOrderDeserializer.ReadDouble(span.Slice(1)); - case 'f': - bytesRead += 4; - return NetworkOrderDeserializer.ReadSingle(span.Slice(1)); - case 'l': - bytesRead += 8; - return NetworkOrderDeserializer.ReadInt64(span.Slice(1)); - case 's': - bytesRead += 2; - return NetworkOrderDeserializer.ReadInt16(span.Slice(1)); case 't': - bytesRead += 1; + bytesRead = 2; return span[1] != 0; - case 'x': - bytesRead += ReadLongstr(span.Slice(1), out var binaryTableResult); - return new BinaryTableValue(binaryTableResult); + case 'I': + bytesRead = 5; + return NetworkOrderDeserializer.ReadInt32(span.Slice(1)); case 'V': + bytesRead = 1; return null; default: - throw new SyntaxErrorException($"Unrecognised type in table: {(char)span[0]}"); + return ReadFieldValueSlow(span, out bytesRead); + } + + // Moved out of outer switch to have a shorter main method (improves performance) + static object ReadFieldValueSlow(ReadOnlySpan span, out int bytesRead) + { + var slice = span.Slice(1); + switch ((char)span[0]) + { + case 'F': + bytesRead = 1 + ReadDictionary(slice, out var dictionary); + return dictionary; + case 'A': + IList arrayResult = ReadArray(slice, out int arrayBytesRead); + bytesRead = 1 + arrayBytesRead; + return arrayResult; + case 'l': + bytesRead = 9; + return NetworkOrderDeserializer.ReadInt64(slice); + case 'i': + bytesRead = 5; + return NetworkOrderDeserializer.ReadUInt32(slice); + case 'D': + bytesRead = 6; + return ReadDecimal(slice); + case 'B': + bytesRead = 2; + return span[1]; + case 'b': + bytesRead = 2; + return (sbyte)span[1]; + case 'd': + bytesRead = 9; + return NetworkOrderDeserializer.ReadDouble(slice); + case 'f': + bytesRead = 5; + return NetworkOrderDeserializer.ReadSingle(slice); + case 's': + bytesRead = 3; + return NetworkOrderDeserializer.ReadInt16(slice); + case 'T': + bytesRead = 1 + ReadTimestamp(slice, out var timestamp); + return timestamp; + case 'x': + bytesRead = 1 + ReadLongstr(slice, out var binaryTableResult); + return new BinaryTableValue(binaryTableResult); + default: + bytesRead = 0; + return ThrowInvalidTableValue((char)span[0]); + } } } @@ -320,10 +335,8 @@ public static int ReadDictionary(ReadOnlySpan span, out Dictionary span, out AmqpTimestamp value public static int WriteArray(Span span, IList val) { - if (val is null) + if (val is null || val.Count == 0) { NetworkOrderSerializer.WriteUInt32(span, 0); return 4; @@ -358,15 +371,25 @@ public static int WriteArray(Span span, IList val) public static int GetArrayByteCount(IList val) { - int byteCount = 4; - if (val is null) + if (val is null || val.Count == 0) { - return byteCount; + return 4; } - for (int index = 0; index < val.Count; index++) + int byteCount = 4; + if (val is List valList) + { + for (int index = 0; index < valList.Count; index++) + { + byteCount += GetFieldValueByteCount(valList[index]); + } + } + else { - byteCount += GetFieldValueByteCount(val[index]); + for (int index = 0; index < val.Count; index++) + { + byteCount += GetFieldValueByteCount(val[index]); + } } return byteCount; @@ -407,111 +430,131 @@ public static int WriteDecimal(Span span, decimal value) public static int WriteFieldValue(Span span, object value) { - if (value is null) - { - span[0] = (byte)'V'; - return 1; - } - + // Order by likelihood of occurrence Span slice = span.Slice(1); + ref var type = ref span[0]; switch (value) { + case null: + type = (byte)'V'; + return 1; case string val: - span[0] = (byte)'S'; - return 1 + WriteLongstr(slice, val); - case byte[] val: - span[0] = (byte)'S'; + type = (byte)'S'; return 1 + WriteLongstr(slice, val); - case int val: - span[0] = (byte)'I'; - NetworkOrderSerializer.WriteInt32(slice, val); - return 5; - case uint val: - span[0] = (byte)'i'; - NetworkOrderSerializer.WriteUInt32(slice, val); - return 5; - case decimal val: - span[0] = (byte)'D'; - return 1 + WriteDecimal(slice, val); - case AmqpTimestamp val: - span[0] = (byte)'T'; - return 1 + WriteTimestamp(slice, val); - case IDictionary val: - span[0] = (byte)'F'; - return 1 + WriteTable(slice, val); - case IList val: - span[0] = (byte)'A'; - return 1 + WriteArray(slice, val); - case byte val: - span[0] = (byte)'B'; - span[1] = val; - return 2; - case sbyte val: - span[0] = (byte)'b'; - span[1] = (byte)val; - return 2; - case double val: - span[0] = (byte)'d'; - NetworkOrderSerializer.WriteDouble(slice, val); - return 9; - case float val: - span[0] = (byte)'f'; - NetworkOrderSerializer.WriteSingle(slice, val); - return 5; - case long val: - span[0] = (byte)'l'; - NetworkOrderSerializer.WriteInt64(slice, val); - return 9; - case short val: - span[0] = (byte)'s'; - NetworkOrderSerializer.WriteInt16(slice, val); - return 3; case bool val: - span[0] = (byte)'t'; + type = (byte)'t'; span[1] = (byte)(val ? 1 : 0); return 2; - case BinaryTableValue val: - span[0] = (byte)'x'; - return 1 + WriteLongstr(slice, val.Bytes); + case int val: + type = (byte)'I'; + NetworkOrderSerializer.WriteInt32(slice, val); + return 5; + case byte[] val: + type = (byte)'S'; + return 1 + WriteLongstr(slice, val); default: - throw new WireFormattingException($"Value of type '{value.GetType().Name}' cannot appear as table value", value); + return WriteFieldValueSlow(span, value); + } + + // Moved out of outer switch to have a shorter main method (improves performance) + static int WriteFieldValueSlow(Span span, object value) + { + // Order by likelihood of occurrence + ref var type = ref span[0]; + Span slice = span.Slice(1); + switch (value) + { + case float val: + type = (byte)'f'; + NetworkOrderSerializer.WriteSingle(slice, val); + return 5; + case IDictionary val: + type = (byte)'F'; + return 1 + WriteTable(slice, val); + case IList val: + type = (byte)'A'; + return 1 + WriteArray(slice, val); + case AmqpTimestamp val: + type = (byte)'T'; + return 1 + WriteTimestamp(slice, val); + case double val: + type = (byte)'d'; + NetworkOrderSerializer.WriteDouble(slice, val); + return 9; + case long val: + type = (byte)'l'; + NetworkOrderSerializer.WriteInt64(slice, val); + return 9; + case byte val: + type = (byte)'B'; + span[1] = val; + return 2; + case sbyte val: + type = (byte)'b'; + span[1] = (byte)val; + return 2; + case short val: + type = (byte)'s'; + NetworkOrderSerializer.WriteInt16(slice, val); + return 3; + case uint val: + type = (byte)'i'; + NetworkOrderSerializer.WriteUInt32(slice, val); + return 5; + case decimal val: + type = (byte)'D'; + return 1 + WriteDecimal(slice, val); + case IDictionary val: + type = (byte)'F'; + return 1 + WriteTable(slice, val); + case BinaryTableValue val: + type = (byte)'x'; + return 1 + WriteLongstr(slice, val.Bytes); + default: + return ThrowInvalidTableValue(value); + } } } public static int GetFieldValueByteCount(object value) { + // Order by likelihood of occurrence switch (value) { case null: return 1; - case byte _: - case sbyte _: + case string val: + return 5 + GetByteCount(val); case bool _: return 2; - case short _: - return 3; case int _: - case uint _: case float _: return 5; - case decimal _: - return 6; + case byte[] val: + return 5 + val.Length; + case IDictionary val: + return 1 + GetTableByteCount(val); + case IList val: + return 1 + GetArrayByteCount(val); case AmqpTimestamp _: case double _: case long _: return 9; - case string val: - return 5 + GetByteCount(val); - case byte[] val: - return 5 + val.Length; + case byte _: + case sbyte _: + return 2; + case short _: + return 3; + case uint _: + return 5; + case decimal _: + return 6; case IDictionary val: return 1 + GetTableByteCount(val); - case IList val: - return 1 + GetArrayByteCount(val); case BinaryTableValue val: return 5 + val.Bytes.Length; default: - throw new WireFormattingException($"Value of type '{value.GetType().Name}' cannot appear as table value", value); + return ThrowInvalidTableValue(value); } } @@ -539,7 +582,7 @@ public static int WriteBits(Span span, bool val) [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int WriteBits(Span span, bool val1, bool val2) { - byte bits = 0; + int bits = 0; if (val1) { bits |= 1 << 0; @@ -549,14 +592,14 @@ public static int WriteBits(Span span, bool val1, bool val2) { bits |= 1 << 1; } - span[0] = bits; + span[0] = (byte)bits; return 1; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int WriteBits(Span span, bool val1, bool val2, bool val3) { - byte bits = 0; + int bits = 0; if (val1) { bits |= 1 << 0; @@ -572,14 +615,14 @@ public static int WriteBits(Span span, bool val1, bool val2, bool val3) bits |= 1 << 2; } - span[0] = bits; + span[0] = (byte)bits; return 1; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int WriteBits(Span span, bool val1, bool val2, bool val3, bool val4) { - byte bits = 0; + int bits = 0; if (val1) { bits |= 1 << 0; @@ -600,14 +643,14 @@ public static int WriteBits(Span span, bool val1, bool val2, bool val3, bo bits |= 1 << 3; } - span[0] = bits; + span[0] = (byte)bits; return 1; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int WriteBits(Span span, bool val1, bool val2, bool val3, bool val4, bool val5) { - byte bits = 0; + int bits = 0; if (val1) { bits |= 1 << 0; @@ -632,7 +675,7 @@ public static int WriteBits(Span span, bool val1, bool val2, bool val3, bo { bits |= 1 << 4; } - span[0] = bits; + span[0] = (byte)bits; return 1; } @@ -642,78 +685,79 @@ public static int WriteBits(Span span, bool val6, bool val7, bool val8, bool val9, bool val10, bool val11, bool val12, bool val13, bool val14) { - byte bits = 0; - if (val1) + int bits = 0; + if (val9) { bits |= 1 << 7; } - if (val2) + if (val10) { bits |= 1 << 6; } - if (val3) + if (val11) { bits |= 1 << 5; } - if (val4) + if (val12) { bits |= 1 << 4; } - if (val5) + if (val13) { bits |= 1 << 3; } - if (val6) + if (val14) { bits |= 1 << 2; } + span[1] = (byte)bits; - if (val7) - { - bits |= 1 << 1; - } - - if (val8) - { - bits |= 1 << 0; - } - span[0] = bits; bits = 0; - if (val9) + if (val1) { bits |= 1 << 7; } - if (val10) + if (val2) { bits |= 1 << 6; } - if (val11) + if (val3) { bits |= 1 << 5; } - if (val12) + if (val4) { bits |= 1 << 4; } - if (val13) + if (val5) { bits |= 1 << 3; } - if (val14) + if (val6) { bits |= 1 << 2; } - span[1] = bits; + + if (val7) + { + bits |= 1 << 1; + } + + if (val8) + { + bits |= 1 << 0; + } + span[0] = (byte)bits; return 2; } @@ -858,12 +902,12 @@ public static int WriteTable(Span span, IDictionary val) public static int GetTableByteCount(IDictionary val) { - int byteCount = 4; - if (val is null) + if (val is null || val.Count == 0) { - return byteCount; + return 4; } + int byteCount = 4; foreach (DictionaryEntry entry in val) { byteCount += GetByteCount(entry.Key.ToString()) + 1; @@ -875,12 +919,12 @@ public static int GetTableByteCount(IDictionary val) public static int GetTableByteCount(IDictionary val) { - int byteCount = 4; - if (val is null) + if (val is null || val.Count == 0) { - return byteCount; + return 4; } + int byteCount = 4; if (val is Dictionary dict) { foreach (KeyValuePair entry in dict) @@ -910,28 +954,24 @@ public static int WriteTimestamp(Span span, AmqpTimestamp val) } public static int ThrowArgumentOutOfRangeException(int orig, int expected) - { - throw new ArgumentOutOfRangeException("span", $"Span has not enough space ({orig} instead of {expected})"); - } + => throw new ArgumentOutOfRangeException("span", $"Span has not enough space ({orig} instead of {expected})"); public static int ThrowArgumentOutOfRangeException(string val, int maxLength) - { - throw new ArgumentOutOfRangeException(nameof(val), val, $"Value exceeds the maximum allowed length of {maxLength} bytes."); - } + => throw new ArgumentOutOfRangeException(nameof(val), val, $"Value exceeds the maximum allowed length of {maxLength} bytes."); public static int ThrowSyntaxErrorException(uint byteCount) - { - throw new SyntaxErrorException($"Long string too long; byte length={byteCount}, max={int.MaxValue}"); - } + => throw new SyntaxErrorException($"Long string too long; byte length={byteCount}, max={int.MaxValue}"); private static int ThrowWireFormattingException(decimal value) - { - throw new WireFormattingException("Decimal overflow in AMQP encoding", value); - } + => throw new WireFormattingException("Decimal overflow in AMQP encoding", value); + + private static int ThrowInvalidTableValue(char type) + => throw new SyntaxErrorException($"Unrecognised type in table: {type}"); + + private static int ThrowInvalidTableValue(object value) + => throw new WireFormattingException($"Value of type '{value.GetType().Name}' cannot appear as table value", value); private static decimal ThrowInvalidDecimalScale(int scale) - { - throw new SyntaxErrorException($"Unrepresentable AMQP decimal table field: scale={scale}"); - } + => throw new SyntaxErrorException($"Unrepresentable AMQP decimal table field: scale={scale}"); } }