Skip to content

Commit 5304636

Browse files
committed
improve basic properties read / write
1 parent 3b95fc3 commit 5304636

File tree

5 files changed

+175
-102
lines changed

5 files changed

+175
-102
lines changed

projects/Benchmarks/WireFormatting/PrimitivesSerialization.cs

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ namespace RabbitMQ.Benchmarks
1111
[BenchmarkCategory("Primitives")]
1212
public class PrimitivesBase
1313
{
14-
protected Memory<byte> _buffer = new byte[16];
14+
protected readonly Memory<byte> _buffer = new byte[16];
1515

1616
[GlobalSetup]
1717
public virtual void Setup() { }
@@ -22,10 +22,18 @@ public class PrimitivesBool : PrimitivesBase
2222
public override void Setup() => WireFormatting.WriteBits(ref _buffer.Span.GetStart(), true, false, true, false, true);
2323

2424
[Benchmark]
25-
public int BoolRead2() => WireFormatting.ReadBits(_buffer.Span, out bool _, out bool _);
25+
public (bool, bool) BoolRead2()
26+
{
27+
WireFormatting.ReadBits(_buffer.Span, out bool v1, out bool v2);
28+
return (v1, v2);
29+
}
2630

2731
[Benchmark]
28-
public int BoolRead5() => WireFormatting.ReadBits(_buffer.Span, out bool _, out bool _, out bool _, out bool _, out bool _);
32+
public (bool, bool, bool, bool, bool) BoolRead5()
33+
{
34+
WireFormatting.ReadBits(_buffer.Span, out bool v1, out bool v2, out bool v3, out bool v4, out bool v5);
35+
return (v1, v2, v3, v4, v5);
36+
}
2937

3038
[Benchmark]
3139
[Arguments(true, false)]
@@ -52,7 +60,11 @@ public class PrimitivesLong : PrimitivesBase
5260
public override void Setup() => WireFormatting.WriteLong(ref _buffer.Span.GetStart(), 12345u);
5361

5462
[Benchmark]
55-
public int LongRead() => WireFormatting.ReadLong(_buffer.Span, out _);
63+
public uint LongRead()
64+
{
65+
WireFormatting.ReadLong(_buffer.Span, out uint v1);
66+
return v1;
67+
}
5668

5769
[Benchmark]
5870
[Arguments(12345u)]
@@ -64,7 +76,11 @@ public class PrimitivesLonglong : PrimitivesBase
6476
public override void Setup() => WireFormatting.WriteLonglong(ref _buffer.Span.GetStart(), 12345ul);
6577

6678
[Benchmark]
67-
public int LonglongRead() => WireFormatting.ReadLonglong(_buffer.Span, out _);
79+
public ulong LonglongRead()
80+
{
81+
WireFormatting.ReadLonglong(_buffer.Span, out ulong v1);
82+
return v1;
83+
}
6884

6985
[Benchmark]
7086
[Arguments(12345ul)]
@@ -76,7 +92,11 @@ public class PrimitivesShort : PrimitivesBase
7692
public override void Setup() => WireFormatting.WriteShort(ref _buffer.Span.GetStart(), 12345);
7793

7894
[Benchmark]
79-
public int ShortRead() => WireFormatting.ReadShort(_buffer.Span, out _);
95+
public ushort ShortRead()
96+
{
97+
WireFormatting.ReadShort(_buffer.Span, out ushort v1);
98+
return v1;
99+
}
80100

81101
[Benchmark]
82102
[Arguments(12345)]
@@ -85,12 +105,16 @@ public class PrimitivesShort : PrimitivesBase
85105

86106
public class PrimitivesTimestamp : PrimitivesBase
87107
{
88-
AmqpTimestamp _timestamp = new AmqpTimestamp(DateTimeOffset.UtcNow.ToUnixTimeSeconds());
108+
private AmqpTimestamp _timestamp = new AmqpTimestamp(DateTimeOffset.UtcNow.ToUnixTimeSeconds());
89109

90110
public override void Setup() => WireFormatting.WriteTimestamp(ref _buffer.Span.GetStart(), _timestamp);
91111

92112
[Benchmark]
93-
public int TimestampRead() => WireFormatting.ReadTimestamp(_buffer.Span, out _);
113+
public AmqpTimestamp TimestampRead()
114+
{
115+
WireFormatting.ReadTimestamp(_buffer.Span, out AmqpTimestamp v1);
116+
return v1;
117+
}
94118

95119
[Benchmark]
96120
public int TimestampWrite() => WireFormatting.WriteTimestamp(ref _buffer.Span.GetStart(), _timestamp);

projects/RabbitMQ.Client/client/framing/BasicProperties.cs

Lines changed: 129 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -200,60 +200,142 @@ public BasicProperties()
200200

201201
public BasicProperties(ReadOnlySpan<byte> span)
202202
{
203-
int offset = WireFormatting.ReadBits(span,
204-
out bool contentType_present,
205-
out bool contentEncoding_present,
206-
out bool headers_present,
207-
out bool deliveryMode_present,
208-
out bool priority_present,
209-
out bool correlationId_present,
210-
out bool replyTo_present,
211-
out bool expiration_present,
212-
out bool messageId_present,
213-
out bool timestamp_present,
214-
out bool type_present,
215-
out bool userId_present,
216-
out bool appId_present,
217-
out bool clusterId_present);
218-
if (contentType_present) { offset += WireFormatting.ReadShortstr(span.Slice(offset), out _contentType); }
219-
if (contentEncoding_present) { offset += WireFormatting.ReadShortstr(span.Slice(offset), out _contentEncoding); }
220-
if (headers_present) { offset += WireFormatting.ReadDictionary(span.Slice(offset), out var tmpDirectory); _headers = tmpDirectory; }
221-
if (deliveryMode_present) { _deliveryMode = span[offset++]; }
222-
if (priority_present) { _priority = span[offset++]; }
223-
if (correlationId_present) { offset += WireFormatting.ReadShortstr(span.Slice(offset), out _correlationId); }
224-
if (replyTo_present) { offset += WireFormatting.ReadShortstr(span.Slice(offset), out _replyTo); }
225-
if (expiration_present) { offset += WireFormatting.ReadShortstr(span.Slice(offset), out _expiration); }
226-
if (messageId_present) { offset += WireFormatting.ReadShortstr(span.Slice(offset), out _messageId); }
227-
if (timestamp_present) { offset += WireFormatting.ReadTimestamp(span.Slice(offset), out _timestamp); }
228-
if (type_present) { offset += WireFormatting.ReadShortstr(span.Slice(offset), out _type); }
229-
if (userId_present) { offset += WireFormatting.ReadShortstr(span.Slice(offset), out _userId); }
230-
if (appId_present) { offset += WireFormatting.ReadShortstr(span.Slice(offset), out _appId); }
231-
if (clusterId_present) { WireFormatting.ReadShortstr(span.Slice(offset), out _clusterId); }
203+
int offset = 2;
204+
ref readonly byte bits = ref span[0];
205+
if (bits.IsBitSet(ContentTypeBit)) { offset += WireFormatting.ReadShortstr(span.Slice(offset), out _contentType); }
206+
if (bits.IsBitSet(ContentEncodingBit)) { offset += WireFormatting.ReadShortstr(span.Slice(offset), out _contentEncoding); }
207+
if (bits.IsBitSet(HeaderBit)) { offset += WireFormatting.ReadDictionary(span.Slice(offset), out var tmpDirectory); _headers = tmpDirectory; }
208+
if (bits.IsBitSet(DeliveryModeBit)) { _deliveryMode = span[offset++]; }
209+
if (bits.IsBitSet(PriorityBit)) { _priority = span[offset++]; }
210+
if (bits.IsBitSet(CorrelationIdBit)) { offset += WireFormatting.ReadShortstr(span.Slice(offset), out _correlationId); }
211+
if (bits.IsBitSet(ReplyToBit)) { offset += WireFormatting.ReadShortstr(span.Slice(offset), out _replyTo); }
212+
if (bits.IsBitSet(ExpirationBit)) { offset += WireFormatting.ReadShortstr(span.Slice(offset), out _expiration); }
213+
214+
bits = ref span[1];
215+
if (bits.IsBitSet(MessageIdBit)) { offset += WireFormatting.ReadShortstr(span.Slice(offset), out _messageId); }
216+
if (bits.IsBitSet(TimestampBit)) { offset += WireFormatting.ReadTimestamp(span.Slice(offset), out _timestamp); }
217+
if (bits.IsBitSet(TypeBit)) { offset += WireFormatting.ReadShortstr(span.Slice(offset), out _type); }
218+
if (bits.IsBitSet(UserIdBit)) { offset += WireFormatting.ReadShortstr(span.Slice(offset), out _userId); }
219+
if (bits.IsBitSet(AppIdBit)) { offset += WireFormatting.ReadShortstr(span.Slice(offset), out _appId); }
220+
if (bits.IsBitSet(ClusterIdBit)) { WireFormatting.ReadShortstr(span.Slice(offset), out _clusterId); }
232221
}
233222

234223
public override ushort ProtocolClassId => 60;
235224
public override string ProtocolClassName => "basic";
236225

226+
//----------------------------------
227+
// First byte
228+
//----------------------------------
229+
private const byte ContentTypeBit = 7;
230+
private const byte ContentEncodingBit = 6;
231+
private const byte HeaderBit = 5;
232+
private const byte DeliveryModeBit = 4;
233+
private const byte PriorityBit = 3;
234+
private const byte CorrelationIdBit = 2;
235+
private const byte ReplyToBit = 1;
236+
private const byte ExpirationBit = 0;
237+
238+
//----------------------------------
239+
// Second byte
240+
//----------------------------------
241+
private const byte MessageIdBit = 7;
242+
private const byte TimestampBit = 6;
243+
private const byte TypeBit = 5;
244+
private const byte UserIdBit = 4;
245+
private const byte AppIdBit = 3;
246+
private const byte ClusterIdBit = 2;
247+
237248
internal override int WritePropertiesTo(Span<byte> span)
238249
{
239-
int offset = WireFormatting.WriteBits(ref span.GetStart(),
240-
IsContentTypePresent(), IsContentEncodingPresent(), IsHeadersPresent(), IsDeliveryModePresent(), IsPriorityPresent(),
241-
IsCorrelationIdPresent(), IsReplyToPresent(), IsExpirationPresent(), IsMessageIdPresent(), IsTimestampPresent(),
242-
IsTypePresent(), IsUserIdPresent(), IsAppIdPresent(), IsClusterIdPresent());
243-
if (IsContentTypePresent()) { offset += WireFormatting.WriteShortstr(ref span.GetOffset(offset), _contentType); }
244-
if (IsContentEncodingPresent()) { offset += WireFormatting.WriteShortstr(ref span.GetOffset(offset), _contentEncoding); }
245-
if (IsHeadersPresent()) { offset += WireFormatting.WriteTable(ref span.GetOffset(offset), _headers); }
246-
if (IsDeliveryModePresent()) { span[offset++] = _deliveryMode; }
247-
if (IsPriorityPresent()) { span[offset++] = _priority; }
248-
if (IsCorrelationIdPresent()) { offset += WireFormatting.WriteShortstr(ref span.GetOffset(offset), _correlationId); }
249-
if (IsReplyToPresent()) { offset += WireFormatting.WriteShortstr(ref span.GetOffset(offset), _replyTo); }
250-
if (IsExpirationPresent()) { offset += WireFormatting.WriteShortstr(ref span.GetOffset(offset), _expiration); }
251-
if (IsMessageIdPresent()) { offset += WireFormatting.WriteShortstr(ref span.GetOffset(offset), _messageId); }
252-
if (IsTimestampPresent()) { offset += WireFormatting.WriteTimestamp(ref span.GetOffset(offset), _timestamp); }
253-
if (IsTypePresent()) { offset += WireFormatting.WriteShortstr(ref span.GetOffset(offset), _type); }
254-
if (IsUserIdPresent()) { offset += WireFormatting.WriteShortstr(ref span.GetOffset(offset), _userId); }
255-
if (IsAppIdPresent()) { offset += WireFormatting.WriteShortstr(ref span.GetOffset(offset), _appId); }
256-
if (IsClusterIdPresent()) { offset += WireFormatting.WriteShortstr(ref span.GetOffset(offset), _clusterId); }
250+
int offset = 2;
251+
ref byte bitValue = ref span.GetStart();
252+
bitValue = 0;
253+
if (IsContentTypePresent())
254+
{
255+
bitValue.SetBit(ContentTypeBit);
256+
offset += WireFormatting.WriteShortstr(ref span.GetOffset(offset), _contentType);
257+
}
258+
259+
if (IsContentEncodingPresent())
260+
{
261+
bitValue.SetBit(ContentEncodingBit);
262+
offset += WireFormatting.WriteShortstr(ref span.GetOffset(offset), _contentEncoding);
263+
}
264+
265+
if (IsHeadersPresent())
266+
{
267+
bitValue.SetBit(HeaderBit);
268+
offset += WireFormatting.WriteTable(ref span.GetOffset(offset), _headers);
269+
}
270+
271+
if (IsDeliveryModePresent())
272+
{
273+
bitValue.SetBit(DeliveryModeBit);
274+
span.GetOffset(offset++) = _deliveryMode;
275+
}
276+
277+
if (IsPriorityPresent())
278+
{
279+
bitValue.SetBit(PriorityBit);
280+
span.GetOffset(offset++) = _priority;
281+
}
282+
283+
if (IsCorrelationIdPresent())
284+
{
285+
bitValue.SetBit(CorrelationIdBit);
286+
offset += WireFormatting.WriteShortstr(ref span.GetOffset(offset), _correlationId);
287+
}
288+
289+
if (IsReplyToPresent())
290+
{
291+
bitValue.SetBit(ReplyToBit);
292+
offset += WireFormatting.WriteShortstr(ref span.GetOffset(offset), _replyTo);
293+
}
294+
295+
if (IsExpirationPresent())
296+
{
297+
bitValue.SetBit(ExpirationBit);
298+
offset += WireFormatting.WriteShortstr(ref span.GetOffset(offset), _expiration);
299+
}
300+
301+
bitValue = ref span.GetOffset(1);
302+
bitValue = 0;
303+
if (IsMessageIdPresent())
304+
{
305+
bitValue.SetBit(MessageIdBit);
306+
offset += WireFormatting.WriteShortstr(ref span.GetOffset(offset), _messageId);
307+
}
308+
309+
if (IsTimestampPresent())
310+
{
311+
bitValue.SetBit(TimestampBit);
312+
offset += WireFormatting.WriteTimestamp(ref span.GetOffset(offset), _timestamp);
313+
}
314+
315+
if (IsTypePresent())
316+
{
317+
bitValue.SetBit(TypeBit);
318+
offset += WireFormatting.WriteShortstr(ref span.GetOffset(offset), _type);
319+
}
320+
321+
if (IsUserIdPresent())
322+
{
323+
bitValue.SetBit(UserIdBit);
324+
offset += WireFormatting.WriteShortstr(ref span.GetOffset(offset), _userId);
325+
}
326+
327+
if (IsAppIdPresent())
328+
{
329+
bitValue.SetBit(AppIdBit);
330+
offset += WireFormatting.WriteShortstr(ref span.GetOffset(offset), _appId);
331+
}
332+
333+
if (IsClusterIdPresent())
334+
{
335+
bitValue.SetBit(ClusterIdBit);
336+
offset += WireFormatting.WriteShortstr(ref span.GetOffset(offset), _clusterId);
337+
}
338+
257339
return offset;
258340
}
259341

projects/RabbitMQ.Client/client/impl/WireFormatting.Read.cs

Lines changed: 2 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -238,31 +238,6 @@ public static int ReadBits(ReadOnlySpan<byte> span, out bool val1, out bool val2
238238
return 1;
239239
}
240240

241-
[MethodImpl(MethodImplOptions.AggressiveInlining)]
242-
public static int ReadBits(ReadOnlySpan<byte> span,
243-
out bool val1, out bool val2, out bool val3, out bool val4, out bool val5,
244-
out bool val6, out bool val7, out bool val8, out bool val9, out bool val10,
245-
out bool val11, out bool val12, out bool val13, out bool val14)
246-
{
247-
byte bits = span[0];
248-
val1 = (bits & 0b1000_0000) != 0;
249-
val2 = (bits & 0b0100_0000) != 0;
250-
val3 = (bits & 0b0010_0000) != 0;
251-
val4 = (bits & 0b0001_0000) != 0;
252-
val5 = (bits & 0b0000_1000) != 0;
253-
val6 = (bits & 0b0000_0100) != 0;
254-
val7 = (bits & 0b0000_0010) != 0;
255-
val8 = (bits & 0b0000_0001) != 0;
256-
bits = span[1];
257-
val9 = (bits & 0b1000_0000) != 0;
258-
val10 = (bits & 0b0100_0000) != 0;
259-
val11 = (bits & 0b0010_0000) != 0;
260-
val12 = (bits & 0b0001_0000) != 0;
261-
val13 = (bits & 0b0000_1000) != 0;
262-
val14 = (bits & 0b0000_0100) != 0;
263-
return 2;
264-
}
265-
266241
[MethodImpl(MethodImplOptions.AggressiveInlining)]
267242
public static int ReadShort(ReadOnlySpan<byte> span, out ushort value)
268243
{
@@ -328,7 +303,7 @@ public static int ThrowSyntaxErrorException(uint byteCount)
328303
private static int ThrowInvalidTableValue(char type)
329304
=> throw new SyntaxErrorException($"Unrecognised type in table: {type}");
330305

331-
private static decimal ThrowInvalidDecimalScale(int scale)
332-
=> throw new SyntaxErrorException($"Unrepresentable AMQP decimal table field: scale={scale}");
306+
private static void ThrowInvalidDecimalScale(int scale)
307+
=> throw new SyntaxErrorException($"Unrepresentable AMQP decimal table field: scale={scale}");
333308
}
334309
}

projects/RabbitMQ.Client/client/impl/WireFormatting.Write.cs

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -302,23 +302,6 @@ public static int WriteBits(ref byte destination, bool val1, bool val2, bool val
302302
return 1;
303303
}
304304

305-
[MethodImpl(MethodImplOptions.AggressiveInlining)]
306-
public static int WriteBits(ref byte destination,
307-
bool val1, bool val2, bool val3, bool val4, bool val5,
308-
bool val6, bool val7, bool val8, bool val9, bool val10,
309-
bool val11, bool val12, bool val13, bool val14)
310-
{
311-
int a = val8.ToByte() + val7.ToByte() * 2 + val6.ToByte() * 4 + val5.ToByte() * 8;
312-
int b = val4.ToByte() + val3.ToByte() * 2 + val2.ToByte() * 4 + val1.ToByte() * 8;
313-
destination = (byte)(a | (b << 4));
314-
315-
a = val14.ToByte() * 4 + val13.ToByte() * 8;
316-
b = val12.ToByte() + val11.ToByte() * 2 + val10.ToByte() * 4 + val9.ToByte() * 8;
317-
318-
destination.GetOffset(1) = (byte)(a | (b << 4));
319-
return 2;
320-
}
321-
322305
[MethodImpl(MethodImplOptions.AggressiveInlining)]
323306
public static int WriteLongstr(ref byte destination, ReadOnlySpan<byte> val)
324307
{
@@ -500,9 +483,6 @@ public static int ThrowArgumentTooLong(int length)
500483
public static int ThrowArgumentOutOfRangeException(int orig, int expected)
501484
=> throw new ArgumentOutOfRangeException("span", $"Span has not enough space ({orig} instead of {expected})");
502485

503-
public static int ThrowArgumentOutOfRangeException(string val, int maxLength)
504-
=> throw new ArgumentOutOfRangeException(nameof(val), val, $"Value exceeds the maximum allowed length of {maxLength} bytes.");
505-
506486
private static int ThrowWireFormattingException(decimal value)
507487
=> throw new WireFormattingException("Decimal overflow in AMQP encoding", value);
508488

projects/RabbitMQ.Client/util/TypeExtensions.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,18 @@ public static ref byte GetOffset(this ref byte source, int offset)
4242
return ref Unsafe.Add(ref source, offset);
4343
}
4444

45+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
46+
public static bool IsBitSet(this in byte value, byte bitPosition)
47+
{
48+
return (value & (1 << bitPosition)) != 0;
49+
}
50+
51+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
52+
public static void SetBit(this ref byte value, byte bitPosition)
53+
{
54+
value |= (byte)(1 << bitPosition);
55+
}
56+
4557
[MethodImpl(MethodImplOptions.AggressiveInlining)]
4658
public static byte ToByte(this ref bool source)
4759
{

0 commit comments

Comments
 (0)